WM_COPYDATA: Sending complex data between apps

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

WM_COPYDATA: Sending complex data between apps

Post by netmaestro »

There are a few threads dealing with sending strings between apps, and some of them allude to WM_COPYDATA as an alternative, but I couldn't find a good example that shows how to use it successfully. (could be I missed it) Anyhow, I thought I'd post one.

WM_COPYDATA is a message you can send from your application or dll which allows any kind of data to be sent to the application owning the target of the message. You don't even need to have a window open in the sender if you want. In this case, just set wParam to #Null in the message. If the receiver doesn't have a window, you'll have to open a hidden one to receive the message. It isn't a good idea to send this message to HWND_BROADCAST as several mainstream apps process it (I found that out the hard way.) One important feature is that in the receiver you have to copy the data to local variables when you process the message, because the pointer isn't valid later. Here's a pair of sample programs, you can compile them or run them from the IDE, it doesn't matter:

Receiver (Run this first and let it wait for the sender)

Code: Select all

;==================================================================
; Program:         WM_COPYDATA demo - Receiver program
;==================================================================

Enumeration
  #Double_Message ; user-defined identifier that a double is being sent
  #String_Message ;     "             "       "    string      "    "
  #Dog_Message    ;     "             "       "     dog        "    "
EndEnumeration

Structure DOG 
  name.s{10}
  age.l
  weight.l
  breed.s{20}
EndStructure

Procedure.l Callback(hwnd, msg, wparam, lparam)
  result = #PB_ProcessPureBasicEvents
  Select msg
    Case #WM_COPYDATA
      *pp.COPYDATASTRUCT = lparam
      Select *pp\dwData ; determine the type of data being sent and process accordingly
        Case #String_Message
          AddGadgetItem(0,-1, "Got a string: "+PeekS(*pp\lpData) )
        Case #Double_Message
          double.d = PeekD(*pp\lpData)  
          AddGadgetItem(0,-1, "Got a double: "+StrD(double) )
        Case #Dog_Message
          *dog.DOG = *pp\lpData
          With *dog
            name$  = \name 
            age    = \age  
            weight = \weight 
            breed$ = \breed
            s$ = "Got a dog: "+name$+", "+breed$+", "+Str(age)+" years old, "+Str(weight)+" kg."
            AddGadgetItem(0,-1, s$ )
          EndWith
      EndSelect
      result = #True ; You should return #True if processing #WM_COPYDATA
  EndSelect
  ProcedureReturn result
EndProcedure

OpenWindow(0,0,0,320,240,"WM_COPYDATA Receiver", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CreateGadgetList(WindowID(0))
ListViewGadget(0,0,0,320,240)

SetWindowCallback(@Callback(),0)
StickyWindow(0,1)

Repeat
  ev = WaitWindowEvent()
Until ev = #WM_CLOSE
Sender: Run once the receiver is up and waiting

Code: Select all

;==================================================================
; Program:             WM_COPYDATA demo - Sender program
; Author:              Lloyd Gallant (netmaestro)
; Date:                November 24, 2007
; Target Compiler:     PureBasic 4.xx and later
; Target OS:           Microsoft Windows All
; License:             Free, unrestricted, credit appreciated 
;                      but not required
;==================================================================

Enumeration 
  #Double_Message
  #String_Message
  #Dog_Message
EndEnumeration
 
window = FindWindow_(0, "WM_COPYDATA Receiver")

Structure DOG ; complex structure for data to be sent
  name.s{10}
  age.l
  weight.l
  breed.s{20}
EndStructure

; ****************** Prepare a string to be sent ********************

string2send.s = "Testing 123"

*stringdata.COPYDATASTRUCT = AllocateMemory(SizeOf(COPYDATASTRUCT))
With *stringdata
  \dwData = #String_Message
  \cbData = Len(string2send)+SizeOf(CHARACTER)
  \lpData = @string2send
EndWith    

; ******************* Prepare a double to be sent *******************

double2send.d = #PI

*doubledata.COPYDATASTRUCT = AllocateMemory(SizeOf(COPYDATASTRUCT))
With *doubledata
  \dwData = #Double_Message
  \cbData = SizeOf(DOUBLE)
  \lpData = @double2send
EndWith    

; ************* Prepare a complex structure to be sent **************

dog2send.DOG
With dog2send
  \name   = "Bowser"
  \age    = 3
  \weight = 20
  \breed  = "German Shepherd"
EndWith

*dogdata.COPYDATASTRUCT = AllocateMemory(SizeOf(COPYDATASTRUCT))
With *dogdata
  \dwData = #Dog_Message
  \cbData = SizeOf(DOG) 
  \lpData = dog2send
EndWith    

; *******************************************************************

SendMessage_(window, #WM_COPYDATA, #Null, *stringdata)
SendMessage_(window, #WM_COPYDATA, #Null, *doubledata)
SendMessage_(window, #WM_COPYDATA, #Null, *dogdata)
Last edited by netmaestro on Sat Nov 24, 2007 9:07 pm, edited 2 times in total.
BERESHEIT
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Post by Kwai chang caine »

Cool, it works fine :D
I have three results return : :D

Code: Select all

Got a string : Testing 123
Got a double : 3.1415926536
Got a dog: Bowser, German Shepherd, 3 years old, 20 kg.
Xp Sp2, Pb v4.10 beta
ImageThe happiness is a road...
Not a destination
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Very nice. Haven't come across that message before! :)

Thanks.
I may look like a mule, but I'm not a complete ass.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

There seems to be no practical limit to the size of data you can send this way, I just tested with a string 10 million chars in length and it successfully transferred the whole thing.
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Looking at the api docs for this message, it is probably worth noting that in a multi-threaded application, steps must be taken to ensure that secondary threads do not attempt to modify the data being sent. A mutex would of course suffice.

I wonder if the data is physically copied or the underlying memory is temporarily mapped into the receiving processes virtual memory? Interesting.
I may look like a mule, but I'm not a complete ass.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

I wonder if the data is physically copied or the underlying memory is temporarily mapped into the receiving processes virtual memory?
Given the fact that you have to copy the data to local variables upon processing the message, because the pointer in lParam becomes no longer valid, I'd guess the latter. Also MSDN cautions against trying to free the memory pointed to, which would imply the same thing.
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Yes, my thoughts exactly. :)

A very nice way of sharing data that's for sure.
I may look like a mule, but I'm not a complete ass.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: WM_COPYDATA: Sending complex data between apps

Post by IdeasVacuum »

If sender and receiver are compiled as unicode, the received data includes garbage.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
X
Enthusiast
Enthusiast
Posts: 311
Joined: Tue Apr 04, 2006 6:27 am

Re: WM_COPYDATA: Sending complex data between apps

Post by X »

Nice piece of code!

Good point about the unicode.

The other question is, have anyone tested the speed of the transfer between programs?
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: WM_COPYDATA: Sending complex data between apps

Post by netmaestro »

Afaik it's 100km/h until it gets close then the speed limit drops to 60 :mrgreen:

Seriously, it's typically not going to be more than will fit in free ram and so it's a straight memory copy. Blazing fast.
BERESHEIT
X
Enthusiast
Enthusiast
Posts: 311
Joined: Tue Apr 04, 2006 6:27 am

Re: WM_COPYDATA: Sending complex data between apps

Post by X »

This could be useful for shards and AI servers that live on the same machine.

Hmm. I'm thinking about eve technology, but that is a whole new interconnect problem ;)

Thanks for the code! I'll h have to poke and prod for that unicode issue.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: WM_COPYDATA: Sending complex data between apps

Post by netmaestro »

I ran this test today on my win7 x86 machine with PB 4.6b3 and I'm not getting any problems with unicode. Are you possibly using x64?
BERESHEIT
User avatar
happer66
User
User
Posts: 33
Joined: Tue Jan 12, 2010 12:10 pm
Location: Sweden

Re: WM_COPYDATA: Sending complex data between apps

Post by happer66 »

Replace Len() with StringByteLength() in the sender code and it should be good to go.

Code: Select all

\cbData = StringByteLength(string2send)+SizeOf(CHARACTER)
Nice old gem btw netmaestro!
Image If your code isn't clean atleast make sure it's pure!
Post Reply