Thread: Callback function as class method

  1. #1
    Registered User
    Join Date
    May 2008
    Posts
    7

    Callback function as class method

    Hello,

    I know that this issue was already asked before many times. I made some research through the web and I found what I was looking for.

    I found some solutions to this problem and I chose one of them. I came here to look for an explanation about the approach I used in my code.

    I'm trying to develop an error class to my Win32 application and I decided that the error messages will show up in a Win32 DialogBox.

    So, I developed an Error class. This class has a method CreateDialogBox, where I will call the DialogBox in the resource file. I need to pass as a parameter to the Win32 API function "DialogBox", that will create my DialogBox, a function called DialogProc with the signature expected by the Win32 API function "DialogBox".

    How did I solve this problem? I used the wrapper approach with a global pointer to the instance of the class declared into my Error.cpp file.

    As you can see in the code below, the DialogProc is a method of my Error class. There is a static wrapper, with the same signature of the DialogProc, that will be passed to the WIN32 API function "DialogBox". Look the code below:

    Error.h:

    Code:
    class Error
    {
        private:
            char* errorMessage;
            int sizeOfMessage;
            bool stopExecution;
        public:
            Error();
            ~Error();
    
            BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
            static BOOL CALLBACK Wrapper(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
            void CreateDialogBox();
    
            void CreateErrorMessage(int size);
            void DestroyErrorMessage();
            void SetErrorMessage(char* msg);
            char* GetErrorMessage();
    
            void SetStopExecution(bool flag);
            bool GetStopExecution();
    };
    Error.cpp:

    Code:
    #include "../Global.h"
    
    void* ptrError;
    
    Error::Error()
    {
        this->errorMessage = 0;
        this->sizeOfMessage = 0;
        this->stopExecution = false;
    }
    
    Error::~Error()
    {
        DestroyErrorMessage();
    }
    
    BOOL CALLBACK Error::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch(uMsg)
        {
            case WM_INITDIALOG:
    
                SetDlgItemText(hwndDlg, IDC_TXT, this->errorMessage);
    
                return TRUE;
    
            case WM_CLOSE:
    
                EndDialog(hwndDlg, 0);
    
                return TRUE;
    
            case WM_COMMAND:
    
                switch(LOWORD(wParam))
                {
                    case IDC_BTN_OK:
    
                        EndDialog(hwndDlg, 0);
    
                        return TRUE;
                }
        }
    
        return FALSE;
    }
    
    BOOL CALLBACK Error::Wrapper(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        Error* mySelf = (Error*) ptrError;
    
        return mySelf->DialogProc(hwndDlg, uMsg, wParam, lParam);
    }
    
    void Error::CreateDialogBox()
    {
        SDL_SysWMinfo *info;
    
        SDL_GetWMInfo(info);
    
        HWND hWin = info->window;
    
        HINSTANCE hInstance = (HINSTANCE) GetWindowLong(hWin, GWL_HINSTANCE);
    
        ptrError = this;
    
        DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, Error::Wrapper);
    }
    
    void Error::CreateErrorMessage(int size)
    {
        this->errorMessage = new char[size];
        this->sizeOfMessage = size;
        memset(this->errorMessage, '\0', size);
    }
    
    void Error::DestroyErrorMessage()
    {
        if(this->errorMessage != 0)
        {
            delete errorMessage;
        }
    }
    
    void Error::SetErrorMessage(char* msg)
    {
        strcpy(this->errorMessage, msg);
    }
    
    char* Error::GetErrorMessage()
    {
        return this->errorMessage;
    }
    
    void Error::SetStopExecution(bool flag)
    {
        this->stopExecution = flag;
    }
    
    bool Error::GetStopExecution()
    {
        return this->stopExecution;
    }
    I don't like very much the idea of using a global pointer to the class Error so I can call the method inside the wrapper.

    So, the first question is: Is there any better way to to this?

    Second: What are the problems I will have with the approach I chose?

    Thanks in advance fot the answers...

    Bruno.

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> So, the first question is: Is there any better way to to this?
    Depends. You could use CreateDialogParam() which passes a custom parameter to WM_INITDIALOG. However, there a few messsages that are dispatched before WM_INITDIALOG. So if you ever needed to hanled those messages you'll have to keep doing what your doing.

    >> What are the problems
    It's not thread safe. Not a problem if you don't have multiple threads calling Error::CreateDialogBox().

    gg

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You could use a thread-local variable instead of a true global. That would make it thread-safe. Just make sure that the thread that creates the dialog is always the one running the message loop.

    This scheme, however, breaks down if you create more than one window in the same thread (i.e. the dialog shows another dialog).
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Registered User
    Join Date
    May 2008
    Posts
    7
    Is there another way, using the same WIN32 API function DialogBox and not using a global pointer?

    Is it a good design to use a global pointer?

    Is it object oriented design doing this?

    Thanks again...

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by schifers View Post
    Is there another way, using the same WIN32 API function DialogBox and not using a global pointer?
    CornedBee suggested using TLS, which is a good suggestion.
    Is it a good design to use a global pointer?

    Is it object oriented design doing this?
    Answer to both above questions: No, but unless you defined a completely new interface for the Win32 API, you can't really fix the problem any other way, other than some semi-global way, e.g. using TLS.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Unfortunately, Win32 API is C, and C is not object oriented so you're kindof limited.
    I don't think there's an easy or "best" way to do this, unfortunately.

    What you could do to use several windows inside the same thread is a map and an index variable, perhaps:
    Code:
    int nIndex = 0;
    
    BOOL CALLBACK Error::Wrapper(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        Error* mySelf = g_map.find(nIndex++)->second;
        return mySelf->DialogProc(hwndDlg, uMsg, wParam, lParam);
    }
    This is a very incomplete solution, but you could build on it.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Registered User
    Join Date
    May 2008
    Posts
    7
    Thanks again for everyone who answered this... I decided to keep this design, since I won't use threads now...

  8. #8
    Registered User
    Join Date
    May 2008
    Posts
    13
    I have been searching for information how to make the CALLBACK as one of my class methods, and I found your thread, thanks.

    However I run into problem because it seems I cant create like your signature.

    Code:
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DISPLAYSTATUS), 
    		NULL, myTest::DisplayStatus);

    the error says: cannot convert parameter 4 from 'int (struct HWND__ *,unsigned int,unsigned int,long)' to 'int (__cdecl *)(struct HWND__ *,unsigne

    any idea?

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Declare the static member as CALLBACK.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    There is no way to get it to work with a callback to a class member unless it's static or the function is global (how would the function know which class instance to call?).
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Registered User
    Join Date
    May 2008
    Posts
    13
    thank you for the replies. If I declare my CALLBACK as static member function, will I able to access my class data that are not static?

    Code:
    class myClass
    {
    public:
    myClass();
    void WriteData(DWORD inputData);
    static BOOL CALLBACK dialProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    private:
    DWORD num;
    myAnotherClass writeDataToFile;
    };
    So inside my CALLBACK function, can I access to the private data especially myAnotherClass object? Because I read somewhere that's static method only can access to static data, but not sure whether I remember it right or not.
    Last edited by lakewa; 05-17-2008 at 10:00 AM.

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Static members can only access static data.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Registered User
    Join Date
    May 2008
    Posts
    13
    If my CALLBACK is static and only can access to static data, then I'm in trouble unless I have to change all the data members to static which I'm not sure if that will affect my other functions as well because they are not static. Otherwise I will have to use alot of global variables for those data members initialization.
    Last edited by lakewa; 05-17-2008 at 10:02 AM.

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Here's the thing - a static member is a member of which there is only one of regardless of how many instances there are. So that means you cannot have an unique data - there will only be one instance of everything.
    So everything static isn't really an option. There's no easy way using a class member as a windowproc due to limitations in C.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Take a look at Error::Wrapper() in the schifers original post. Wrapper() is the static method, which then calls non-static Error::DialogProc() on the Error object instance.

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  2. My Window Class
    By Epo in forum Game Programming
    Replies: 2
    Last Post: 07-10-2005, 02:33 PM
  3. c++ linking problem for x11
    By kron in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2004, 10:18 AM
  4. structure vs class
    By sana in forum C++ Programming
    Replies: 13
    Last Post: 12-02-2002, 07:18 AM
  5. Warnings, warnings, warnings?
    By spentdome in forum C Programming
    Replies: 25
    Last Post: 05-27-2002, 06:49 PM