-2

here is my problem. I'm trying to create a windowless program that still uses trayIcon and Hooks, so I need to use messages (or not?) but when I use them, I don't know how to free my memory when I kill my process. Even classes's destructors aren't called. Here is a test main:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR pCmdLine, int nCmdShow)
{
    MSG msg;

    OutputDebugStringW(L"Start.\n");
    while (GetMessageW(&msg, NULL, NULL, NULL) > 0)
    {
        OutputDebugStringW(L"one.\n");
        TranslateMessage(&msg);
        OutputDebugStringW(L"two.\n");
        DispatchMessage(&msg);
        OutputDebugStringW(L"three.\n");
    }
    OutputDebugStringW(L"end.\n");
    return 0;
}

As I saw in the doc, the GetMessage() call should return 0 or less to exit, but when I run it, and then shut it down, I get only the "Start." log, and I don't understand why. And if there is no way to get through this loop, then how can i call my destructors? I do have a window for my trayIcon that doesn't receive any message, maybe i should pass it as parameter to GetMessage()?

PS: my project uses trayIcons and Hooks, and when I run it with the same debug prints, it display once the 4 first strings, but nothing at shutdown, not even the "end." string.

EDIT: my (terrible) trayIcon creation:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    OutputDebugStringW(L"Message!\n");
    switch (uMsg)
    {
    case WM_DESTROY:
        OutputDebugStringW(L"Close message received\n");

        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

        EndPaint(hwnd, &ps);
    }
    case WM_RBUTTONUP:
    {
        OutputDebugStringW(L"Trying to open Context menu\n");
        POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
        break;
    }
    return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

bool    CreateNotifyIcon(HINSTANCE& hInstance)
{
    NOTIFYICONDATA  notif = {};
    static const wchar_t class_name[] = L"ExtendClass";
    WNDCLASSEX wx = {};
    wx.cbSize = sizeof(WNDCLASSEX);
    wx.lpfnWndProc = WindowProc;
    wx.hInstance = hInstance;
    wx.lpszClassName = class_name;

    RegisterClassEx(&wx);
    HWND win = CreateWindowEx(0, class_name, L"Windows Extend", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    if (win == NULL)
        OutputDebugStringW(L"failed to create window\n");
    ZeroMemory(&notif, sizeof(NOTIFYICONDATA));
    notif.cbSize = sizeof(NOTIFYICONDATA);
    notif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    // Need to find a non-busy uID
    notif.uID = 44421;
    notif.hWnd = win;
    StringCchCopy(notif.szTip, ARRAYSIZE(notif.szTip), L"Access Windows Extend options.");
    StringCchCopy(notif.szInfo, ARRAYSIZE(notif.szInfo), L"Access Windows Extend options.");
    StringCchCopy(notif.szInfoTitle, ARRAYSIZE(notif.szInfoTitle), L"Windows Extend");
    notif.hIcon = (HICON)LoadImage(NULL, L"./Luma.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);

    if (!Shell_NotifyIcon(NIM_ADD, &notif))
    {
        OutputDebugStringW(L"failed to create notifIcon\n");
        return false;
    }
    return true;
}
bachinblack
  • 150
  • 12
  • 1
    There aren't any d'tors. Which d'tor would you expect to run in your smoke code? – IInspectable Oct 26 '17 at 22:56
  • 1
    Did you try checking `GetMessage`’s return value for non-zero (and not “greater than zero”)? I think that “non-zero” that is specified in the docs can imply not only positive values. – ivanmoskalev Oct 26 '17 at 22:56
  • Also, how do you shut the app down? – ivanmoskalev Oct 26 '17 at 23:00
  • 1
    *my project uses trayIcons* - I guess when user click try icon you popup some menu or window. add to menu item *Exit* or button *Exit* to window. and when user press it - in handle call `PostQuitMessage`. as result you `GetMessage` loop is finished – RbMm Oct 26 '17 at 23:21
  • Since i have no window, the only ways to shut down the app are a key combination (with which i can free my data and exit) and the visual studio stop button (certainly a kind of SIGINT). In this test main, GetMessage never returns – bachinblack Oct 26 '17 at 23:22
  • @RbMm You are pointing out my second problem: My trayIcon window never gets "messages like WM_RBUTTONUP" or WM_CONTEXTMENU" so i can't use context menu. But maybe I shouldve asked for this instead of GetMessage. – bachinblack Oct 26 '17 at 23:25
  • @bachinblack - try icon window of course got `WM_RBUTTONUP` or `WM_CONTEXTMENU` messages. if you not got it say only that you have some error in implementation – RbMm Oct 26 '17 at 23:26
  • 1
    [_Because the system directs messages to individual windows in an application, a thread must create at least one window before starting its message loop._](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644928(v=vs.85).aspx) – zett42 Oct 26 '17 at 23:27
  • @zett42 - formal exist `PostThreadMessage` - so some time possible have message loop without windows. but if OP want try icon he of course need window which will be receive messages for this try icon – RbMm Oct 26 '17 at 23:30
  • I have a window, and it receives some messages at its creation, but when I try to left click or right click on it, It reveives no messages. Anyway, maybe i asked too soon, I will search for more examples and find the error in my code. Now at least, I understood why does my GetMessage loop never ends, even if it was obvious – bachinblack Oct 26 '17 at 23:37
  • @bachinblack: if you are not receiving meessages for your tray icon, then you are not setting it up correctly. Please show your actual code for that. The code you have shown doesn't do anything with the system tray – Remy Lebeau Oct 26 '17 at 23:38
  • @bachinblack: you are enabling the `NIF_MESSAGE` flag when calling `Shell_NotifyIcon(NIM_ADD)`, but you are not assigning any value to the `notif.uCallbackMessage` field, so it is 0 (`WM_NULL`). You need to specify a specific window message that you want the icon to send to your window, and then you need to handle that message in your `WindowProc`. And if you [read the documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159.aspx), you should also call `Shell_NotifyIcon(NIM_SETVERSION)` to specify how mouse and keyboard actions on the icon are reported to your window. – Remy Lebeau Oct 26 '17 at 23:56
  • @bachinblack: also, your `WindowProc` is missing a `break` in the `WM_PAINT` handler, and there is an erroneous `return 0` statement after the `WM_RBUTTONUP` handler. – Remy Lebeau Oct 26 '17 at 23:57
  • @Remy Lebeau Oh, you are totally right, thank you so much for your answer, I will fix it right now and I will also fix my switch. I already accepted your answer, but these comments helped even more, thank you. – bachinblack Oct 27 '17 at 00:02

2 Answers2

4

GetMessage() does not return until it receives a window/thread message, or has a message to synthesis. In the code you have shown, you are not creating any windows, not posting any thread messages to yourself, and not creating any hooks that require a message loop, so there is nothing for GetMessage() to do. It is blocked indefinitely. That is why you are only seeing your start message and nothing else happens until you forcibly kill the program.

Since you intend to display a system tray icon, you need a window for it, even if just a hidden window, in order to receive notifications about user interaction with the icon. Your GetMessage() loop will handle messages for all windows created in the same thread as the loop, since you are setting the hWnd parameter to NULL instead of a specific window. Once you create your tray icon window, the loop will receive messages for it just fine. You can then provide your tray icon with a popup menu that contains an item that will exit your message loop when clicked, thus allowing you to exit from WinMain() gracefully, invoke destructors, etc.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you man, you and the comments made me understand my mistake. It was pretty obvious and yet i didn't see it. I will search for my real error in my window creation, and validate your answer – bachinblack Oct 26 '17 at 23:43
0

GetMessage returns -1 if an error occurs. So you should do while != 0 generally. But if you are not seeing "end", then your application is probably crashing.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx

yano
  • 4,095
  • 3
  • 35
  • 68