SendMessage WM_COPYDATA

S

Sean

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.dll")]
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));
}

}
 
S

Sean

class WIN32 {

Is Win32 {
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;

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

Mattias Sjögren

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

How does the recieving code look like?

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

This is better declared as

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

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

lpData and dwData should be IntPtrs.

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;
}

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
 
S

Sean

Mattias said:
How does the recieving code look like?

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.

lpData and dwData should be IntPtrs.

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
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.

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.StringToCoTaskMemAnsi(str);

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

string str =
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(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.AllocCoTaskMem(str.Length);

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

System.IntPtr iPtr =
System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds));
System.Runtime.InteropServices.Marshal.StructureToPtr(cds,
iPtr, true);

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

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

Sean

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

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

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

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.AllocCoTaskMem(str.Length);

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

System.IntPtr iPtr =
System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds));
System.Runtime.InteropServices.Marshal.StructureToPtr(cds,
iPtr, true);

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


System.Runtime.InteropServices.Marshal.FreeCoTaskMem(cds.lpData);
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(iPtr);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top