JNA -> VB COM VARIANT Array of one element?

91 views
Skip to first unread message

Jared Davis

unread,
Apr 13, 2021, 6:58:51 PM4/13/21
to Java Native Access
Hi,

I think I need to create a VARIANT that is an array of 1 integer to match the VB class below.

    /**
     * <p>id(0x60030030)</p>
     * <p>vtableId(59)</p>
     * @param lNumberOfProjects [inout] {@code Integer}
     * @param sPdsg [in, optional] {@code String}
     */
    @ComMethod(name = "ProjectCount", dispId = 0x60030030)
    eProjectReturnCode ProjectCount(VARIANT lNumberOfProjects, String sPdsg);

VB6 Declaration is this:

Public Function ProjectCount(lNumberOfProjects As Long, _
    Optional ByVal sPdsg As String) As eProjectReturnCode

The function alters the INumberOfProjects value and that is the data I need in Java.

One of the issues I have is the INumberOfProjects variable is never updated in java after a call.

from here:

It looks like I need to pass

a java.lang.Integer[] single element array

as the first parameter.

I can not figure out how to do it.

Thanks in advance.

Jared


Jared Davis

unread,
Apr 14, 2021, 1:11:44 PM4/14/21
to Java Native Access
Here is what I have tried:

A test DLL com control was created using the same vb6 compiler so I can share the DLL if anyone wants to try.

COM control does some debugmessage outputs and adds 10 to the passed in number. That is it.

Full vb6 source:

 

Option Explicit

 

Public Enum eProjectReturnCode

    RETURN_ERROR = 0

    RETURN_OK = -1

    RETURN_USER_CANCEL = -2

End Enum

 

Private Declare Sub OutputDebugString Lib "kernel32" Alias "OutputDebugStringA" (ByVal lpOutputString As String)

 

Public Function ProjectCountTest(lNumberOfProjects As Long, Optional ByVal sPdsg As String) As eProjectReturnCode

     OutputDebugString "ProjectCountTest start" + Chr(13) + Chr(10)

     OutputDebugString "ProjectCountTest passed in long " + Str(lNumberOfProjects) + Chr(13) + Chr(10)

     OutputDebugString "ProjectCountTest passed in string [" + sPdsg + "]" + Chr(13) + Chr(10)

    lNumberOfProjects = lNumberOfProjects + 10

     OutputDebugString "ProjectCountTest returning " + Str(lNumberOfProjects) + Chr(13) + Chr(10)

 

  ProjectCountTest = RETURN_OK

  OutputDebugString "ProjectCountTest setting return to OK, may change" + Chr(13) + Chr(10)

 

  If (Len(sPdsg)) > 0 Then

     If sPdsg = "AER" Then

         ProjectCountTest = RETURN_ERROR

       OutputDebugString "ProjectCountTest setting return_error" + Chr(13) + Chr(10)

     ElseIf sPdsg = "BUC" Then

      ProjectCountTest = RETURN_USER_CANCEL

       OutputDebugString "ProjectCountTest setting return_user_cancel" + Chr(13) + Chr(10)

    End If

   End If

 End Function


generated java classes

@ComObject(clsId = "{5D1B16BD-41DD-47B1-9D2C-21627949AE7D}")
public interface COMTester extends IUnknown
    ,_COMTester
{
}


@ComInterface(iid="{C4B5FC56-593D-4935-B7E2-C9B3B05F2541}")
public interface _COMTester extends IUnknown, IRawDispatchHandle, IDispatch {
    /**
     * <p>id(0x60030000)</p>
     * <p>vtableId(7)</p>
     * @param lNumberOfProjects [inout] {@code Integer}
     * @param sPdsg [in, optional] {@code String}
     */
    @ComMethod(name = "ProjectCountTest", dispId = 0x60030000)
    eProjectReturnCode ProjectCountTest(VARIANT lNumberOfProjects,
            String sPdsg);
            
    
}


Send in 1 expect 11 back for the vb LONG java int variable. Never get 11 back.


public class ComTest001 {
    public static void main(String[] args) throws IOException {
        WinNT.HRESULT hr = Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_APARTMENTTHREADED); 
        COMUtils.checkRC(hr);
        Factory fact = new Factory();
        try {
            System.out.println("A");
            COMTester comtest = fact.createObject(COMTester.class);
            System.out.println("B");
            int one = 1;
            
            //IntByReference length = new IntByReference(one);
            //VARIANT vp = new VARIANT(length);
              //C int@0x5d7dc0=0x1 (1)
              //Exception in thread "main" com.sun.jna.platform.win32.COM.COMInvokeException: Type mismatch.(HRESULT: 80020005) (puArgErr=1)
            

            //VARIANT vp = new VARIANT(one);
               // runs but vp does not contain value set in com method.
            
            
            
            //IntByReference length = new IntByReference(one);
            //VARIANT.ByReference vp = new VARIANT.ByReference(length.getPointer());
            //Exception in thread "main" java.lang.IllegalArgumentException: Structure exceeds provided memory bounds
    //at com.sun.jna.Structure.useMemory(Structure.java:372)
     //.. Caused by: java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=4, offset=16

         //   VARIANT v = new VARIANT(one);
         //   VARIANT.ByReference vp = new VARIANT.ByReference(v.getPointer());
         // runs but vp / v do not contain value set in com method.
            
            
            
            //VARIANT v = new VARIANT(one);
            //VARIANT.ByReference vp = new VARIANT.ByReference(v);
            // runs but vp / v do not contain value set in com method.
            
            
            VARIANT v = new VARIANT(one);
            VARIANT vp = new VARIANT(v.getPointer());
            // runs but vp / v do not contain value set in com method.
            
            System.out.println("C  " + vp.getValue());
            eProjectReturnCode rc = comtest.ProjectCountTest(vp, "");
            System.out.println("D  " + rc);
            System.out.println("E  " + vp.getValue());
            System.out.println("EE " + v.getValue());
        } finally {
            fact.disposeAll();
            Ole32.INSTANCE.CoUninitialize();
        }
    }

output

A
B
C  1
D  RETURN_OK
E  1
EE 1

E/ EE should be 11 for a valid run.

debugmessages

[8896] ProjectCountTest start 
[8896] ProjectCountTest passed in long  1 
[8896] ProjectCountTest passed in string [] 
[8896] ProjectCountTest returning  11 
[8896] ProjectCountTest setting return to OK, may change 

Matthias Bläsing

unread,
Apr 14, 2021, 4:11:37 PM4/14/21
to jna-...@googlegroups.com
Hi Jared,

this is a gut feeling, as I have no experience with interfacing with
VB, but Variants are a huge beast and you can not only store value, but
also references to values.

The binding of VARIANT is a bit arkward and maybe I'd have bound it
differently, but that is history.

I suggest to do this:

VARIANT variant = new VARIANT();
LONGLONGByReference longHolder = new LONGLONGByReference();
longHolder.setValue(new LONGLONG(42L));
variant.setValue(Variant.VT_I8 | Variant.VT_BYREF, longHolder);
// Call your code
long result = longHolder.getValue().longValue();

Or (unsigned variant):

VARIANT variant = new VARIANT();
ULONGLONGByReference longHolder = new ULONGLONGByReference();
longHolder.setValue(new ULONGLONG(42L));
variant.setValue(Variant.VT_UI8 | Variant.VT_BYREF, longHolder);
// Call your code
long result = longHolder.getValue().longValue();

The idea is here, that you pass only the reference with a type
signature through the variant.

This is untested and if kittens die, I don't feel responsible.

Greetings

Matthais
> --
> You received this message because you are subscribed to the Google
> Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to jna-users+...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/jna-users/a1a75ac7-43e7-4dd3-b653-61ae9500cc72n%40googlegroups.com
> .


Jared Davis

unread,
Apr 14, 2021, 4:58:33 PM4/14/21
to Java Native Access
Hi,

Thanks.

No kittens harmed, at this time. But exceptions were thrown.

C  LONGLONG@0x12b82e0=42
Exception in thread "main" com.sun.jna.platform.win32.COM.COMInvokeException: Type mismatch.(HRESULT: 80020005) (puArgErr=1)

LONG in vb is an int in java. So I need INTINT. Don’t see one.

DWORD was closest. There is no DWORDDWORDByReference class, yet. I tried to write one. source below.

I think I need a VT_I4 type.

I am probably wrong on the unsigned vs signed issues in DWORD vs int. Don't care at this point. 


            VARIANT v = new VARIANT();
            DWORDDWORDByReference holder = new DWORDDWORDByReference();
            holder.setValue(new DWORDDWORD(42L));
            v.setValue(Variant.VT_I4 | Variant.VT_BYREF, holder);

New interface/classes below throw an error. Stumped again.

 
Throws

Exception in thread "main" java.lang.IllegalArgumentException: Can not set com.sun.jna.platform.win32.WinDef$LONGByReference field com.sun.jna.platform.win32.Variant$VARIANT$_VARIANT$__VARIANT.plVal to jnatest.WinDefExtended$DWORDDWORDByReference
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:758)
at com.sun.jna.Structure.setFieldValue(Structure.java:668)
at com.sun.jna.Structure.setFieldValue(Structure.java:662)
at com.sun.jna.Structure.writeField(Structure.java:837)
at com.sun.jna.Union.writeField(Union.java:143)
at com.sun.jna.platform.win32.Variant$VARIANT.setValue(Variant.java:386)
at com.sun.jna.platform.win32.Variant$VARIANT.setValue(Variant.java:326)
at jnatest.ComTest001.main(ComTest001.java:78)


ComTest001.java:78 is this line:    v.setValue(Variant.VT_I4 | Variant.VT_BYREF, holder);

package jnatest;

import com.sun.jna.IntegerType;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.ptr.ByReference;


public interface WinDefExtended  extends WinDef {
    
     public static class DWORDDWORD extends IntegerType implements Comparable<DWORDDWORD> {
        public static final int SIZE = 4; // WORD =2, DWORD =4
        public DWORDDWORD() {
            this(0);
        }
        public DWORDDWORD(long value) {
            super(SIZE, value, false);
        }
        @Override
        public int compareTo(DWORDDWORD other) {
            return compare(this, other);
        }
    }
    public class DWORDDWORDByReference extends ByReference {
        public DWORDDWORDByReference() {
            this(new DWORDDWORD(0));
        }
        public DWORDDWORDByReference(DWORDDWORD value) {
            super(DWORDDWORD.SIZE);
            setValue(value);
        }
        public void setValue(DWORDDWORD value) {
            getPointer().setInt(0, value.intValue());
        }
        public DWORDDWORD getValue() {
            return new DWORDDWORD(getPointer().getInt(0));
        }
    }
}

Jared Davis

unread,
Apr 14, 2021, 6:52:32 PM4/14/21
to Java Native Access
Hi,

Please ignore the prior message I posted. I was very very wrong. Your reply led me to a solution.


            VARIANT v = new VARIANT();
            LONGByReference longHolder = new LONGByReference(new LONG(42));
            v.setValue(Variant.VT_I4 | Variant.VT_BYREF, longHolder);
            
            System.out.println("C " + v.getValue());
            eProjectReturnCode rc = comtest.ProjectCountTest(v, "");
            System.out.println("D  " + rc);
            System.out.println("EE " + v.getValue());


[4584] ProjectCountTest start 
[4584] ProjectCountTest passed in long  42 
[4584] ProjectCountTest passed in string [] 
[4584] ProjectCountTest returning  52 
[4584] ProjectCountTest setting return to OK, may change 

C LONG@0x916100=42
D  RETURN_OK
EE LONG@0x916100=52

EE is now 52!!!

Thanks!


Reply all
Reply to author
Forward
0 new messages