C# using SendMessage, problem with WM_COPYDATA

前端 未结 3 2025
甜味超标
甜味超标 2020-12-18 04:15

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

相关标签:
3条回答
  • 2020-12-18 05:08

    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.
    
    0 讨论(0)
  • 2020-12-18 05:08

    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.

    0 讨论(0)
  • 2020-12-18 05:11

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题