Sign in to follow this  
Followers 0

Interesting solution to a "Windows gotcha"

1 post in this topic

This is only sort-of related to AutoIt. It shows a different way to do something I've seen in AutoIt, which is the releavance.

I finally figured out a way make a GUI fully encapsulated in a class without requiring a hard-code global variable to do the message loop. Jon, as you know, you currently have something like this for the message handler:

LRESULT CALLBACK AutoIt_App::WndProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
        return g_oApplication.WndProcHandler(hWnd, iMsg, wParam, lParam);

} // WndProc()

LRESULT AutoIt_App::WndProcHandler (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
    // Actual code

However, I conjured up a way to replace g_oApplication with an object determined at run-time. Here's how (I tested this using DialogBox functions, there is an extra step involved and the messages are different for a Window, but the same idea will work).

Step 1: The last parameter in DialogBoxParam/CreateWindow allows us to pass whatever we want to WM_INITDIALOG/WM_CREATE. In either case, pass this (The object, of course) for that parameter.

Step2: This is where things change a bit. In a dialog, the LPARAM of WM_INITDIALOG will be our this pointer. That makes it easy, call SetWindowLong with DWL_USER and supply our pointer as the data. Now the pointer is attached to the HWND. In a Window, there is a structure passed to WM_CREATE. Inside the structure is our pointer. So we should get it from that instead (and it should also be set to GWL_USERDATA instead of DWL_USER).

Step3: In WndProc() above, instead of calling the global object, this can be used:

AutoIt_App *obj = reinterpret_cast<AutoIt_App*>(GetWindowLong(hWnd, GWL_USERDATA));
return obj->WndProcHandler(hWnd, iMsg, wParam, lParam);

Voila, proper object determined at run time.

Here is a sample of how I did it with a modal dialog box I'm working on, currently. First, the class definition:

class CPermaBarGUI
    int Show(HWND hParent, HINSTANCE hInstance);

public: // Callback functions
    static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    INT_PTR CALLBACK DlgProcHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    INT_PTR CALLBACK OnDialogCommand(WORD wCommand, WORD wNotify, HWND hControl, HWND hParent);

and now the implementation of the relevant functions:

int CPermaBarGUI::Show(HWND hParent, HINSTANCE hInstance)
    return static_cast<int>(DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hParent, 
        DlgProc, reinterpret_cast<LPARAM>(this)));

inline INT_PTR CALLBACK CPermaBarGUI::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    if (uMsg == WM_INITDIALOG)
        SetWindowLong(hWnd, DWL_USER, static_cast<LONG>(lParam));   
    // Fall through so the object gets to handle WM_INITDIALOG, too
    CPermaBarGUI *obj = reinterpret_cast<CPermaBarGUI*>(GetWindowLong(hWnd ,DWL_USER));
    return obj->DlgProcHandler(hWnd, uMsg, wParam, lParam);

inline INT_PTR CALLBACK CPermaBarGUI::DlgProcHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    return FALSE;

There is a size warning which can be disabled in VC++ with #pragma's, but I left that out of the example.

I thought this was kind of interesting. I hated having to use a global variable whenever I wrap a GUI in a class like Jon did with AutoIt. Now I have a nice working solution to the problem that wasn't hard to implement and shouldn't have much of a performance hit at all. Jon, feel free to use it if the global variable bothers you, too, as this seems to work quite well (And, since it binds each object to the HWND, it supports multiple objects/windows).

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  
Followers 0