C# 使用 SendMessage,WM_COPYDATA 问题

发布于 2024-11-25 09:10:31 字数 2313 浏览 0 评论 0原文

我花了几天(或更长时间)试图让它发挥作用。

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

通过调试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);

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 called rush_cmdline.exe which uses SendMessage to send requests to FTPRush.

From debugging the rush_cmdline.exe I can see lParam, wParam, Message and hWnd.

My code is as follows (using SendMessage, not 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);

And I've tried a another specification also:

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

The handle (hWnd) is not the problem, as this works:

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

Which (I didn't paste the dllimport as it's not important here. Let me know if you wish to see it) brings the window to front. Also, I checked by debugging rush_cmdline.exe. So the handle is the same.

Two attempts which both fail (silently):

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

I would expect at least the 2nd solution to work, as it matches up pretty well with this: perl example

Any enlightenment is GREATLY appreciated!

Thanks,

  • Frank

UPDATE:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

药祭#氼 2024-12-02 09:10:31

我的定义

[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 以 null 结尾“\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.

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.
_失温 2024-12-02 09:10:31

在上面的两个答案之间,我拼凑了一个工作示例。 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 秒超时。

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.

远昼 2024-12-02 09:10:31

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;

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

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文