0

I have struggled a lot to make it work (a call to win32 API : SendMessage with WM_COPYDATA and COPYDATASTRUCT to hold the data) and as it works on my windows 7 computer now, I am wondering if my mapping is the good one and if there is no side effect of my solution ?

Here is my code :

/**
     * For usage with WM_COPYDATA
     * cf : https://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx
     */
    long SendMessage(HWND hWnd, int msg, WPARAM wParam, COPYDATASTRUCT.ByReference lParam);

    int WM_COPYDATA = 0x004A;


//cf : https://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx
class COPYDATASTRUCT extends Structure {

    public static class ByReference extends COPYDATASTRUCT implements Structure.ByReference {
    }

    public COPYDATASTRUCT() {
        super();
    }

    public int dwData;
    public long cbData;
    public Pointer lpData;

    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] { "dwData", "cbData", "lpData" });
    }
}

And the calling code with 2 examples :

User32Extension.COPYDATASTRUCT.ByReference dataStruct = new User32Extension.COPYDATASTRUCT.ByReference();
        String message = "Hello ! :-) !";
        Memory m = new Memory(message.length() + 1);
        m.setString(0, message);
        dataStruct.dwData = 10;
        dataStruct.cbData = message.length() + 1;
        dataStruct.lpData = m;
        dataStruct.write(); // writes to native memory the structure.
        result = user32.SendMessage(hwndTarget, // target hwnd.
                User32Extension.WM_COPYDATA, // copy data message.
                wparam, // current hwnd
                dataStruct // data by reference here
        );

        User32Extension.COPYDATASTRUCT.ByReference myDataStruct = new User32Extension.COPYDATASTRUCT.ByReference();
        User32Extension.TEST_STRUCT myStruct = new User32Extension.TEST_STRUCT();
        //simple C structure here with 4 fields of C types int, char, char and long.
        myStruct.iNumber = 677;
        myStruct.cCode = 'E';
        myStruct.cCode2 = 'T';
        myStruct.lLong1 = new NativeLong(123456789L);
        myStruct.write();
        LOGGER.trace("myStruct (size=" + myStruct.size() + ")=" + myStruct.toString(true));

        myDataStruct.dwData = 11;
        myDataStruct.cbData = myStruct.size();
        myDataStruct.lpData = myStruct.getPointer();
        myDataStruct.write(); // writes to native memory the structure.
        result = user32.SendMessage(hwndTarget, // target hwnd.
                User32Extension.WM_COPYDATA, // copy data message.
                wparam, // current hwnd
                myDataStruct // data
        );

The key thing is this code compared to everything I have found on the net, is that COPYDATASTRUCT attribute cbData is of type long. If I set to int, it does not work (data is not correctly received in WndProc of the legacy C application). Is it correct to map a DWORD to a long java type ? Would it be better with a NativeLong ?

Another thing, that is to be noted, is the explicit call to Structure.write() for all the instantiated Structures (myStruct and myDataStruct). It is necessary in order to not have an empty memory before calling the SendMessage api. Do you think it is normal ? Or jna should call it automatically before invoking the SendMessage ?

Thanks in advance.

cnico7
  • 3
  • 2
  • JNA provides definitions for `DWORD` and other windows types. Any usage of native `long` type should be represented by JNA's `NativeLong` type. `Structure.write()` is called by JNA automatically before any native function call with structure arguments. – technomage Nov 02 '16 at 12:33
  • @technomage, for the write, I did another test : it is not necessary for correct behaviour. I added it for debug with a call to `Structure.toString(true)`. For the DWORD type, if I use it, the message does not arrive to the C called program... – cnico7 Nov 03 '16 at 21:58
  • Here is for information, the memory dumps in the trace (for test with the string Hello) : memory dump with DWORD for cbData : [0a000000] [0e000000] [00c2d058] [00000000] memory dump with long for cbData : [0a000000] [00000000] [0e000000] [00000000] [306eda58] [00000000] – cnico7 Nov 03 '16 at 22:05
  • Perhaps a correct mapping could be long for dwData and int for cbData ? In such a case, it works sucessfully and the memory dump is : [0a000000] [00000000] [0e000000] [00000000] [40f56958] [00000000] – cnico7 Nov 03 '16 at 22:07

1 Answers1

0

This native code:

typedef struct tagCOPYDATASTRUCT {
  ULONG_PTR dwData;
  DWORD     cbData;
  PVOID     lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

maps to this JNA Structure definition:

public class COPYDATASTRUCT extends Structure {
    ULONG_PTR dwData;
    DWORD     cbData; // or use "int"
    Pointer   lpData;
}

The first and last fields will be of different size (and the structure will have different alignment/padding) depending on whether you're running 32- or 64-bit.

technomage
  • 9,861
  • 2
  • 26
  • 40
  • It seems obvious with your answer and it is what I tested first but did not worked (did find back the reason). By now, with my current test, it works if my JVM is 32 or 64 bits. And I confirm that dwData with long type does only work in 64bits JVM but not in 32 bits JVM. So the type ULONG_PTR has indeed a different size depending on the 32 or 64 bits JVM that uses JNA to send the message. Thank you. – cnico7 Nov 05 '16 at 15:47
  • I believe that technically `ULONG_PTR` is meant to be an unsigned long that's big enough to fit a pointer, so it would make sense that it's defined differently on 32 vs 64 bits. The JNA definition of the type reflects that fact, using `Pointer.SIZE` to determine how big it should be. – technomage Nov 07 '16 at 16:30