Reputation: 480
I am trying to send a String to a message-only window that I have created in java using JNA.
I am using SendMessage
to send a message. (I know that it is not recommended to use FindWindow every time I send a message, but this is just for this example here)
SendMessage(FindWindow(NULL,"jnaWnd"), WM_USER + 10, 0, (LPARAM)L"Test");
On the Java (JNA) side, I had to use this interface for a proper WindowProc implementation
Besides that, I also made my way through creating a message-only hwnd on the java side. But this is not relevant for this problem.
Anyway, this is the implementation of the interface.
@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);
}
}
Everything works fine, and I do receive the messages. Working with Integers works fine, and conversion is also easy.
But I have no clue how to parse the String out of the LPARAM value.
JNA is very well known for converting types on the fly by using the java equivalent type in the methods.
The first problem is that I use this interface, and therefore I need to stick to the params defined in the interface.
The second problem is that I also want to work with Integer values as well, which means that I cannot just replace LPARAM lParam
with String lParam
Even tho I could try to dig deeper on the JNA side to create my own interface and such... I just wanted to ask here before I move forward on this topic.
Maybe there is a simple way to get the String value out of the LPARAM
Any tips are appreciated.
EDIT_1:
I tried to use the WM_COPYDATA method, but this still leads to an invalid memory access
exception...
This is what I have tried so far:
I defined a new struct named STRMSG
struct on both sides;
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;
};
And here is the new SendMessage procedure:
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);
I also added the following case in my callback method on the java side:
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);
}
The invalid memory error happens anyway :(...
Upvotes: 1
Views: 738
Reputation: 595392
If the message-only window exists in a different process, then you can't send a string via a raw pointer across process boundaries in the manner you are trying. The sent pointer is only valid in the context of the sending process, it will not point at valid memory which exists in the context of the target process. So, you must marshal the string data instead (ie, actually copy the characters into the memory of the target process), such as with WM_COPYDATA
(or any other IPC mechanism, such as sockets, pipes, ActiveX/COM, etc). You can't just send a raw pointer, as you are trying to do (unless you use VirtualAllocEx()
to allocate the pointed-at memory in the target process, so the raw pointer is valid in that process. But don't go this way in this situation, there are better ways).
In your EDIT, your WM_COPYDATA
is suffering from the exact same problem as your orignal code. Your sender is still sending a raw pointer as-is, just wrapped inside of a STRMSG
struct, and then your receiver is trying to use the received pointer to access memory which does not exist in the receiving process. You didn't marshal the string data at all. Get rid of STRMSG
and point the COPYDATASTRUCT
directly at the string data so WM_COPYDATA
will copy the actual characters.
Also, Pointer.getString()
likely won't work in this situation, as the string being sent is a wchar_t
string, so you should use Pointer.getWideString()
instead.
With all of that said, try something more like this instead:
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);
}
Upvotes: 1