如何将字符串从 C++ 应用程序发送到消息 window in java(LPARAM 到字符串的转换)

How to send a string from c++ app to a message only window in java (LPARAM to String conversion)

我正在尝试将字符串发送到我在 java 中使用 JNA 创建的仅消息 window。

我正在使用 SendMessage 发送消息。 (我知道不建议每次发消息都用FindWindow,不过这里只是为了这个例子)

SendMessage(FindWindow(NULL,"jnaWnd"), WM_USER + 10, 0, (LPARAM)L"Test");

在 Java (JNA) 方面,我不得不使用这个 interface 来实现正确的 WindowProc

除此之外,我还在 java 端创建了一个仅消息 hwnd。但这与此问题无关。

不管怎样,这就是接口的实现。

@Override
public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case 1034: { //1034 is WM_USER + 10
            //These lines doesn't work.... :(

            //java.lang.UnsupportedOperationException: This pointer is opaque
            String s = lParam.toPointer().getString(0);

            // java.lang.Error: Invalid memory access
            String s = lParam.toPointer().getWideString(0);
            return new LRESULT(0);
        }
        default:
            return JNA.USER32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

一切正常,我确实收到了消息。 使用整数工作正常,转换也很容易。

但我不知道如何从 LPARAM 值中解析出字符串。

JNA 以通过在方法中使用 java 等效类型来动态转换类型而闻名。

第一个问题是我使用这个interface,因此我需要坚持接口中定义的参数。

第二个问题是我也想使用整数值,这意味着我不能只用 String lParam

替换 LPARAM lParam

即使我可以尝试在 JNA 方面进行更深入的挖掘以创建我自己的界面等等... 在继续讨论这个话题之前,我只是想在这里问一下。

也许有一种简单的方法可以从 LPARAM

中获取字符串值

如有任何提示,我们将不胜感激。

EDIT_1:

我尝试使用 WM_COPYDATA 方法,但这仍然导致 invalid memory access 异常...

这是我目前尝试过的方法:

我在两边都定义了一个名为STRMSG结构的新结构;

Java:

public class STRMSG extends Structure{

    public STRMSG() {
        super();
    }

    public STRMSG(Pointer p) {
        super(p);
        read();
    }

    public String message;

    protected List<String> getFieldOrder() {
        return Arrays.asList("message");
    }
}

C++:

typedef struct STRMSG {
    LPCSTR message;
};

这是新的 SendMessage 过程:

STRMSG msg;
msg.message = "Some message";

COPYDATASTRUCT cds;
cds.dwData = 10;
cds.cbData = sizeof(STRMSG);
cds.lpData = &msg;
                    
SendMessage(FindWindow(NULL, L"jnaWnd"), WM_COPYDATA, 0, (LPARAM)&cds);

我还在 java 端的回调方法中添加了以下情况:

case WinUser.WM_COPYDATA: {
    COPYDATASTRUCT cds = new COPYDATASTRUCT(new Pointer(lParam.longValue()));
    ULONG_PTR uMsg1 = cds.dwData;
    Pointer lParam1 = cds.lpData;
    int wParam1 = cds.cbData;

    switch(uMsg1.intValue()){
        case 10: {
            STRMSG msg = new STRMSG(lParam1); // Error: Invalid memory access here
            Logger.debug(msg.message);
        }
    }
    return new LRESULT(0);
}

无论如何都会发生无效内存错误:(...

如果仅消息 window 存在于不同的进程中,则您无法以您尝试的方式通过原始指针跨进程边界发送字符串。发送指针仅在发送进程的上下文中有效,它不会指向目标进程上下文中存在的有效内存。因此,您必须改为编组字符串数据(即,实际将字符复制到目标进程的内存中),例如使用 WM_COPYDATA(或任何其他 IPC 机制,例如套接字、管道、ActiveX/COM, ETC)。您不能像尝试做的那样只发送原始指针(除非您使用 VirtualAllocEx() 在目标进程中分配指向的内存,因此原始指针在该进程中有效。但是不要在这种情况下不要走这条路,有更好的方法)。

在您的编辑中,您的 WM_COPYDATA 遇到与原始代码完全相同的问题。您的发送方仍在按原样发送原始指针,只是包裹在 STRMSG 结构中,然后您的接收方试图使用接收到的指针来访问接收进程中不存在的内存。您根本没有编组字符串数据。摆脱 STRMSG 并将 COPYDATASTRUCT 直接指向字符串数据,这样 WM_COPYDATA 将复制实际字符。

此外,Pointer.getString() 在这种情况下可能不起作用,因为发送的字符串是 wchar_t 字符串,因此您应该改用 Pointer.getWideString()

综上所述,试试这样的方法:

const UINT myStringId = RegisterWindowMessage(L"MyStringDataID");

...

HWND hwnd = FindWindow(NULL, L"jnaWnd");
if ((hwnd != NULL) && (myStringId != 0))
{
    std::wstring msg = L"Test";

    COPYDATASTRUCT cds;
    cds.dwData = myStringId;
    cds.cbData = (msg.size() + 1) * sizeof(wchar_t);
    cds.lpData = const_cast<wchar_t*>(msg.c_str());
                    
    SendMessage(hwnd, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&cds));
}
final int myStringId = JNA.USER32.INSTANCE.RegisterWindowMessage("MyStringDataID");

...

@Override
public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WinUser.WM_COPYDATA: {
            COPYDATASTRUCT cds = new COPYDATASTRUCT(new Pointer(lParam.longValue()));
            if ((myStringId != 0) && (cds.dwData.intValue() == myStringId)) {
                String s = cds.lpData.getWideString(0);
                Logger.debug(s);
                return new LRESULT(0);
            }
            break;
        }
    }

    return JNA.USER32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
}