Int Byte Ptr

BlitzMax Forums/BlitzMax Programming/Int Byte Ptr

Eternal Crisis(Posted 2012) [#1]
I'm sending a message to a second application using SendMessageW() but I cannot figure out how to retrieve that message correctly on the second application.

TestApp.bmx
Type TProcessMessage
  Field Message:Short Ptr
EndType

Local tmpMsg:TProcessMessage = New TProcessMessage
Local tmpMsgPtr:Short Ptr
Local tmpMsgStr:String = "THIS IS A TEST"

tmpMsg.Message = tmpMsgStr.ToWString()

SendMessageW(pHandle, WM_COPYDATA, 0, Int Byte Ptr tmpMsg)


I've seen throughout "win32maxguiex.bmx" send their Types using "Int Byte Ptr" so I had followed suit. Now, for the other application, I've edited win32maxguiex.bmx to process the "WM_COPYDATA" type message, and this is where I can't figure out how to get that information back that was passed.

win32maxguiex.bmx - starts on line 363 (Function ClassWndProc(hwnd,msg,wp,lp) "win32"
Select msg
  
  Case WM_COPYDATA
    ' ... here


How do I basically get my TProcessMessage back from "lp"? I've tried so many different ways but it basically seems like I can't?

Any ideas are appreciated.


Eternal Crisis(Posted 2012) [#2]
If not a Type, then what about just a Int Short Ptr? I'm only really interested in sending a string. I was hoping for Types so that I could send more complex messages in the near future.

Thanks.


Htbaa(Posted 2012) [#3]
Yeah I believe you can't exchange types with C/C++ and BlitzMax. Just pass a pointer to the actual string.


col(Posted 2012) [#4]

Yeah I believe you can't exchange types with C/C++ and BlitzMax


You can but not in that way.


Like Htbaa says just pass a pointer to the string...

Local tmpMsg:Short Ptr = tmpMsgStr.ToWString()
SendMessageW(.......,Int Byte Ptr tmpMsg 'lp is just a regular integer so cast it

to send the string

and

......GetMessageW(hwnd,msg,wp,lp) -< Or whatever you're using
String.FromWString(Short Ptr lp)

should work to recieve it. Haven't tested so...

Last edited 2012


Eternal Crisis(Posted 2012) [#5]
Thanks for the replies, and thanks for the example, col.

I've tried your example and SendMessageW keeps triggering an EXCEPTION_ACCESS_VIOLATION. I'm not exactly sure why at the moment but if I try anything but a Type it gives the error.


col(Posted 2012) [#6]
The EAV...

You're sending the wrong data if you want to send a WM_COPYDATA message...

http://msdn.microsoft.com/en-us/library/windows/desktop/ms649011(v=vs.85).aspx


Eternal Crisis(Posted 2012) [#7]
Ah, good eye. I must have looked at that page 500 times. If I can't send a Type, retrieve it's information, how in the world would I do this? lol

Going to start trying.

Thanks col.


col(Posted 2012) [#8]
It looks like you can set something up like this...

Local Msg$="THIS IS A TEST"
Local MsgData[3]

MsgData[0] = Int( Msg$.ToWString() )
MsgData[1] = SizeOf(Msg)
MsgData[2] = 0 ; Or if it doesnt work then try 'Int( Msg$.ToWString() )' here too

SendMessageW( pHandle, WM_COPYDATA, pHandle, Varptr MsgData ) '<- May not need the Varptr? Try with/without

*Untested

Last edited 2012

Last edited 2012


col(Posted 2012) [#9]
Its not as hard as it initially looks :-)

Just remember that structures and types are simply memory addresses that contain bunches of numbers, and a pointer is usually a 32bit memory address, which is a standard Int. Whats important is how those number are laid out. You need to know the size of the data type(s) that the structure contains.

The WM_COPYDATASTRUCT is a nice easy example to use:-

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

This is a simple one, break it down line by line...

Second line 'ULONG_PTR dwData'. You may not know what a ULONG PTR is but the real hint is 'dwData'. The little dw means Double Word, which is usually a 32bit value ie an Int
Third line 'DWORD cbData;' DWORD again so its a 32bit value - another Int.
Fouth 'PVOID lpData;' lp hints as a LONG PTR again so its a 32bit value again, so another Int

Dont worry about the last line :D unless you're going to use c/c++. It's just other ways to handle the struct itself.

So all in all, you need some way of getting 3 integers in memory in a row, the easiest way is an array :P

Then poke the values that are required according to MSDN into the array and pass a pointer ( usually ) to the array as a parameter thats expecting the struct :P

And yes at first you'll play/hack around until you get used to the correct address to pass in :D

Last edited 2012


col(Posted 2012) [#10]
ps.

If you're going to run it Vista/Win7 machines you'll need to use

ChangeWindowMessageFilter(WM_DATACOPY,1)

to allow the message go into the message system otherwise it gets blocked.

EDIT: That function opens the message to the whole system. Win7 specific :- You can specify it for just for your app windows using

ChangeWindowMessageFilterEx(pMyWindowHandle, WM_DATACOPY, 1, 0)
http://msdn.microsoft.com/en-us/library/dd388202(v=vs.85).aspx

Last edited 2012


Eternal Crisis(Posted 2012) [#11]
Thanks for the help, col, greatly appreciated. I'm still bashing away at this trying to get the actual message.

The SendMessageW "lParam" expects an Int.

I have ChangeWindowMessageFilter() already from when I started doing all of this since yesterday since I read the comments on the MSDN pages. I never could get "ChangeWindowMessageFilterEx" to work (log always claimed it couldn't be found in the DLL).

Didn't get this to work yet but I at least feel closer.

At the moment I'm trying to send it with: Int Byte Ptr MsgData, again. The result I get from the receiving end is:

local lpip:int ptr = int ptr(lp)
lpip[0] is the "handle" and I'm guessing lpip[1] is the "wParam" and lpip[2] may be the "lParam", not sure.. even if I was sure this was true I'm unsure on how to retrieve this as an array, or anything useful, so I can get the actual message, lol.

EDIT: ..no, that's not right

Last edited 2012


col(Posted 2012) [#12]
After a look at the example try this, pay extra detail to the parameters expected in SendMessage, and the conversion back in lparam...

In the sender...


then in the reciever...


lparam is the address of your MsgData that you sent ( might be different but doesnt matter ). Easiest way to the data back is to use a simple MemCopy :-
MemCopy MsgData,lparam,12 ( Int is 4 bytes and we had 3 Int in the array to make 12 bytes to copy )

You can send multiple different messages and use MsgData[0] as a clue to the type of data you've sent. Like you use any value to define that you sent a string and another value to say youve a Type or one kind, a different value for another Type. Then MsgData[1] will be the size of that data or the Type, MsgData[2] will be the address of the Type in memory. You can create a New Type and MemCopy from MsgData[2] directly into the first Field of the Type, using MsgData[1] for the size parameter.

You might need to throw in a couple of Varptr to get it correctly because I think you'll find the lparam value is a pointer to the MsgData.

Last edited 2012


col(Posted 2012) [#13]
Here you go :P

A complete working example...

This opens 2 windows - Window0 on the left and Window1 on the right, check the window titles.
Press SPACE to send a changing message from Window0 to Window1 via WM_COPYDATA. This sends a simple string and sets the window title to that string.
Press T to send an instance of TestType from Window1 to Window 0 via WM_COPYDATA. Window1 creates the new Type and changes the data in it then sends it over to Window0 which then builds a string from that data and sets the titlebar to the string :D

I wrote this on Win7. There was no need to use 'ChangeWindowMessageFilter' function, maybe because I have the correct privileges as a user etc?



Last edited 2012


col(Posted 2012) [#14]
Looking at the reciever code, I can see some potential bugs creeping in. When it comes to keeping the data from the MsgData[2] , as it is a MemCopy then when the instance of the original sent type goes out of scope then the data may get collected making pointers to strings etc invalid so don't rely on it being valid for long. I'd recommend making a manual field copy to another instance that you want to keep so the GC knows about the fields values. I don't know for a fact if the gc would cause this issue or not, but just to be safe I'd guess it will.

Using that analogy... the reason the code works correctly as it is is because the data is sent to the window title bar so no doubt the window will hold its own local copy so it doesn't ematter if the original goes out of scope and gets collected up. Again this is an educated guess.


Eternal Crisis(Posted 2012) [#15]
Thank you very much for all of your help, col. It is greatly appreciated.

Our testing scenarios are different so the end result is different. I have my main application running and the one sending the message is a second application (that has no window or anything). I was trying to send a message from the application to the window and the overall purpose is two fold;

1) Stop two instances of the main application from running (this was my test application which will now be moved over to the main application to perform all of this when executed)
2) If there is a second instance then it will send the applications arguments to the instance that is already running (hence the window data message).

This is what I had to do to get this to send the string:

Sender:
Local MsgStr:String = "THIS IS A TEST"
Local MsgData:int[3]
MsgData[0] = 1
MsgData[1] = Len(MsgStr)+1
MsgData[2] = Int(MsgStr.ToCString())

ChangeWindowMessageFilter(WM_COPYDATA, 1)  ' Just in case
SendMessageW(pHandle, WM_COPYDATA, 0, Int Byte Ptr MsgData)


Receiver:
Local MsgData:int[3]
MemCopy(MsgData, Byte Ptr lp, 12)

Notify(String.FromCString(Byte Ptr MsgData[2]))


And it works! Thank you again, col.

MsgData[1] needed to be the size of the string or the data wouldn't be correct at all. Without the +1 added to the length, it would cut off the last letter and/or insert random characters. Glad to finally get this working as it's been now three days.

Cheers, col.

Add me on Skype: RATSPaul


col(Posted 2012) [#16]
Excellent :D

It was good for me to learn this too :P

Glad you got it working how you wanted.
I'd guess the +1 is just including the Null terminator in the CString length.

Added.

Last edited 2012