locked
Extremely strange compiler behavior, please help identify source RRS feed

  • Question

  • While I was reinventing the wheel a little bit and throwing together a wrapper class for some basic Windows API calls, I discovered that my program will compile, but when I run it the first time it throws an unhandled exception and drops to the debugger. If I break, and stop debugging, it will run perfectly fine every subsequent compilation/execution, regardless of what additional code I add. I find this behavior exceptionally odd. Could anyone please help me identify what causes such a bug? I am using Visual Studio 2010 C++ Express Edition, with Windows SDK 7.1 for .NET Framework 4, targeting x64.

    I am attempting to follow the design principles from this site http://www.gamedev.net/page/resources/_/technical/game-programming/creating-a-win32-window-wrapper-class-r1810, but I decided to allow the the user to pass his own callback function instead of registering a function for each message.

    Here is the relevant code:

    Window.h

    #include <Windows.h>
    #include <stdlib.h>
    #include <tchar.h>
    
    #include "resource.h"
    
    typedef LRESULT(*WindowProcedure_T)(HWND, UINT, WPARAM, LPARAM);
    
    class Window {
    
    public:
    	Window(WindowProcedure_T WndProc, LPWSTR title, LPCWSTR thisClassName, LPWSTR hIcon, int width, int height);
    	~Window();
    
    	void SetHWND(HWND hwnd) { hWnd = hwnd; }
    	BOOL Show(int cmdShow);
    	WindowProcedure_T getWndProc() { return WindowProcedure; }
    
    	static HINSTANCE hInst;
    	
    private:
    	static LRESULT CALLBACK MessageHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    	HWND hWnd;
    	WindowProcedure_T WindowProcedure;
    };

    Window.cpp

    #include "Window.h"
    
    HINSTANCE Window::hInst = (HINSTANCE)GetModuleHandle(NULL);
    
    Window::Window(WindowProcedure_T WndProc, LPWSTR title, LPCWSTR thisClassName, LPWSTR hIcon, int width, int height) {
    
    	WindowProcedure = WndProc;
    
        WNDCLASSEX wc;
        wc.cbSize        = sizeof(WNDCLASSEX);
        wc.style         = 0;
        wc.lpfnWndProc   = MessageHandler;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = hInst;
        wc.hIcon         = LoadIcon(hInst, hIcon);
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        wc.lpszMenuName  = NULL;
        wc.lpszClassName = thisClassName;
        wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
    
        if(!RegisterClassEx(&wc)) throw std::runtime_error(std::string("REGISTERFAIL"));
    	hWnd = CreateWindowEx(
            WS_EX_CLIENTEDGE,
            thisClassName,
            title,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, width, height,
            NULL, NULL, hInst, this);
    
    	if(hWnd == NULL) throw std::runtime_error(std::string("CREATEFAIL"));
    
    }
    
    Window::~Window() {
    }
    
    LRESULT CALLBACK Window::MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    
      Window *wnd = 0;
      if(msg == WM_NCCREATE)  {
    	// retrieve Window instance from thisClassName
        wnd = reinterpret_cast<Window *>(((LPCREATESTRUCT)lParam)->lpCreateParams);
    	// store retrieved pointer as the registered window's user data 
        ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<long>(wnd));
    	// save window handle
        wnd->SetHWND(hwnd);
      }
      else
    	// retrieve associated Window instance
        wnd = reinterpret_cast<Window *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
      
      // call the windows message handler
      return (wnd->getWndProc())(hwnd, msg, wParam, lParam); // This is where the first-time execution throws the unhandled exception. The backtrace indicates CreateWindowEx has not returned yet.
    }
    
    BOOL Window::Show(int cmdShow) {
    	ShowWindow(hWnd, cmdShow);
    	if(!UpdateWindow(hWnd)) throw std::runtime_error(std::string("UPDATEFAIL"));
    
    	return true;
    }
    

    file with winmain:

    #include <vmci_sockets.h>
    #define BUFSIZE 4096
    
    #include <windows.h>
    #include <stdlib.h>
    #include <tchar.h>
    
    #include "Window.h"
    
    #pragma comment(lib, "Ws2_32.lib")
    
    #define WM_NOTIFIER (WM_USER + 1)
    #define IDM_EXIT	1001
    
    LRESULT WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    Window *wnd1;
    
    int WINAPI WinMain
       (HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow)
    {
    	try{
    		wnd1 = new Window(&WindowProcedure, L"Test1!", L"wut1", MAKEINTRESOURCE(IDI_ICON1), 240, 120);
    		wnd1->Show(cmdShow);
    	}
    	catch (std::runtime_error &e) {
    		::MessageBox(NULL, L"I broked it.", L"Runtime Error", MB_OK|MB_ICONERROR|MB_DEFBUTTON1|MB_TASKMODAL|MB_TOPMOST);
    		return 1;
    	}
    	catch (...) {
    		::MessageBox(NULL, L"I broked it hard.", L"Runtime Error", MB_OK|MB_ICONERROR|MB_DEFBUTTON1|MB_TASKMODAL|MB_TOPMOST);
    		return 1;
    	}
    
    	MSG Msg;
        while(GetMessage(&Msg, NULL, 0, 0) > 0)
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    
        return (int)Msg.wParam;
    
    }
    
    
    LRESULT WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    
    		NOTIFYICONDATA nId;
    
    		switch(msg)
    		{
    			case WM_CREATE:
    
    				nId.cbSize = sizeof(NOTIFYICONDATA);
    				nId.hWnd = hwnd;
    				nId.uID = 100;
    				nId.uVersion = NOTIFYICON_VERSION;
    				nId.uCallbackMessage = WM_NOTIFIER;
    				nId.hIcon = LoadIcon(Window::hInst, MAKEINTRESOURCE(IDI_ICON1));
    				wcscpy_s(nId.szTip, L"ROLE1!");
    				nId.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
    	
    				Shell_NotifyIcon(NIM_ADD, &nId);
    			break;
    			case WM_CLOSE:
    				DestroyWindow(hwnd);
    			break;
    			case WM_DESTROY:
    				nId.cbSize = sizeof(NOTIFYICONDATA);
    				nId.hWnd = hwnd;
    				nId.uID = 100;
    				Shell_NotifyIcon(NIM_DELETE, &nId);
    
    				PostQuitMessage(0);
    			break;
    			case WM_NOTIFIER:
    				switch(lParam) {
    					case WM_CONTEXTMENU:
    					case WM_RBUTTONDOWN:
    						HMENU contextMenu = CreatePopupMenu();
    						AppendMenu(contextMenu, MF_STRING, IDM_EXIT, L"E&xit");
    						POINT CurPos ;
    						GetCursorPos (&CurPos);
    						SetForegroundWindow(hwnd);
     						TrackPopupMenu(contextMenu, TPM_CENTERALIGN|TPM_BOTTOMALIGN|TPM_LEFTBUTTON|TPM_VERNEGANIMATION, CurPos.x,CurPos.y, 0, hwnd, NULL);
    						PostMessage(hwnd, WM_NULL, 0, 0); 
    					break;
    				}
    			break;
    			case WM_COMMAND:
    				switch(LOWORD(wParam)) {
    					case IDM_EXIT:
    						PostMessage(hwnd, WM_CLOSE, 0, 0);
    					break;
    				}
    			break;
    			default:
    				return DefWindowProc(hwnd, msg, wParam, lParam);
    		}
    		return 0;
    }
    

     

    Sunday, October 16, 2011 1:37 AM

Answers

  • It seems that there are messages which are sent before WM_NCCREATE. For example: WM_GETMINMAXINFO. I would suggest you to call DefWindowProc if wnd is null. Or investigate how the things are done in MFC and ATL.

     

    Why it runs fine every subsequent execution? Probably due to “Program Compatibility Assistant”.

     

    • Edited by Viorel_MVP Sunday, October 16, 2011 11:44 AM
    • Marked as answer by Jon Real Sunday, October 16, 2011 8:49 PM
    Sunday, October 16, 2011 10:39 AM

All replies

  • In order to replicate the error behavior, create a new solution with the same source.
    Sunday, October 16, 2011 1:41 AM
  • It seems that there are messages which are sent before WM_NCCREATE. For example: WM_GETMINMAXINFO. I would suggest you to call DefWindowProc if wnd is null. Or investigate how the things are done in MFC and ATL.

     

    Why it runs fine every subsequent execution? Probably due to “Program Compatibility Assistant”.

     

    • Edited by Viorel_MVP Sunday, October 16, 2011 11:44 AM
    • Marked as answer by Jon Real Sunday, October 16, 2011 8:49 PM
    Sunday, October 16, 2011 10:39 AM
  • Hit the nail on the head! Thanks!
    Sunday, October 16, 2011 8:50 PM