I\'ve been spending a few days (or more) trying to get this to work.
The application at hand is FTPRush, and I know there is a cmd line application call
My Definitions have
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
public struct COPYDATASTRUCT {
public int cbData;
public IntPtr dwData;
[MarshalAs(UnmanagedType.LPStr)] public string lpData;
}
var cds = new Win32.COPYDATASTRUCT {
dwData = new IntPtr(3),
cbData = str.Length + 1,
lpData = str
};
Win32.SendMessage(ftprush, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
Of course, make sure that str is null terminated "\0"
Alternatively a definition given by PInvoke.NET is
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam);
//If you use '[Out] StringBuilder', initialize the string builder with proper length first.
Between the 2 answers above I cobbled together a working example. Bryce Wagner's class works, so I added a method to use SendMessageTimeout to send the data. it's a static method, so you just call it to send data. This isn't really my work, just gluing together and sharing back.
[StructLayout(LayoutKind.Sequential)]
public struct CopyData: IDisposable {
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, ref CopyData target,
SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult);
[Flags]
enum SendMessageTimeoutFlags: uint {
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
const uint WM_COPYDATA = 0x4A;
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
public void Dispose() {
if (lpData != IntPtr.Zero) {
Marshal.FreeCoTaskMem(lpData);
lpData = IntPtr.Zero;
cbData = 0;
}
}
public string AsAnsiString {
get { return Marshal.PtrToStringAnsi(lpData, cbData); }
}
public string AsUnicodeString {
get { return Marshal.PtrToStringUni(lpData); }
}
public static CopyData CreateForString(int dwData, string value, bool Unicode = false) {
var result = new CopyData();
result.dwData = (IntPtr) dwData;
result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value);
result.cbData = value.Length + 1;
return result;
}
public static UIntPtr Send(IntPtr targetHandle, int dwData, string value, uint timeoutMs = 1000, bool Unicode = false) {
var cds = CopyData.CreateForString(dwData, value, Unicode);
UIntPtr result;
SendMessageTimeout(targetHandle, WM_COPYDATA, IntPtr.Zero, ref cds, SendMessageTimeoutFlags.SMTO_NORMAL, timeoutMs, out result);
cds.Dispose();
return result;
}
}
To use it:
CopyData.Send(targetHandle, 1234, "This is a test");
That uses the default 1 second timeout.
The order of arguments in the COPYDATASTRUCT are critically important, and Bob Vale's answer has them in the wrong order. http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx It should be in this order:
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
I haven't gotten the MarshalAs(UnmanagedType.LPStr)] public string lpData to work either. I've only gotten it to work by doing the marshalling myself:
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT : IDisposable
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
/// <summary>
/// Only dispose COPYDATASTRUCT if you were the one who allocated it
/// </summary>
public void Dispose()
{
if (lpData != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(lpData);
lpData = IntPtr.Zero;
cbData = 0;
}
}
public string AsAnsiString { get { return Marshal.PtrToStringAnsi(lpData, cbData); } }
public string AsUnicodeString { get { return Marshal.PtrToStringUni(lpData); } }
public static COPYDATASTRUCT CreateForString(int dwData, string value, bool Unicode = false)
{
var result = new COPYDATASTRUCT();
result.dwData = (IntPtr)dwData;
result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value);
result.cbData = value.Length + 1;
return result;
}
}