C Board  

Go Back   C Board > Platform Specific Boards > Windows Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 05-15-2008, 06:17 AM   #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.
schifers is offline   Reply With Quote
Old 05-15-2008, 07:31 AM   #2
Registered User
 
Codeplug's Avatar
 
Join Date: Mar 2003
Posts: 3,900
>> 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
Codeplug is online now   Reply With Quote
Old 05-15-2008, 07:49 AM   #3
Cat without Hat
 
CornedBee's Avatar
 
Join Date: Apr 2003
Posts: 8,492
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
CornedBee is offline   Reply With Quote
Old 05-15-2008, 08:22 AM   #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...
schifers is offline   Reply With Quote
Old 05-15-2008, 08:26 AM   #5
Kernel hacker
 
Join Date: Jul 2007
Location: Farncombe, Surrey, England
Posts: 15,686
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.
Quote:
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.
matsp is offline   Reply With Quote
Old 05-15-2008, 08:26 AM   #6
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,783
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.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 05-15-2008, 08:31 AM   #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...
schifers is offline   Reply With Quote
Old 05-16-2008, 09:02 PM   #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?
lakewa is offline   Reply With Quote
Old 05-17-2008, 03:49 AM   #9
Cat without Hat
 
CornedBee's Avatar
 
Join Date: Apr 2003
Posts: 8,492
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
CornedBee is offline   Reply With Quote
Old 05-17-2008, 03:57 AM   #10
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,783
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?).
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 05-17-2008, 09:52 AM   #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.
lakewa is offline   Reply With Quote
Old 05-17-2008, 09:54 AM   #12
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,783
Static members can only access static data.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 05-17-2008, 09:59 AM   #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.
lakewa is offline   Reply With Quote
Old 05-17-2008, 10:02 AM   #14
Mysterious C++ User
 
Elysia's Avatar
 
Join Date: Oct 2007
Posts: 14,783
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.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 05-17-2008, 10:05 AM   #15
Registered User
 
Codeplug's Avatar
 
Join Date: Mar 2003
Posts: 3,900
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
Codeplug is online now   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
How to fix misaligned assignment statements in the source code? biggyK C++ Programming 28 07-16-2006 11:35 PM
My Window Class Epo Game Programming 2 07-10-2005 02:33 PM
c++ linking problem for x11 kron Linux Programming 1 11-19-2004 10:18 AM
structure vs class sana C++ Programming 13 12-02-2002 07:18 AM
Warnings, warnings, warnings? spentdome C Programming 25 05-27-2002 06:49 PM


All times are GMT -6. The time now is 11:51 PM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22