Skip to main content
GameDev.net gamedev.net
🔒 Locked

WinAPI: OOP scope correction

Started by wing_majin May 29, 2003 at 4:00 PM 7 replies 1.6k views
Original Post
wing_majin
wing_majin
I''m attempting to implement a class that defines a special type of dialog box however when I attempt to create it as a modeless box within the implementation file of my class as such: void CDlg::CreateCDialog(HWND inparent, HINSTANCE maininst) { parent = inparent; CWin = CreateDialog(maininst, MAKEINTRESOURCE(IDD_MAIN), parent, CProc); } I get this error: "error C2664: ''CreateDialogParamA'' : cannot convert parameter 4 from ''int (struct HWND__ *,unsigned int,unsigned int,long)'' to ''int (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'' My procedure is defined as a member function of the class and is in the same file. When not implemented in a class this works just fine as normal. However when I make the procedure''s prototype in the header file and then implement the proc function as such: BOOL CALLBACK CDlg::CProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { } it gives the above error. I don''t really understand how __stdcall works into this. All I know is that when I comment out the proc function''s prototype in the header file and then make it just BOOL CALLBACK CProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { } it works, but I can''t access the members of my class obviously (which is quite annoying). This seems really easy to fix I just can''t see it ...any ideas? (sorry if this was long, it''s hard to explain in one or two sentences)
antareus
antareus
Recall that in C++ nonstatic class functions are implemented with a hidden this pointer. This makes your CProc method have five arguments, causing it not to match the four required. Are you sure MFC doesn''t handle this problem for you?
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
wing_majin
wing_majin
I tried making the procedure function static but since I call other member functions that aren''t and cannot be static then that won''t work. I even also tried passing the proc as this->CProc and the error doesn''t not change. (I''m not using MFC by the way, I just made generic names for my functions, sorry for any confusion)

The article linked there is enlightening but too above me at this point (I''m a little rusty). I''m not looking for anything that perfect anyway. How can I make the proc function static yet still be able to access normal member functions and alter variables? Any other ideas? What am I missing?
sbennett
sbennett
The way I do this is to make the callback function static, and keep (as another static member) a std::map<HWND, CDlg*> to map the HWND that gets passed in to the CDlg object associated with it. Then, in your dialog proc, simply look up the HWND in this map, and call any methods you need on that object.

[edit: fixed <>]

[edited by - sbennett on May 30, 2003 11:01:43 AM]
wing_majin
wing_majin
I made the proc static and also made a map called CWinMap as per your suggestion.
I then do this:

void CDlg::CreateCDialog(HWND inparent, HINSTANCE maininst)
{
parent = inparent;
CWinMap.insert(pair(CreateDialog(maininst, MAKEINTRESOURCE(IDD_MAIN), parent, CProc), this));
}

and then in the proc function, at the very top:

BOOL CALLBACK CDlg::CProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
map::iterator thisone = CWinMap.find(hwnd);
CDlg * CurrDlg = thisone->second;

...

From there everytime I access a member function or variable of the class I do so by doing something like: CurrDlg->Function(hwnd) etc
but although this all compiles I get a linking error (unresolved external). Where''d I screw up?

sbennett
sbennett
Can you post the exact error you''re getting?
Jumpster
Jumpster
Here's a template class that I have for just such an occasion: I use similar classes for my Threads, Windows, Timers and other controls that take some "CALLBACK" function (the only differences are their respective Execute() methods)... The one below is specialized for your situation...


template <class parent_class> class WFCUSTOM_ACTION
{
public:
typedef BOOL (parent_class::* parent_method)(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);

private:
typedef WFCUSTOM_ACTION<parent_class> ACTION;


// Event-Notifier Target Object...

parent_class* pObject;

// Event-Notifier Method pointer to call...

parent_method pMethod;

public:
// Event-Notifier Constructor...

WFCUSTOM_ACTION( parent_class* parent = NULL ) : pObject(parent), pMethod(NULL) {}

// Event-Notifier Destructor...

~WFCUSTOM_ACTION( WFVOID ) {};

WFBOOL WFCALLBACK Execute(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
WFBOOL result = WFFALSE;
if (WFTRUE == Assigned())
{
result = (pObject->*pMethod)( hwnd, Message, wParam, lParam);
}
return result;
}


// Determine if the Event-Notifier has been assigned yet...

WFBOOL Assigned( WFVOID )
{ return ((NULL != pObject) && (NULL != pMethod)) ? WFTRUE : WFFALSE; }

// Set the Event-Notifier method...

ACTION& SetHandler( const parent_method pfunc )
{ pMethod = pfunc; return VALUE_OF(this); }

// Set the Event-Notifier object...

ACTION& SetObject( parent_class* pobj )
{ pObject = pobj; return VALUE_OF(this); }

// Set the Event-Notifier method...

ACTION& operator= ( const parent_method pfunc )
{ return SetHandler( pfunc ); }

// Set the Event-Notifier object...

ACTION& operator= ( parent_class* pobj )
{ return SetObject( pobj ); }

// Return a constant pointer to the Event-Notifier object...

const parent_class* GetObject( WFVOID ) const
{ return pObject; }

// Return a constant pointer to the Event-Notifier method...

const parent_method GetHandler( WFVOID ) const
{ return pMethod; }

};


I would then create a variable in your class...

WFCUSTOM_ACTION Action;  


and then in the constructor (or in your case CDlg::CreateCDialog) I would complete it as such:


void CDlg::CreateCDialog( HWND inparent, HINSTANCE maininst)
{
Action.SetObject(this);
Action.SetHandler( this->CProc );
CWin = CreateDialog(maininst, MAKEINTRESORCE(IDD_MAIN), parent, Action.Execute );
}


It may take a bit of tweaking (like removing the WF prefix from variable types - but you should get the general idea...

Good luck and happy coding,

Regards,
Jumpster

Edit: Oops, I had the Execute() returning a void - I fixed it to return the expected BOOL... But anyway, here's the general Idea.


[edited by - Jumpster on June 2, 2003 10:34:08 AM]
Regards,JumpsterSemper Fi
Oluseyi
Oluseyi
quote:
Original post by wing_majin
I tried making the procedure function static but since I call other member functions that aren''t and cannot be static then that won''t work.
Yes, so you need a way to associate the HWND with the CDlg object and call functions specific to that object. HWNDs have a LONG value associated with them, which can be accessed using SetWindowLong and retrieved using GetWindowLong. So you make the DialogProc static to the CDlg class, associate the CDlg object with the HWND in the initialization phase using SetWindowLong and reinterpret_cast (to convert a pointer to a CDlg to a long) and retrieve the CDlg * whenever a message is dispatched to figure out which object to call functions on.

The article isn''t over your head. The specific approach may be, because it goes further than merely creating an object-oriented wrapper and also creates a framework for runtime insertion of new message handlers.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.