使用SendMessage的C#,WM_COPYDATA的问题

我已经花了几天(或更多)试图让这个工作。

手头的应用程序是FTPRush ,我知道有一个名为rush_cmdline.exe的cmd行应用程序,它使用SendMessageFTPRush发送请求。

从调试rush_cmdline.exe我可以看到lParamwParamMessagehWnd

我的代码如下(使用SendMessage,而不是SendMessageW):

 [DllImport("User32.dll", EntryPoint = "FindWindow")] public static extern Int32 FindWindow(String lpClassName, String lpWindowName); [DllImport("USER32.DLL", EntryPoint= "SendMessage")] public static extern IntPtr SendMessage(int hWnd, int Msg, int wParam, IntPtr lParam); 

我还尝试了另一个规范:

 [DllImport("User32.dll", EntryPoint = "SendMessage")] public static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam); 

句柄( hWnd )不是问题,因为这有效:

 int ftprush = FindWindow("TfmRush", null); ShowWindow(ftprush, 8); 

哪个(我没有粘贴dllimport,因为它在这里不重要。如果你想看到它,请告诉我)将窗口带到前面。 另外,我通过调试rush_cmdline.exe进行了检查。 所以手柄是一样的。

两次尝试都失败了(默默地):

 public const Int32 WM_COPYDATA = 0x4A; string msg = "RushApp.FTP.Login('backup','',0); "; // 1 byte[] array = Encoding.UTF8.GetBytes((string)msg); int size = Marshal.SizeOf(array[0]) * array.Length + Marshal.SizeOf(array[0]); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(array, 0, ptr, array.Length); Marshal.WriteByte(ptr, size - 1, 0); SendMessage(ftprush, WM_COPYDATA, 0, ptr); // 2 public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } COPYDATASTRUCT cds; cds.dwData = (IntPtr)100; cds.lpData = msg; cds.cbData = sarr.Length + 1; SendMessage(ftprush, WM_COPYDATA, 0, ref cds); 

我希望至少第二个解决方案能够正常工作,因为它与此匹配得非常好: perl示例

任何启蒙都非常感激!

谢谢,

  • 坦率

更新:

 string msg = "RushApp.FTP.Login('backup','',0);\0"; var cds = new COPYDATASTRUCT { dwData = new IntPtr(3), cbData = msg.Length + 1, lpData = msg }; IntPtr ftprush = FindWindow("TfmRush", null); SendMessage(ftprush, WM_COPYDATA, IntPtr.Zero, ref cds); 

我的定义有

 [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); 

当然,确保str为空终止“\ 0”

或者, PInvoke.NET给出的定义是

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

COPYDATASTRUCT中的参数顺序非常重要,Bob Vale的答案是错误的顺序。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx它应该按此顺序:

 [StructLayout(LayoutKind.Sequential)] public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; public IntPtr lpData; } 

我还没有得到MarshalAs(UnmanagedType.LPStr)] public string lpData 。 我只是通过自己编组来完成工作:

 [StructLayout(LayoutKind.Sequential)] public struct COPYDATASTRUCT : IDisposable { public IntPtr dwData; public int cbData; public IntPtr lpData; ///  /// Only dispose COPYDATASTRUCT if you were the one who allocated it ///  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; } } 

在上面的两个答案之间,我拼凑了一个工作的例子。 Bryce Wagner的类工作,所以我添加了一个方法来使用SendMessageTimeout来发送数据。 它是一个静态方法,所以你只需要调用它来发送数据。 这不是我的工作,只是粘合在一起并分享回来。

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

要使用它:

 CopyData.Send(targetHandle, 1234, "This is a test"); 

这使用默认的1秒超时。