【问题标题】:How to send message from C# to C++ (MESSAGE ONLY WINDOW) using SendMessage (WM_COPYDATA)?如何使用 SendMessage (WM_COPYDATA) 将消息从 C# 发送到 C++ (MESSAGE ONLY WINDOW)?
【发布时间】:2021-05-05 05:08:50
【问题描述】:

编辑:我把它修好了,这是我的工作完整代码,可以为新朋友树立榜样,我原来的问题也在下面。

在代码之前,让我向您介绍一些文档(按顺序):

https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#message-routing

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage

https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows

https://docs.microsoft.com/en-us/windows/win32/dataxchg/using-data-copy

http://pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html

C#程序

using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Program
{

    public partial class Form1 : Form
    {

        [DllImport("user32.dll")]
        static extern long SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr FindWindow(string classname, string windowname);


        [StructLayout(LayoutKind.Sequential)]
        struct COPYDATASTRUCT
        {
            public uint dwData;
            public int cbData;
            public IntPtr lpData;
        }
        public static IntPtr IntPtrAlloc<T>(T param)
        {
            IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
            Marshal.StructureToPtr(param, retval, false);
            return (retval);
        }

        public static void IntPtrFree(IntPtr preAllocated)
        {
            if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
            Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
        }

        const int WM_COPYDATA = 0x004A;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread.Sleep(3000);

            string message = "This is a test";
            IntPtr hWnd = FindWindow("MyClass", "MyTitle");
            if (hWnd == IntPtr.Zero)
            {
                MessageBox.Show("couldn't find the window");
            }
            else
            {
                COPYDATASTRUCT cds;
                cds.dwData = 1;
                cds.cbData = message.Length + 1;
                cds.lpData = Marshal.StringToHGlobalAnsi(message);
                IntPtr cdsBuffer = IntPtrAlloc(cds);
                SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
                IntPtrFree(cds.lpData);
                IntPtrFree(cdsBuffer);
            }

        }

    }
}

C++ 程序


#include <iostream>
#include <Windows.h>
using namespace std;


LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Msg == WM_COPYDATA)
    {
        PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
        MessageBoxA(hWnd, (LPSTR)data->lpData, "info", 0); // The character set depends on the characters you send
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
}


int main(){
    
    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.lpfnWndProc = WindProc;
    wcx.hInstance = GetModuleHandle(NULL);
    wcx.lpszClassName = TEXT("MyClass");
    RegisterClassEx(&wcx);
    HWND hWnd = CreateWindowEx(0, TEXT("MyClass"), TEXT("MyTitle"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);


    MSG message;
    for(int i = 0; i < 1000; i++)
    {
        std::cout << "working" << std::endl;
        Sleep(2 * 1000);
        if(PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE))
        {
            break;
        }
    }

    int x;
    cout<<"got it!";
    cin>>x;
    return 0;
}

原始问题:

我有一个 C# 应用程序,我想与我在 C# 应用程序中创建的 c++ 进程进行通信。

我手里有一个我想应该可以工作的代码,但它没有。该消息根本不是由 c++ 应用程序获取的。

我的 C# 程序:

namespace ScannerGUI
{

    public partial class Form1 : Form
    {
        
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);


        [StructLayout(LayoutKind.Sequential)]
        struct COPYDATASTRUCT
        {
            public uint dwData;
            public int cbData;
            public IntPtr lpData;
        }
        public static IntPtr IntPtrAlloc<T>(T param)
        {
            IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
            Marshal.StructureToPtr(param, retval, false);
            return (retval);
        }

        public static void IntPtrFree(IntPtr preAllocated)
        {
            if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
            Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero;
        }

        const int WM_COPYDATA = 0x004A;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //creating child process

            var prcsInfo = new ProcessStartInfo
            {
                UseShellExecute=true,
                CreateNoWindow = false,
                FileName = "main.exe",
            };
            
            Process myProcess = Process.Start(prcsInfo);

            ChildProcessTracker.AddProcess(myProcess);

            Thread.Sleep(3000);

            string message = "This is a test";
            IntPtr hWnd = myProcess.Handle;
            if (hWnd == IntPtr.Zero)
            {
                MessageBox.Show("couldn't find the process");
            }
            else
            {
                COPYDATASTRUCT cds;
                cds.dwData = 1;
                cds.cbData = message.Length + 1;
                cds.lpData = Marshal.StringToHGlobalAnsi(message);
                IntPtr cdsBuffer = IntPtrAlloc(cds);
                PostMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
                IntPtrFree(cds.lpData);
                IntPtrFree(cdsBuffer);
            }

        }

    }
}

我的 c++ 应用程序(main.exe):

int main(){
    

    MSG message;
    for(int i = 0; i < 1000; i++)
    {
        std::cout << "working" << std::endl;
        Sleep(2 * 1000);
        if(PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE))
        {
            break;
        }
    }

    int x;
    cout<<"got it!";
    cin>>x;
    return 0;
}

当我启动 c# 程序时。没有错误,没有什么。 与 c++ 相同,没有错误,没有任何东西,但永远不会破坏 for 循环:(

感谢大家的宝贵时间。

【问题讨论】:

  • hWnd = myProcess.Handle你认为这是窗口?
  • 我是这么认为的。如果不是,我可以使用 myProcess 获取窗口句柄吗?顺便说一句,我的 C++ 应用程序有窗口吗?我的意思是它会弹出一个命令提示符,但它被视为窗口吗?编辑:我尝试将其链接到 hWnd = myProcess.MainWindowHandle;但没有用。谢谢
  • 我是否必须在我的 c++ 中创建一个窗口才能实现 IPC (PostMessage/PeekMessage)?
  • 你的 C++ 程序是否真的有一个窗口,我看你没有创建一个。 MainWindowHandle 真的有价值吗? public uint dwData; 应该是 ulong,因为它被定义为 ULONG_PTR
  • 不要将PostMessageWM_COPYDATA 一起使用。参见例如Why can't I PostMessage the WM_COPYDATA message, but I can SendMessageTimeout it with a tiny timeout?

标签: c# c++ winapi ipc sendmessage


【解决方案1】:

首先Process.Handle是进程的句柄,而不是窗口。

其次,由于你的main.exe是一个控制台应用程序,它只有一个控制台窗口,你只能通过MainWindowHandle获取控制台窗口的句柄。但是控制台不属于main.exe,所以不能使用PeekMessage来处理发送到控制台窗口的消息。 控制台窗口归控制台子系统 csrss.exe 拥有(请参阅 https://stackoverflow.com/a/28248281/10611792)。您应该为您的 C++ 应用程序创建自己的窗口,或者创建一个Message-Only 窗口。然后,你可以使用FindWindow在C#中获取窗口句柄:

public partial class Form1 : Form
{
    [DllImport("user32.dll")]
    static extern long SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll",CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindow(string classname, string windowname);
    ...
    private void Form1_Load(object sender, EventArgs e)
    {
        ...
        Thread.Sleep(3000);

        string message = "This is a test";
        IntPtr hWnd = FindWindow("MyClass", "MyTitle");
        if (hWnd == IntPtr.Zero)
        {
            MessageBox.Show("couldn't find the process");
        }
        else
        {
            COPYDATASTRUCT cds;
            cds.dwData = 1;
            cds.cbData = message.Length + 1;
            cds.lpData = Marshal.StringToHGlobalAnsi(message);
            IntPtr cdsBuffer = IntPtrAlloc(cds);
            SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, cdsBuffer);
            IntPtrFree(cds.lpData);
            IntPtrFree(cdsBuffer);
        }

    }
}

C++:

LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Msg == WM_COPYDATA)
    {
        PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
        MessageBoxA(hWnd, (LPSTR)data->lpData, "info", 0); // The character set depends on the characters you send
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
}
int main() {

    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.lpfnWndProc = WindProc;
    wcx.hInstance = GetModuleHandle(NULL);
    wcx.lpszClassName = TEXT("MyClass");
    RegisterClassEx(&wcx);
    HWND hWnd = CreateWindowEx(0, TEXT("MyClass"), TEXT("MyTitle"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);

    MSG msg;
    for (int i = 0; i < 1000; i++)
    {
        std::cout << "working" << std::endl;
        Sleep(2 * 1000);
        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            break;
        }
    }

    int x;
    cout << "got it!";
    cin >> x;
    return 0;
}

还要注意使用SendMessage 而不是PostMessage

此外,您还可以选择其他IPC方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 2012-12-07
    • 1970-01-01
    • 2015-05-26
    • 2010-12-16
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多