【问题标题】:Sending a Custom windows message...custom data marshalling发送自定义窗口消息...自定义数据编组
【发布时间】:2013-04-24 19:30:32
【问题描述】:

我有一个需要与另一个应用程序交互的 WPF 应用程序。

此应用程序有大约 20 条自定义 Windows 消息 (WM_USER+50...WM_USER+70)。

我正在努力完成的总结:

WPF 应用程序 -> SendMessage -> 第三方应用程序

我遇到的问题是所有消息都是自定义消息。因此,我必须实现自己的数据封送处理。

见http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx

看来我需要经过的流程是:

  1. 抓取进程并将其打开以供所有访问。 User32.GetWindowThreadProcessId(windowHandle, out pId);

    // 以所有访问权限打开进程
    someprocess = OpenProcess((0x1F0FFF), false, (int)pId);

  2. 在进程中分配一个缓冲区: IntPtr 缓冲区 = VirtualAllocEx(hProcess, IntPtr.Zero, 1024, 0x1000, 0x04);

  3. 填充某种结构,将写入 #2 中创建的缓冲区?

  4. 将#3 复制到远程缓冲区是#2? WriteProcessMemory??

  5. 发送自定义消息(SendMessage(windowhandle, customMsg, 0, buffer from #2?)

  6. 将结构从远程进程缓冲区读回本地缓冲区

  7. 将此数据编组为托管类型。 (这是一个 C# .Net 应用程序)

我真的可以使用一些洞察力。到目前为止,我还没有多少运气。我认为我最坚持的部分是发送到 WriteProcessMemory 的结构类型是什么?

【问题讨论】:

  • 你可以使用WM_COPYDATA,让它为你做数据封送处理。
  • 如果您正在做的事情之前已经完成了无数次(两个进程正在通信)并且您发现自己正在使用调试功能(OpenProcess、WriteProcessMemory 等),那么这是一个非常明显的迹象,表明您'做错了。 Windows has many ways of doing Interprocess Communication.
  • 我理解让两个进程进行通信,但我理解在处理 CUSTOM 消息时必须自定义数据编组是错误的吗?这些都是 WM_USER+X 消息。
  • 您是对的,您不能使用 WM_USER 消息发送自定义数据。所以你需要使用一些其他的机制来发送数据。现在您正在使用其他机制发送数据,您需要自定义消息做什么?自定义消息是一个糟糕的选择。由于您已经在使用消息,WM_COPYDATA 将是一个不错的选择,正如其他人所指出的那样。
  • 感谢您的回复。虽然现在正在使用 ReadProcessMemory 和 WriteProcessMemory。我将研究 wm_copydata。但是,我担心我仍然需要能够写入和写入数据。自定义消息传递也不是我的选择。无论好坏,我都坚持下去。需要让它工作,因为它是我可用于集成的。感谢您的回复。在我调查 wm_copydata 后将选择一个答案

标签: c# .net winapi marshalling sendmessage


【解决方案1】:

WM_COPYDATA 绝对是最简单的方法。 WM_COPYDATA 允许您将两个不同的数据项发送到另一个进程——一个 DWORD 值和一个任意大小的数据块。因此,对于您的实现,您可能会执行以下操作:

COPYDATASTRUCT cds;
cds.dwData = WM_USER + 50; // the "message" you want to send
cds.cbData = sizeof(MyDataForMessage50); // the size of the chunk of data
cds.lpData = lpMessage50Data; // a pointer to the chunk of data
SendMessage(hwndTarget, WM_COPYDATA, reinterpret_cast<WPARAM>(hwndSender),
            reinterpret_cast<LPARAM>(&cds));

注意hwndTarget是其他进程中的目标窗口,hwndSender是发送进程中的窗口。目标窗口接收相同的参数,因此可以使用wParam 了解谁发送了消息,因此可以在需要时发送回复。

在接收端的WndProc中:

if (uMsg == WM_COPYDATA)
{
    HWND hwndSender = reinterpret_cast<HWND>(wParam);
    LPCOPYDATASTRUCT pcds = reinterpret_cast<LPCOPYDATASTRUCT>(lParam);
    DWORD dwCustomMsg = pcds->dwData;
    LPVOID pCustomData = pcds->lpData;
    DWORD dwCustomDataSize = pcds->cbData;

    // do something with the custom message

    // return TRUE to indicate message received
    return TRUE;
}

还要注意WM_COPYDATA 文档中的重要说明:

接收应用程序应将数据视为只读。这 lParam 参数仅在消息处理过程中有效。 接收应用程序不应释放所引用的内存 l 参数。如果接收应用程序必须在之后访问数据 SendMessage 返回,它必须将数据复制到本地缓冲区中

【讨论】:

  • 关于 wm_copydata 的问题...我无法访问目标代码。这是第三方集成。他们不需要一些处理程序来处理传入的 wm_copydata 消息吗?我可以请求对目标进行零更改
  • 正如@tronious 所说,其他代码无法修改。另外,WM_COPYDATA 没有什么特别之处,它很方便,但它只是完成问题所要求的编组,仅此而已。
  • @user34660 那么为什么不写一个答案呢?比讽刺评论更有用。
  • @user34660 没有错,只是它不准确。 OP没有说不能修改代码。 cmets(四年前)的共识是 WM_COPYDATA 是解决方案。 OP 说的最后一件事是他们将对其进行调查。然后 4 年后,您弹出没有提供任何新信息。如果您认为您有一些有用的内容要添加,请写下答案。
  • @user34660 我没有说这个问题不清楚,我说你的评论不准确。无论如何,期待阅读您的替代答案。
【解决方案2】:

发送消息的示例代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
    class Program
    {
        public const int WM_COPYDATA = 0x4A;
        public const UInt32 WM_COMMAND = 0x0111;
        public const UInt32 IDM_MENU_SECUREDISCONNECT = 305;
        public const UInt32 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpData;
        }
        [DllImport("User32.dll", EntryPoint = "SendMessage", SetLastError = true)]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
        private const string MARKER = "MARKER\n";
        static void Main(string[] args)
        {
            IntPtr destWnd = (IntPtr)int.Parse(args[0]);
          string packedargs = DAZZLE_MARKER + String.Join("\n", args[1]);
            /
            byte[] sarr = System.Text.Encoding.Unicode.GetBytes(packedargs);
            int len = sarr.Length;
            COPYDATASTRUCT CopyDataStruct;
            CopyDataStruct.dwData = (IntPtr)100;
            CopyDataStruct.cbData = (len + 1) * 2;
            CopyDataStruct.lpData = packedargs;
            int result = SendMessage(destWnd, WM_COPYDATA, 0, ref CopyDataStruct);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-30
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-03
    相关资源
    最近更新 更多