1

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);
    }
}
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • 4
    You are crossing code that's not under your control. When using `setjmp`/`longjmp` you have to make sure, that everyone is in on the joke. Everything in between your call to `DispatchMessage` and `WndProc` is not prepared for this stunt. If it appears to work in a 64-bit build, it does so by coincidence only. – IInspectable Sep 11 '15 at 14:35
  • If goto is considered harmful, what is this? Why are you trying to re-enter `main()`? If the goal is to exit your message loop when you get the first paint... [there are better ways](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644945%28v=vs.85%29.aspx) – theB Sep 11 '15 at 14:46
  • Unfortunately it was used to work ... many years ago. Is there any specific Microsoft document that warns about the possible side effects or limitations in setjmp/longjmp usage? – Giuseppe Guerrini Sep 11 '15 at 14:47
  • Stack unwinding through the Wow64 emulator is in general a problem, especially so in Win7. WM_PAINT originates in the 64-bit window manager, the call stack crosses the boundary twice. Also a big problem with stack unwinding due to exception handling unwinding, [this MSDN article](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573%28v=vs.85%29.aspx) talks about it. – Hans Passant Sep 11 '15 at 14:51
  • 1
    `ret = setjmp(jump_buffer);` You are allowed to **test** the "return" value from setjmp() in a **conditional** , but thou **should not use it in an assignment**. – joop Sep 11 '15 at 14:52
  • @joop: sorry, I can't any note about this limitation in MSDN; could you post some links about this issue? – Giuseppe Guerrini Sep 11 '15 at 15:01
  • 1
    It is in the C-standard, since c89, IIRC. Maybe the c.l.c archive/faq ? GIYF BTW: MSDN is *not* a source of information. (well, maybe on implementation or usage stuff) Also: your `flag` should really be volatile, – joop Sep 11 '15 at 15:02
  • Instead, this article warns about interactions between setjmp/longjmp and SEH/C++ exceptions: https://msdn.microsoft.com/en-us/library/yz2ez4as.aspx . I don't know if it has something to do with this problem, – Giuseppe Guerrini Sep 11 '15 at 15:03
  • Just found something about limitation of setjmp here (it talks about GNU implementation): http://www.gnu.org/software/libc/manual/html_node/Non_002dLocal-Details.html . But it doesn't seem to be the Dmitry's problem (just tested on my W7-64 machine with and without assignment: no differences). – Giuseppe Guerrini Sep 11 '15 at 15:11
  • 1
    http://stackoverflow.com/questions/16636206/how-does-non-local-jumps-in-c-defined-in-setjmp-h-work There you go ... – joop Sep 11 '15 at 15:12
  • Thanks everyone! It seems that there's no straightforward solution to the problem; i'll try some other way. As to "why", it's the code that runs inside perl that uses setjmp for its exception implementation. – Dmitry Karasik Sep 12 '15 at 13:35

1 Answers1

2

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.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252