0

I am trying to use an api library with functions that send messages to a server, then receive back messages. One of the function's arguments is a HWND and the library document says the message will be received by it. To read the received message I studied a few MFC documents, created a class that inherits CDialog with a function to handle the message and tried to work with the message map.

But these efforts seemed like a bit too much trouble when I don't want to create a working dialog window, but just want the message itself so I can make it appear in a console or use it in other parts of my code. So is there any way to simply 'extract' the message from HWND and not worry about MFC? If not, what is the simplest way?


More on the API doc

Class Wrapper wraps the dll library file into member functions, I'm trying to use the function BOOL Wrapper::Func1(HWND hWnd, DWORD msg, const char* inputStr)

Class MyDlg inherits CDialog and has Wrapper m_wrp as its class member.

LRESULT MyDlg::HandleEvent(WPARAM w, LPARAM l) is a member function that prints the recieved data and returns TRUE

There's this code in the middle of the cpp file where member functions of MyDlg are defined. It seems that whatever inputStr is sent to the server by Wrapper::Func1, the same message CA_01 is received. CA_01 is a const DWORD defined in another header file. After some searching I believe this is the part that continuously checks for messages and if MSG with msg = CA_01 is received, calls HandleEvent.:

BEGIN_MESSAGE_MAP(MyDlg, CDialog)
    ON_MESSAGE(CA_01, HandleEvent)
END_MESSAGE_MAP()

There is a button MyDlg creates, and when it is pressed, input text is read, void MyDlg::OnSend() is called and m_wrp.Func1(...) is called.

void MyDlg::OnSend(){
    CString strData;
    m_editData.GetWindowText(strData);
    m_wrp.Func1(GetSafeHwnd(), CA_01, strData);
}

I have tested this sample code from the api document, and it works fine. A window with a editable text box and a button appears, I enter some text, press the button, and after a few seconds the received message is shown.

However, when I create a Wrapper instance, and inside a while loop, try to call Func1 and recieve the message using PeekMessage, nothing happens:

HWND hWnd = CreateWindowW(L"static", L"", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
MSG msg;
Wrapper m_wrp;
CString inputStr = "test";

while (true){
    m_wrp.Func1(hWnd, CA_01, inputStr);
    if (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) {
        std::cout << "Got message: " << msg.message << std::endl;
    }
    else {
        std::cout << "No messages, sleep" << std::endl;
        Sleep(2000);
    }
}

Is this because of a difference between ON_MESSAGE(...) and PeekMessage(...)?

Hpotato
  • 3
  • 5
  • [Message-only windows](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows). – IInspectable Aug 20 '21 at 16:57

1 Answers1

0

You sure don't need MFC for that. Not even a "Windows" app - a simple console app will work, just keep pumping messages:

#include <iostream>
#include <windows.h>

int main() {
    HWND hWnd = CreateWindowW(L"static", L"", 0,
        0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);

    MSG msg;
    while (true) {
        if(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) {
            std::cout << "Got message: " << msg.message << std::endl;
        }
        else {
            std::cout << "No messages, posting '7' and sleep" << std::endl;
            PostMessage(hWnd, 7, 0, 0);
            Sleep(2'000);
        }
    }
}

UPDATE:

The code above works only for posted messages. To also process sent messages, this minimal sample will work:

#include <iostream>
#include <windows.h>

const DWORD CA_01 = WM_USER + 1;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == CA_01)
        std::cout << "Got message: " << message << std::endl;
    return DefWindowProc(hWnd, message, wParam, lParam);
}

ATOM MyRegisterClass()
{
    WNDCLASSEXW wcex{ sizeof(WNDCLASSEX) };
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = ::GetModuleHandle(NULL);
    wcex.lpszClassName = L"x";
    return RegisterClassExW(&wcex);
}

int main() {
    HWND hWnd = CreateWindowW((LPCWSTR)MyRegisterClass(), L"", 0,
        0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);

    std::cout << "Sending `CA_01`" << std::endl;
    LRESULT lr = SendMessageW(hWnd, CA_01, 0, 0);
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        DispatchMessage(&msg);
    }
}

May be that library on your app to pump windows messages? Try to add a in instead of the while loop (see modified code above)

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
  • Thank you for your answer! I tried applying it but for some reason keep getting infinite "No messages". To find out why this is happening, I studied the working api sample code along with your answer but failed to find anything wrong. It feels like something about the difference b/w PeekMessage and ON_MESSAGE macro is the problem, but can't be sure. I added some more details in my question. Would you care to tell me what I'm doing wrong? – Hpotato Aug 22 '21 at 10:24
  • @Hpotato could you show your PeekMessage() loop? – Vlad Feinstein Aug 22 '21 at 15:22
  • I added it in my question. It's almost exactly same as your answer. – Hpotato Aug 22 '21 at 17:07
  • I also tried not using `Sleep()` , and using `GetMessage` instead of `PeekMessage` – Hpotato Aug 22 '21 at 17:14
  • Is your `m_wrp` created the same way in the working MFC app? Are you linking to that library the same way? Does it need to be initialized somehow? Does `Func1` returns something from that library (like success/failure)? Can you use `Spy++` to see if a message `CA_01` is ever sent? – Vlad Feinstein Aug 22 '21 at 17:21
  • `m_wrp` is a class member of MyDlg and is created when MyDlg is created. No initialization is needed for the Wrapper class, everything is in the constructor and no arguments are needed. The library linking all happens in the Wrapper class and I was able to use the debugging tools to see that the correct member function linking to the library was called both in my code and in the working app. Func1 itself as far as it's documented doesn't return anything and even if it does, it's not used in the working app. Success/Failure is delivered through WPARAM in MSG. I'll have to try using Spy++ now. – Hpotato Aug 22 '21 at 17:41
  • I made the console print hWnd at the very beginning and used Spy++ on it to see whether any messages were being sent or received. No messages were being sent nor recieved. The messages window stayed blank. I'm baffled. – Hpotato Aug 22 '21 at 19:45
  • Wait, re: `m_wrp is a class member of MyDlg and is created when MyDlg is created.` - are you still using an MFC Dialog-based app? I thought you didn't want to use that. You shouldn't have two message pumps in one app. Could you try the same in the simple console app from my answer? – Vlad Feinstein Aug 22 '21 at 20:41
  • Oh no that was an answer for your question "Is your m_wrp created the same way in the working MFC app?". So that's how `m_wrp` is created in the working MFC app from the api doc. When using the simple console app you wrote I just created a `Wrapper` instance by doing `Wrapper m_wrp`. By "the same", you mean I should create the class `MyDlg`, without making it inherit `CDialog` or any window class, right? – Hpotato Aug 22 '21 at 20:55
  • @Hpotato Re: `What do you mean by "the same"?` That, what you've just answered :) Could you move `m_wrp.Func1(...)` out of the loop? Also what do you mean by `The messages window stayed blank`? That window is not visible... – Vlad Feinstein Aug 22 '21 at 21:00
  • 1
    Okay I'll try. Thank you really for all this help. Oh, by that I meant the interface of Spy++ that shows all the messages of the chosen window, not the window itself. – Hpotato Aug 22 '21 at 21:05
  • I did so, but still no messages(https://www.codepile.net/pile/Woq4KP9x). In the working app, `Func1` is called inside a protected function `CallFunc1` of `MyDlg` and `CallFunc1` is linked to a button. Should this matter? `CallFunc1` is declared as `afx_msg void CallFunc1();` in `MyDlg`. – Hpotato Aug 22 '21 at 21:33
  • @Hpotato - no, that shouldn't matter. I might have taken too big of a shortcut. My code also won't work if you replace my `PostMessage` with the `SendMessage` (as your library likely does). The difference is that `PostMassage` places a message into a message queue of the targst window, and it can be picked up by `PeekMessage`. `On another hand, `SendMessage` calls a window procedure associated with that HWND directly, and since I was using canned class `static`, the message is ignored. So you DO need a proper WindowProc to work with that library, but it doesn't need to be MFC – Vlad Feinstein Aug 22 '21 at 21:39
  • Ah. I guess I have to try some more different ways. Thank you really for your help. So I shoud be creating a class and a CALLBACK WindowProc to receive the message? – Hpotato Aug 22 '21 at 21:50
  • @Hpotato yes, but it's also not too bad. Please see update in my answer above. – Vlad Feinstein Aug 22 '21 at 22:09
  • Oh, that looks pretty simple. But still I cannot see the messages being sent nor recieved:( I can only see those messages that appear when I first create hWnd. The library is a dll file and I used GetProcAddress to load the __stdcall functions from it.. maybe there's something wrong here? The working mfc app uses the same Wrapper class though. Could there be something about the way they created the .dll such that it needs MFC to work? – Hpotato Aug 23 '21 at 11:38
  • @Hpotato one *could* create an “MFC extension DLL” that requires the exe to also be MFC, but why? Is the doc saying something? Is it in public domain? Can you share a link? Do you use LoadLibrary? Does it succeed? – Vlad Feinstein Aug 23 '21 at 14:36
  • @Hpotato see my updated code, I hope it will get your messages pumped :) – Vlad Feinstein Aug 23 '21 at 23:03
  • Yes, that worked! Thank you so much for your patience and everything! :D – Hpotato Aug 24 '21 at 06:47