Need help: 32 Bit COM Server called by 64 Bit Java

33 views
Skip to first unread message

Markus Karg

unread,
Jan 24, 2024, 9:45:12 AMJan 24
to Java Native Access
From a 64 Bit Java VM I need to invoke a 32 Bit COM Server.

Within short time I had it run using JACOB, but certainly I want to use JNA instead!

With JNA I am stuck at the point where it does call the needed function correctly (I notice the application doing the right thing in the background), but it fails to return the result in a ByReference (i. e. out) param!

NB: In JACOB I had to use pass a reference to "Variant(0)" (hence: vt_int) wherever the IDL spec said "long", due to the 32/64 Bit mismatch, which made it work: I do see the returned value (numbers larger than zero)!

In JNA it fails always:
* When using LongByReference (as per IDL spec) the result is always 0 (which definitively is wrong, as the invoked 32 Bit application guarantees values larger than zero).
* When using IntByReference, there is an exception: Type Conflict.(HRESULT: 80020005) (puArgErr=4)

I assume that there is a bug in JNA that makes it think "long" in IDL is "long" in Java, or I have to somehow tell JNA to use a reference to vt_int -- but how?!

ANY HELP HIGHLY APPRECIATED! :-)

This is my current mapping:
```java
 @ComObject(progId = "Qs_stat.TQsstatRemoteControl")
 public static interface C {
     @ComMethod(name = "ClientConnect")
     void clientConnect(IntByReference handle, int moduleID, int languageID, String userID, String userPwd);
 ```

Tres Finocchiaro

unread,
Jan 24, 2024, 10:34:44 AMJan 24
to jna-...@googlegroups.com
I don't have a solution to your problem (years ago I used JACOB and it was very good for COM) but I did stumble upon a few articles, I'm sorry if these are redundant to your efforts:



This all suggests that there is a COM surrogate.  How this is (or can be?) mapped (either via JACOB or JNA) is unbeknownst to me, quoting mattmags:

> 2. Data types
> In most instances, 64-bit Windows uses the same data types as the 32-bit
> version. The differences are mainly in pointers which are 32 bits long
> in 32-bit Windows and 64 bits long in 64-bit Windows. The pointer-derived
> data types such as HANDLE and HWND are also different between 32-bit and
> 64-bit versions. Windows helps you to keep a single code base for both
> 32-bit and 64-bit software versions by offering polymorphic data types
> that have a different length depending on the target platform, for example
> INT_PTR declares an integer with the size of a pointer’. Any variable of
> this type is an integer which is 32 bits long on a 32-bit platform and 64
> bits long on a 64-bit platform.

Furthemore, here's an old mirror of a SAP how-to which may also have pointers to your question:

https://scripting1208.rssing.com/chan-56607393/article15.html


Finally, and a bit off topic, I'm curious why you would choose to use JNA for this purpose (dependency simplicity perhaps or support for ARM64?).  In my experience, JACOB is very good for mapping COM calls, it would still be my preferred approach.

Markus Karg

unread,
Jan 24, 2024, 10:47:22 AMJan 24
to Java Native Access
Thank you so much for your kind reply. Unfortunately, the articles are not really of much help here: They talk about loading 32 Bit DLLs in 64 Bit apps in-process, which I do not perform -- I let COM handle that under the hood, i. e. the COM subsystem provides that surrogate automatically. As the solution already works in JACOB, this proofs that the problem is neither in understanding the cause nor in modifying something in Windows, but solely in making it work in JNA solely. Or possibly I missed something in the articles. Anyways, thank you so much for chiming in !

Regarding your final question: I cannot use JACOB due to two reasons. (A) It is not supported anymore since four years. (B) One of the COM objects I am accessing has an interface not inherited from IDispatch, so I need to call that single interface using vtable invocation, which JNA can do, but JACOB (IMHO) cannot.

Matthias Bläsing

unread,
Jan 24, 2024, 1:56:34 PMJan 24
to jna-...@googlegroups.com
Am Mittwoch, dem 24.01.2024 um 06:45 -0800 schrieb Markus Karg:
NB: In JACOB I had to use pass a reference to "Variant(0)" (hence: vt_int) wherever the IDL spec said "long", due to the 32/64 Bit mismatch, which made it work: I do see the returned value (numbers larger than zero)!

In JNA it fails always:
* When using LongByReference (as per IDL spec) the result is always 0 (which definitively is wrong, as the invoked 32 Bit application guarantees values larger than zero).
* When using IntByReference, there is an exception: Type Conflict.(HRESULT: 80020005) (puArgErr=4)

I assume that there is a bug in JNA that makes it think "long" in IDL is "long" in Java, or I have to somehow tell JNA to use a reference to vt_int -- but how?!

ANY HELP HIGHLY APPRECIATED! :-)

This is my current mapping:
```java
 @ComObject(progId = "Qs_stat.TQsstatRemoteControl")
 public static interface C {
     @ComMethod(name = "ClientConnect")
     void clientConnect(IntByReference handle, int moduleID, int languageID, String userID, String userPwd);
 ```

For a dispatch based invocations special cases can be handled by manually providing the Variant for the parameter.

I would try this approach:

  1. Change signature to:  void clientConnect(VARIANT handle, int moduleID, int languageID, String userID, String userPwd);
  2. Initialize the variant before the call:
    VARIANT handle = new VARIANT();
    OleAuto.INSTANCE.VariantInit(handle);
    // call into clientCollect
    // extract handle using getValue

JNA will not touch the VARIANT and pass it to native as is.

You can also query the VARTYPE of the handle after the call and see what was placed in it.

Maybe that helps

Matthias

Markus Karg

unread,
Jan 25, 2024, 4:02:00 AMJan 25
to Java Native Access
Thank you, Matthias, for chiming in. Your input is highly appreciated!

Unfortunately your code did not work: After the call the variant still has the type vt_empty! So I tried to resemble what JACOB actually does internally, and that successfully worked:

var ref = new WinDef.LONGByReference();
var handleRef = new VARIANT();
handleRef.setValue(VT_I4 | VT_BYREF, ref);
c.clientConnect(handleRef, moduleID, languageID, userID, userPwd);
var handle = ref.getValue().intValue();

NB: TLB / TLD files literally say "long", but both were authored on a 32 Bit machine, and the COM server actually is a 32 Bit EXE, so according to https://learn.microsoft.com/en-us/windows/win32/midl/long this means: (quote) "On 32-bit platforms, long is synonymous with int." (looking at the date when this document was originally authored, what they actually meant to say is: "on all non 16-bit platforms"), hence in Java it must be "int"! I assume that this fact was not known or ignored by the original author of the COM-Support in JNA, as JNA internally seems to think that an IDL "long" should be a Java "long", while it must be a Java "int" always. Could somebody please confirm this?

NB: Surprisingly, JNA does not like to accept IntByReference or LongByReference here, but it really must be WinDef.LONGByReference! Apparently no support for IntByReference or LongByReference exists in JNA's VARIANT class, which one simply has to know (I couldn't find any documentation about that. If it exists I would be happy for a pointer!).

From here, it should not be too far to a production-grade solution:

Question 1: Is there a simpler / more convenient / otherwise "better" way in JNA to achieve that?

Question 2: Do I have to manually free or clear any resources?

Question 3: Why is the above solution not happening automatically (internally in JNA) when using the following code (I assume the cause is JNA's misconception that an IDL "long" should be a Java "long", see above comments / please confirm)?

@ComMethod(name = "ClientConnect")
void clientConnect(IntByReference handle, int moduleID, int languageID, String userID, String userPwd);

Thanks a lot! JNA is great! 👍

Matthias Bläsing

unread,
Jan 25, 2024, 12:46:57 PMJan 25
to jna-...@googlegroups.com
Am Donnerstag, dem 25.01.2024 um 01:02 -0800 schrieb Markus Karg:

Unfortunately your code did not work: After the call the variant still has the type vt_empty! So I tried to resemble what JACOB actually does internally, and that successfully worked:

var ref = new WinDef.LONGByReference();
var handleRef = new VARIANT();
handleRef.setValue(VT_I4 | VT_BYREF, ref);
c.clientConnect(handleRef, moduleID, languageID, userID, userPwd);
var handle = ref.getValue().intValue();

NB: TLB / TLD files literally say "long", but both were authored on a 32 Bit machine, and the COM server actually is a 32 Bit EXE, so according to https://learn.microsoft.com/en-us/windows/win32/midl/long this means: (quote) "On 32-bit platforms, long is synonymous with int." (looking at the date when this document was originally authored, what they actually meant to say is: "on all non 16-bit platforms"), hence in Java it must be "int"! I assume that this fact was not known or ignored by the original author of the COM-Support in JNA, as JNA internally seems to think that an IDL "long" should be a Java "long", while it must be a Java "int" always. Could somebody please confirm this?

I won't confirm, that JNA thinks a C long is a java long. The fact that NativeLong exists is a result of the knowledge, that long can change size depending on platform.  Not sure from where where you draw your conclusion.


NB: Surprisingly, JNA does not like to accept IntByReference or LongByReference here, but it really must be WinDef.LONGByReference! Apparently no support for IntByReference or LongByReference exists in JNA's VARIANT class, which one simply has to know (I couldn't find any documentation about that. If it exists I would be happy for a pointer!).

Yes, when you mess with native code and especially VARIANT, you need to know what a variant is and how it is structured. I agree, that VARIANT is not nicely bound, but good luck improving that without introducing new problems.


From here, it should not be too far to a production-grade solution:

Question 1: Is there a simpler / more convenient / otherwise "better" way in JNA to achieve that?

In isolation this is not answerable. The same that holds true for many problems here is also hold true here: Without the code and ways to test it, help is diffcult.

Question 2: Do I have to manually free or clear any resources?

Variants need to be cleared if they contain exotic data (COM objects, SAFEArrays) or the contained data will not be removed. The variant structure itself will be colleced by the VM.

Question 3: Why is the above solution not happening automatically (internally in JNA) when using the following code (I assume the cause is JNA's misconception that an IDL "long" should be a Java "long", see above comments / please confirm)?

Because it is a corner case and noone bother to implement it. When I worked on it I wanted to talk to Word and that worked ok. The API of MS Office is sensibly built to support the dispatch interface, other software (or corners of Office I did not see).

Greetings

Matthias

Markus Karg

unread,
Feb 8, 2024, 2:27:09 AMFeb 8
to Java Native Access
Sad but true, now I have the same trouble with a double. To resolve it, I need to create VARIANT(VT_R4 | VT_BYREF, new DoubleByReference()), but JNA denies to do that (it says it cannot fill DoubleByReference into the flt field of the double). Can someone tell me what the correct *ByReference is to do the same trick here?

Matthias Bläsing

unread,
Feb 8, 2024, 12:44:27 PMFeb 8
to jna-...@googlegroups.com
Am Mittwoch, dem 07.02.2024 um 23:27 -0800 schrieb Markus Karg:
> Sad but true, now I have the same trouble with a double. To resolve it, I need to create VARIANT(VT_R4 | VT_BYREF, new DoubleByReference()), but JNA denies to do that (it says it cannot fill DoubleByReference into the flt field of the double). Can someone tell me what the correct *ByReference is to do the same trick here?

R4 is a 4byte wide floating point value. You are looking for
FloatByRef.

Have a look here:

https://github.com/java-native-access/jna/blob/7da005e8f2ddb7bbe6488418a1bc302c0c67b4e5/contrib/platform/src/com/sun/jna/platform/win32/Variant.java#L337-L478


There you see which field is mapped to which VARTYPE. When you know the
field name you can look at the type it is mapped to.

HTH

Matthias

Markus Karg

unread,
Feb 8, 2024, 12:54:06 PMFeb 8
to Java Native Access
Matthias,

thanks once more, and sorry for not being precise enough.

I actually do know that table and used FloatByRef before, since it is the only *ByRef accepted by JNA. The problem is that the result is always 0.0 then. That's why I wrote, I need to do "the same trick" as you told me with IntByRef. Unfortunately I cannot find a floating-point analogy to the integer-trick.
Reply all
Reply to author
Forward
0 new messages