There's a problem when I use longjmp in a 32-bit application on Windows 64-bit (Windows 7). Instead of returning to a point of the last setjmp() call, it lands after the last DispatchMessage() call. Here's the code example, which works fine if compiled by a 64-bit compiler, and fails with a 32-bit version.
Are there any ideas for a workaround? Microsoft seems to be silent on a tangential question here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/b63a573f-007e-43a3-877c-b06280aa8bcc/0x80000026-application-error-when-exiting-after-using-longjmp-on-windows8-x64?forum=windowscompatibility
// Compile as: cl a.c user32.lib kernel32.lib
#include <windows.h>
#include <setjmp.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
jmp_buf jump_buffer;
int flag = 0;
int main()
{
WNDCLASS wc = {0, };
ATOM atom = 0;
HWND wnd;
MSG msg;
int ret;
wc.lpfnWndProc = &WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "ExitWindows() handler";
atom = RegisterClass(&wc);
wnd = CreateWindow(wc.lpszClassName, wc.lpszClassName,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wc.hInstance, NULL);
ret = setjmp(jump_buffer);
switch ( ret ) {
case 0:
ShowWindow(wnd,SW_SHOW);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if ( flag ) {
printf("not ok\n");
break;
}
}
break;
case 1:
printf("ok\n");
break;
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_PAINT:
flag = 1;
longjmp(jump_buffer, 1);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
Using longjmp in WndProc is unsafe because of the nature of a window procedure:
if can be called through a SendMessage function, in which case it in not called with the same context (stack) that what you used for the setjmp. In that case, I think that anything could happen... - Ok WM_PAINT in normally posted and not sent so that should not apply here, even if IMHO it is the main reason to not do that
the system could prepare some internal structs for you before calling the window procedure (in DispatchMessage) and expect to be able to clean them after WndProc returns. Using a longjmp would break that.
The Windows API on WindowProc functions says: The return value is the result of the message processing and depends on the message sent.
My understanding of it is that a window procedure is supposed to return and to never call longjmp of exit. It is not explicit in Windows documentation, but I would not dare use a window procedure that would not return.
The correct way to exit correctly from a message loop is by posting a WM_QUIT message (Use the PostQuitMessage function to exit a message loop). It makes the GetMessage functions returns 0 and allows the system to cleanup the message loop that was installed at first call to GetMessage.
Related
Below is my code.it getting a Handle mistake.
`
int main(){
HWND hMainWin;
DWORD threadID;
//Create thread down
HANDLE threadHandle = CreateThread(NULL,
0,
threadFunction,
(LPVOID)hMainWin,
0,
&threadID);
}
DWORD WINAPI threadFunction(LPVOID param){ //my thread function
HANDLE hwnd = (HANDLE)param;
for(int i=0;i<5;i++)
{
printf("hello");
}
return 0;
}
Error:
You have a variable HWND hMainWin; that is not initialized and you are using that (uninitialized) variable as argument #4 in a call to CreateThread(). Since the variable is uninitialized its value is undefined which is the reason for the error message you are getting.
From the looks of it your code seems to be a console application. As such you don't have a window or a handle to it (the HWND) thus you are having trouble initializing the hMainWin.
It looks like that parameter is just a dummy variable (not really used in your thread function) so you might as well (just for a "quick fix") initialize it with a NULL (or a 0)
While elaborating an answer for another question (by myself), I've come up with the idea of using a MessageBox to report the result of my dialog box. It is a WinAPI modal dialog box created with the DialogBox() function.
However, I noticed that handling WM_DESTROY in the dialog's procedure function will prevent the message box from appearing. I would like to understand the mechanics behind that.
Here's the full code, and the referred message box is at the end, right before return 0:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string.h>
#include "resource.h"
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
// uncomment these and the MessageBox won't show up
//case WM_DESTROY:
//PostQuitMessage(0);
//break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (MessageBoxW(hwnd, L"Close the window?", L"Confirm", MB_OKCANCEL) == IDOK)
EndDialog(hwnd, (INT_PTR)wParam);
break;
case IDCANCEL:
MessageBoxW(hwnd, L"Goodbye.", L"Close", MB_OK);
EndDialog(hwnd, (INT_PTR)TRUE);
break;
}
}
return (INT_PTR)FALSE;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
INT_PTR ret = DialogBoxW(NULL, MAKEINTRESOURCEW(IDD_DIALOG1), NULL, DlgProc);
wchar_t retTxt[10];
if (ret == IDOK)
wcscpy(retTxt, L"Alright!");
else
wcscpy(retTxt, L"Error");
MessageBoxW(NULL, retTxt, L"Result", MB_OK);
return 0;
}
When you use DialogBox rather than DialogBoxParam, the dialog runs its own message loop that handles WM_DESTROY internally. When you post the WM_QUIT message from your dialog procedure you are generating an additional message* that the dialog box won't use, so it remains in your thread's message queue once the DialogBox function returns.
You then call MessageBox, which runs its own message loop. This immediately pulls the WM_QUIT out of the queue, exits the loop and returns before the dialog is even displayed.
(* actually it's not a "real" message - an internal flag is set that indicates quit has been posted - but the effect is the same on the subsequent message loop)
Win32 API:PostMessage
BOOL WINAPI PostMessage(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
I have a shared_ptr created in a worker thread, and I need to pass it to the UI thread. How can I do it?
I have defined my own message WM_USER_THREAD, hWnd is the main window.
If I have the following code below, how do I detect when the window has been closed, so I can quit? r never seems to get the value -1 0, and I need to process messages for the entire thread, not just the current window.
HWND hWnd = CreateWindowExW(0, L"Edit", L"My Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL, NULL, NULL);
ShowWindow(hWnd, SW_SHOWDEFAULT);
MSG msg;
BOOL r;
while ((r = GetMessageW(&msg, NULL, 0, 0)) != 0)
{
if (r == -1) { break; }
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
Waiting for r = -1 is not the way you detect that your window has closed. A return value of -1 is not a normal condition: it indicates that an error has occurred in the message loop.
From the documentation:
Return Value
Type: BOOL
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.
When GetMessage retrieves a WM_QUIT message from the queue, it will return a value of 0, and you should end the loop.
If you just want to know when the window has closed, you probably want to handle either the WM_CLOSE or WM_DESTROY messages. For a discussion of these messages, see the answers to this question: What is the difference between WM_QUIT, WM_CLOSE, and WM_DESTROY in a windows program?
I found a solution for this: WM_NULL.
The message loop can handle the matter on its own independently of WndProc:
// written in C#
MSG msg = new MSG();
while (GetMessage(out msg, window, 0, 0))
{
if ((msg.message == WM_NULL) && !IsWindow(window))
break;
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
Console.WriteLine("yeah, out of loop ^^");
From my observation: When window is destroyed GetMessage retrieves WM_NULL messages without pause (1st hint) and IsWindow can check the window (affirmation).
I've tried using the "grab all of the process IDs enumerated by the desktop" method, however that doesn't work.
Is there a way to convert a handle to a window handle? -or-
Is there a way to take a process ID and find out all of the child windows spawned by the process?
I don't want to use FindWindow due to multiple process issues.
You could call EnumWindows() to iterate over all the top-level windows on the screen, then use GetWindowThreadProcessId() to find out which ones belong to your process.
For example, something like:
BOOL CALLBACK ForEachTopLevelWindow(HWND hwnd, LPARAM lp)
{
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
if (processId == (DWORD) lp) {
// `hwnd` belongs to the target process.
}
return TRUE;
}
VOID LookupProcessWindows(DWORD processId)
{
EnumWindows(ForEachTopLevelWindow, (LPARAM) processId);
}