使用SendMessage的C#,WM_COPYDATA的问题
我已经花了几天(或更多)试图让这个工作。
手头的应用程序是FTPRush
,我知道有一个名为rush_cmdline.exe
的cmd行应用程序,它使用SendMessage
向FTPRush
发送请求。
从调试rush_cmdline.exe
我可以看到lParam
, wParam
, Message
和hWnd
。
我的代码如下(使用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秒超时。