views:

502

answers:

4

In my Java SWT application I'm hosting an 3rd party ActiveX control. I'm using OleClientSite to do this.

// Ah, this works. :-)
OleAutomation comObject = new OleAutomation(...);

There are 2 easy little functions I want to call from Java. Here are the COM function definitions:

[id(5)]
void easyFoo([in] int blah);

[id(20)]
void problemFoo([in] VARIANT floatArray);

Easy, right? Here's my pretend code:

// Ah, this works. :-)
OleAutomation comObject = new OleAutomation("Some3rdPartyControlHere");

// Call easyFoo(42). This works. :-)
int easyFooId = 5;
comObject.invoke(easyFooId, new Variant[] { new Variant(42) });

// Call problemFoo(new float[] { 4.2, 7.0 }). This doesn't work. :-(
int problemFooId = 20;
comObject.invoke(problemFooId, [ACK! What goes here?]);

The problem is on the last line: how do I pass a float array to the 3rd party COM object? HELP!

A: 

I suspect there is no constructor that takes a float[] because VARIANTs don't have a float array member.

I think what you need to do to make this work is pack up your floats into a SAFEARRAY (ick; and I have no idea how to create one in Java).

Alternatively, you may try serializing your array to raw bits and use the BYTE* member of the VARIANT struct, and pass an int that has the count of bytes so you can accurately de-serialize on the other side (and I assume this is all in the same process and thread, otherwise it gets harder).

[id(20)]
void problemFoo([in] VARIANT bytes /* VT_BYREF|VT_UI1 */, [in] VARIANT byteCount /* VT_UI4 */);
jeffamaphone
Same process, same thread, but there is no problemFoo(bytes, byteCount). The COM object is a 3rd party control, one that only takes VARIANT, and the documentation states you should pass in a float array.
Judah Himango
Okay. Well, hopefully this or the other answer points you in the right direction. Maybe contact the vendor and ask if they have some Java sample code.
jeffamaphone
Vendor no longer supports this COM component, so I can't get support from them. (We're trying to move off this 3rd party control, but these things take time.) I will investigate the byte* member. If that works, I'll mark yours as the correct answer.
Judah Himango
Well, the control on the other side is going to be expecting some specific type. It should reject anything that doesn't specify that type in the vt field of the VARIANT. The easiest thing to do would be attach a debugger (VS or NTSD) and step through the disassembly of the function call. It should be pretty obvious where the control does the comparison and you can know instantly whether it wants a SAFEARRAY or what.
jeffamaphone
Jeff, any resources you can point me to that explain exactly how to do this? I'm pretty proficient with a debugger for Java and .NET, but rather dumbfounded when debugging native code.
Judah Himango
I don't know of any online tutorials. In VS you just have to attach to the process and set a breakpoint on your function that makes the call into the component. Then right before you make the call your right-click and select "Go to Disassembly", then there should be a bunch of interop goo, but eventually you'll end up in the Active X dll. I've never done it from Java, so I'm not sure I can help there. If you have public symbols for the Active X control, you can set a breakpoint on the method directly...
jeffamaphone
Jeff, since I can't change the 3rd party control, switching to the bytes answer doesn't work. I used the OleView tool to see the proper type it's expecting: it just says "VARIANT". I would imagine it would say "SAFEARRAY" if it was expecting a Safe Array, right?
Judah Himango
A: 

What's wrong with creating an array of Variant and filling it with your float array values?

Variant[] problemFooArgs = new Variant[myFloats.length]; 
for( int i=0; i<myFloats.length; i++)
{
        problemFooArgs[i] = new Variant(myFloats[i]);
}

If it really needs only one argument (an array of float), you could try one level of indirection:

Variant[] problemFooArgs = new Variant[1]; 
Variant[] myFooArgs = new Variant[1]; 
for( int i=0; i<myFloats.length; i++)
{
        myFooArgs [i] = new Variant(myFloats[i]);
}
problemFooArgs[0] = myFooArgs;


If the simple approach does not work and you do need a SAFEARRAY, you could try and create one after the example "Reading and writing to a SAFEARRAY", using the constants of org.eclipse.swt.internal.win32.OS. But it seems for char[] only.

Other source of inspiration for creating the relevant SAFEARRAY:

class SafeArray of project com4j (and its associated class, like Variant)

VonC
Thanks, it didn't occur to me that I could send an array of variants. I'll try that and get back to you.
Judah Himango
Regarding SAFEARRAY, I don't think I need a SAFEARRAY, as the COM IDL will specify when it needs one, right? I've seen IDLs of functions that require SAFEARRAY; this particular IDL states I need a Variant. Anyways, I'll give these a try and get back to you.
Judah Himango
Actually, now I'm not so sure; it actually may be a SAFEARRAY. Hrmmm.
Judah Himango
Passing in an array of variants doesn't seem to work. In the actual code, problemFoo takes 2 args, both float arrays.
Judah Himango
I'll try doing a SAFEARRAY and get back to you.
Judah Himango
@Judah: `SAFEARRAY` is usually the structure needed to pass an array, and it itself referenced by a `Variant`. What I did not find is how to copy an array floats (4 bytes per float) within the `struct` that is a `SAFEARRAY`, as opposed to an array of char (2 bytes per char)
VonC
Got it, thanks. I'm trying to get this to work for float[] (or even an int[] would work). If you can figure out how to make it work for float[] or int[], I'll mark yours as the answer.
Judah Himango
Yeah, I saw the SafeArray from the Koders search engine as well. Unfortunately, most of the calls are native calls, so I don't really know what's going on.
Judah Himango
VonC, I tried the following code to create a SafeArray that holds floats: http://judahhimango.com/javavariantfloatarray.txt It didn't work. Any idea why?
Judah Himango
@Judah: nope, sorry. I did some tests without much success...
VonC
Any other ideas? I'm really stuck here, and there's next to zero documentation on the web for this.
Judah Himango
VonC, I'm thinking SafeArray is the wrong route. I'm using Ole/COM object viewer tool in the Windows SDK, and when I point it to this 3rd party COM object, it says the function takes a VARIANT. Wouldn't it say SAFEARRAY if it was expecting a safe array?
Judah Himango
A: 

Hi Judah,

I’m encountering exactly the same problem as you were. I’ve spent few weeks searching for the solution, but couldn’t fine one. Please let me know if you were able to solve this problem. Thanks!

Jay Kim
Sheng Jiang's answer might help you. I've got some code that *appears* to work. I will post it shortly.
Judah Himango
+1  A: 

You need to pass a float array. In COM terms, that mean s a VARIANT with vt set to VT_R4|VT_ARRAY. An array of variants may not work as the document does not say it can accept an array of variants (VT_VARIANT |VT_ARRAY). In java you should be able to use float[] as the parameter type. If not you can always call the Windows API to construct a safe array of desired type.

Sheng Jiang 蒋晟
Thanks for the answer. I've seen the "reading and writing to safe array" in Java article that you link to. I've adapted that code to work for floats, and it appears to work. I'll post the results shortly.
Judah Himango

related questions