3

EDIT: I GOT IT FIXED HERE'S MY WORKING FULL CODES TO SET EXAMPLE TO NEW FRIENDS and my original question is below too.

before the codes let me introduce you to some docs (in order):

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

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

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

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

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

C# program

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++ program


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

ORGINIAL QUESTION:

I have a C# application that i want to communicate with a c++ process that i create within my C# app.

I have a code in my hand that is supposed to work i suppose but it doesn't. The message simply is not gotten by c++ app.

my C# program:

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

        }

    }
}

my c++ app (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;
}

when i start the c# program. no errors, no nothing. same with c++, no errors, no nothing but never break the for loop :(

thanks for everyone for their time.

Zeki Kral
  • 35
  • 2
  • 10
  • `hWnd = myProcess.Handle` you think this is window ? – RbMm Feb 01 '21 at 00:36
  • i thoought so. If its not, can i get the window handle by using myProcess? btw does my c++ app has a window? i mean it pops up a command prompt but is it considered as window? edit: i tried chaing it to hWnd = myProcess.MainWindowHandle; but didn't work. thank you – Zeki Kral Feb 01 '21 at 00:41
  • do i have to create a windows in my c++ to achieve IPC (PostMessage/PeekMessage)? – Zeki Kral Feb 01 '21 at 00:46
  • Does your C++ program actually have a window, I don't see you create one. Does `MainWindowHandle` actually have a value? `public uint dwData;` should be `ulong` as it's defined as `ULONG_PTR` – Charlieface Feb 01 '21 at 01:24
  • I posted the whole programs. if command prompt counts as a window, it does have a window but if it doesn't then it doesn't have a window. do i need to create a window to use postmessage/send message explicitly? does command promp count as a window? thank you everyone – Zeki Kral Feb 01 '21 at 02:18
  • 1
    Don't use `PostMessage` with `WM_COPYDATA`. See for example [Why can't I PostMessage the WM_COPYDATA message, but I can SendMessageTimeout it with a tiny timeout?](https://devblogs.microsoft.com/oldnewthing/20110916-00/?p=9623). – dxiv Feb 01 '21 at 05:30
  • Please see [Message Routing](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#message-routing) to get familiar with the fundamentals. – IInspectable Feb 01 '21 at 09:17

1 Answers1

1

First of all, Process.Handle is the handle of the process instead of the window.

Secondly, since your main.exe is a console application and it only has a console window, you can only get the handle of the console window by using MainWindowHandle. However, the console does not belong to the main.exe, So you cannot use PeekMessage to handle the message sent to the console window. Console windows are owned by the console subsystem, csrss.exe(see https://stackoverflow.com/a/28248281/10611792). You should create your own window for your C++ app, or create a Message-Only window. Then, you can use FindWindow to get the window handle in 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;
}

Also Note that use SendMessage instead of PostMessage.

In addition, you can also choose other IPC methods.

Drake Wu
  • 6,927
  • 1
  • 7
  • 30