Connect with Expertise | Find Experts, Get Answers, Share Insights

SendMessage WM_COPYDATA

Sean
 
Posts: n/a
#1: Jun 3 '06
I am trying to send a WM_COPYDATA message to another application in C#,
..NET 2.0.

The other application receives the message, but only seems to see the
first character of the string, does anybody have any ideas?

I have copied the code below.

class WIN32 {

//SendMessage
[System.Runtime.InteropServices.DllImport("user32.d ll")]
public static extern int SendMessage(System.IntPtr hwnd, int
msg, int wparam, int lparam);


//Copy Data Structure
public struct COPYDATASTRUCT
{
public int dwData;
public int cbData;
public int lpData;
}

public static int VarPtr(object e)
{
System.Runtime.InteropServices.GCHandle GC =
System.Runtime.InteropServices.GCHandle.Alloc(e,
System.Runtime.InteropServices.GCHandleType.Pinned );
int gc = GC.AddrOfPinnedObject().ToInt32();
GC.Free();
return gc;
}

}

public class SendTheMessage {

public SendTheMessage(string str, System.IntPtr handle,
System.IntPtr iHandle, System.UInt32 signature) {
Win32.COPYDATASTRUCT cds;

cds.dwData = Convert.ToInt32(signature);
str = str + '\0';
cds.cbData = 3;
cds.lpData = Win32.VarPtr(str);

Win32.SendMessage(handle, Win32.WM_COPYDATA,
0, Win32.VarPtr(cds));
}

}


Sean
 
Posts: n/a
#2: Jun 3 '06

re: SendMessage WM_COPYDATA


> class WIN32 {

Is Win32 {
[color=blue]
>
> public class SendTheMessage {
>
> public SendTheMessage(string str, System.IntPtr handle,
> System.IntPtr iHandle, System.UInt32 signature) {
> Win32.COPYDATASTRUCT cds;
>
> cds.dwData = Convert.ToInt32(signature);
> str = str + '\0';
> cds.cbData = 3;[/color]

is str.Length (and tried str.Length * 2 ). It's only 3 here from
testing!

Mattias Sjögren
 
Posts: n/a
#3: Jun 3 '06

re: SendMessage WM_COPYDATA


>The other application receives the message, but only seems to see the[color=blue]
>first character of the string, does anybody have any ideas?[/color]

How does the recieving code look like?

[color=blue]
> //SendMessage
> [System.Runtime.InteropServices.DllImport("user32.d ll")]
> public static extern int SendMessage(System.IntPtr hwnd, int
>msg, int wparam, int lparam);[/color]

This is better declared as

[System.Runtime.InteropServices.DllImport("user32.d ll",
CharSet=CharSet.Auto)]
public static extern IntPtr SendMessage(System.IntPtr hwnd, int msg,
IntPtr wparam, ref COPYDATASTRUCT lparam);

[color=blue]
> //Copy Data Structure
> public struct COPYDATASTRUCT
> {
> public int dwData;
> public int cbData;
> public int lpData;
> }[/color]

lpData and dwData should be IntPtrs.

[color=blue]
> public static int VarPtr(object e)
> {
> System.Runtime.InteropServices.GCHandle GC =
>System.Runtime.InteropServices.GCHandle.Alloc(e ,
>System.Runtime.InteropServices.GCHandleType.Pinne d);
> int gc = GC.AddrOfPinnedObject().ToInt32();
> GC.Free();
> return gc;
> }[/color]

This is completely broken. If a garbage collection happens after the
GC.Free call, your pointer is invalid. Use one of the
Marshal.StringTo* methods to copy the string to a native buffer
instead, and assign the result to cds.lpData.


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Sean
 
Posts: n/a
#4: Jun 3 '06

re: SendMessage WM_COPYDATA


Mattias Sjögren wrote:[color=blue][color=green]
> >The other application receives the message, but only seems to see the
> >first character of the string, does anybody have any ideas?[/color]
>
> How does the recieving code look like?[/color]

I dont know, this is in a closed source application. Applications
written in other languages seem to have no problem communicating with
it, so it must be an error in my C# code.

[color=blue][color=green]
> > //Copy Data Structure
> > public struct COPYDATASTRUCT
> > {
> > public int dwData;
> > public int cbData;
> > public int lpData;
> > }[/color]
>
> lpData and dwData should be IntPtrs.[/color]

Yes, I have changed that. They were originally, but changed a lot to
retry. I have changed the last paramenter of SendMessage to an IntPtr
[color=blue]
>[color=green]
> > public static int VarPtr(object e)
> > {
> > System.Runtime.InteropServices.GCHandle GC =
> >System.Runtime.InteropServices.GCHandle.Alloc(e ,
> >System.Runtime.InteropServices.GCHandleType.Pinne d);
> > int gc = GC.AddrOfPinnedObject().ToInt32();
> > GC.Free();
> > return gc;
> > }[/color]
>
> This is completely broken. If a garbage collection happens after the
> GC.Free call, your pointer is invalid. Use one of the
> Marshal.StringTo* methods to copy the string to a native buffer
> instead, and assign the result to cds.lpData.[/color]

Thanks. I copied that somewhere off the internet, which I knew would
be a bad idea.

I have changed that method to look like this now, but the same result
occurs, the receiving application is only receiving the first 1
character.

I also tried:
cds.lpData =
System.Runtime.InteropServices.Marshal.StringToCoT askMemAnsi(str);

Because this is the reverse of what I do in my overridden wndproc:

string str =
System.Runtime.InteropServices.Marshal.PtrToString Ansi(cds.lpData);

this str, in wndproc, always contains the full string the application
sent me.

--- Updated Function Code ---
Win32.COPYDATASTRUCT cds;

cds.dwData = new System.IntPtr(sig);
str = str + '\0';
cds.cbData = str.Length;
cds.lpData =
System.Runtime.InteropServices.Marshal.AllocCoTask Mem(str.Length);

System.Runtime.InteropServices.Marshal.Copy(str.To CharArray(), 0,
cds.lpData, str.Length);

System.IntPtr iPtr =
System.Runtime.InteropServices.Marshal.AllocCoTask Mem(System.Runtime.InteropServices.Marshal.SizeOf( cds));
System.Runtime.InteropServices.Marshal.StructureTo Ptr(cds,
iPtr, true);

Win32.SendMessage(handle, Win32.WM_COPYDATA, iHandle,
iPtr);

//I would add some code here to Free the Memory

Sean
 
Posts: n/a
#5: Jun 3 '06

re: SendMessage WM_COPYDATA


> I also tried:[color=blue]
> cds.lpData =
> System.Runtime.InteropServices.Marshal.StringToCoT askMemAnsi(str);
>
> Because this is the reverse of what I do in my overridden wndproc:
>
> string str =
> System.Runtime.InteropServices.Marshal.PtrToString Ansi(cds.lpData);[/color]

This actually did work. I forgot to comment the line below it which
was doing what my old code did.

Thanks Mattias your post got me on right track to making it all work.
For anybody that cares, here
is the code:


Win32.COPYDATASTRUCT cds;

cds.dwData = new System.IntPtr(sig);
str = str + '\0';
cds.cbData = str.Length + 1;
cds.lpData =
System.Runtime.InteropServices.Marshal.AllocCoTask Mem(str.Length);

cds.lpData =
System.Runtime.InteropServices.Marshal.StringToCoT askMemAnsi(str);

System.IntPtr iPtr =
System.Runtime.InteropServices.Marshal.AllocCoTask Mem(System.Runtime.InteropServices.Marshal.SizeOf( cds));
System.Runtime.InteropServices.Marshal.StructureTo Ptr(cds,
iPtr, true);

Win32.SendMessage(handle, Win32.WM_COPYDATA, iHandle,
iPtr);


System.Runtime.InteropServices.Marshal.FreeCoTaskM em(cds.lpData);
System.Runtime.InteropServices.Marshal.FreeCoTaskM em(iPtr);

Closed Thread