Click here to Skip to main content
Click here to Skip to main content

Callbacks, Threads, and MFC

, 16 May 2000
Rate this:
Please Sign up or sign in to vote.
Learn how to use callbacks and threads with MFC.

One of the many problems that seem to plague programmers is the problem of using callback methods. These callbacks are part how the Windows API works, and some are the result of enumerators which provide access to underlying sets of Windows information.

This essay discusses how you can move from the raw API callback domain back to the MFC domain, which greatly simplifies how you actually write the code to handle the callbacks. It will be done primarily in the context of callbacks, with a final example about how to create worker threads in an MFC class.


Callbacks, particularly Enumerators

There are many useful methods that you can use to find out information; for example EnumFontFamiliesEx, EnumWindows, and the like. For some of these, there is no other effective way to enumerate the objects. For others, like EnumWindows, the method is the strongly recommended method for doing the enumeration (EnumWindows, for example, works even when other threads may be creating or deleting windows concurrent with your enumerating them).

The real problem, as seen by many, is that these are raw API functions, which in MFC are ::EnumWindows, ::EnumChildWindows, ::EnumFontFamiliesEx, and so on. What you get is a raw API object, for example, an HWND, and life becomes difficult because you have no access to the instance that did the enumeration or any MFC properties of it. For example, you may want to place the list of fonts in a list box, or the list of windows in a combo box, or something similar.

This often forces the unnecessary use of global variables, and an unnecessary reliance on GetDlgItem, both of which are fundamentally bad ideas (if you wonder about GetDlgItem being bad, see my essay on this topic).

For most enumerators, this can be avoided entirely by using the LPARAM parameter to the call. For example, the specification of several enumerator functions are:

int EnumFontFamiliesEx(HDC, LPLOGFONT, FONTENUMPROC,
                       LPARAM, DWORD);
BOOL EnumChildWindows(HWND, WNDENUMPROC, LPARAM);
BOOL EnumWindows(WNDENUMPROC, LPARAM);
BOOL EnumThreadWindows(DWORD, WNDENUMPROC, LPARAM);
BOOL EnumMetaFile(HDC, HMETAFILE, MFENUMPROC, LPARAM);
BOOL EnumResourceNames(HMODULE, LPCTSTR,
                       ENUMRESOURCENAMESPROC,
                       LONG /* LPARAM */);
BOOL EnumResoureTypes(HMODULE, ENUMRESOURCETYPESPROC,
                      LONG /* LPARAM */);

Alas, due to what I think is a serious failure in design, there are some enumerators which do not take an LPARAM, such as

EnumDateFormats
EnumDateFormatsEx
EnumTimeFormats

which make life difficult; what is inexcusable here is that the methodology of using an LPARAM was well-known when these functions were designed.

The above are only representative examples. In addition, there are a number of "Enum..." functions that do not use callbacks, and these can be safely ignored for this discussion.

For the sake of our example, we will look at a typical callback enumerator, EnumWindows. Note that the callback techniques I am about to describe apply to all callbacks, whether from enumerators or other sources, to which you can supply an arbitrary LPARAM-style value.

When using EnumWindows, remember that the function you call has no access to your class unless you take specific action to make it so. For example,

EnumWindows(enumwndfn, NULL)

will call the enumerator function, enumwndfn, which must be either an ordinary C function, or a static class member (thus having no access to any instance of the class, such as your CDialog-derived class)

The preferred solution to this is to pass the class instance in by using the LPARAM value:

EnumWindows(enumwndfn, (LPARAM)this)

Add the two declarations to your class:

static BOOL CALLBACK enumwndfn(HWND hWnd, LPARAM lParam);
BOOL enumwndfn(CWnd * wnd);

I often use the same name for both, simplifying the number of concepts I have to deal with; C++ overloading rules helps sort them out. Note carefully the use of the word "static" to qualify the first declaration. The consequence of this is that there is no this parameter implicit in the call, which also means that the method has no implicit access to the class instance.

In the implementation of the static member function do

BOOL CALLBACK CMyDialog::enumwndfn(HWND hWnd, LPARAM lParam)
{
    CMyDialog * me = (CMyDialog *)lParam;
    return me->enumwndfn(CWnd::FromHandle(hWnd);
}

For the nonstatic member, you now have direct access to the objects of the class, for example, a list box c_Windows:

BOOL CMyDialog::enumwndfn(CWnd * wnd)
{
    // full access to class here
    CString s;
    wnd->GetWindowText(s);
    c_Windows.AddString(s);
}

which clearly is more convenient for MFC programming. 

So, you say, "But I really use LPARAM to pass information in! Now what do I do?" The answer is straightforward. The simple answer is: store the information you used to pass via LPARAM in an instance variable of the class. Instead of accessing the lParam variable, you can instead write code as follows:

enumParam = whatever;
EnumWindows(enumwndfn, this);
BOOL CMyDialog::enumwndfn(CWnd * wnd)
{
    if(enumParam == whatever) ...
}

Some of you will now say "Aha! Got you! That technique isn't thread-safe if I have multiple threads doing the enumeration!". Indeed, that is so. So there is a slightly more complex solution.

typedef struct EnumParm {CMyDialog * me; LPARAM lParam;}

then write

EnumParam p;
p.me = this;
p.lParam = whatever;
EnumWindows(enumwndfn, (LPARAM)&p);

and rewrite the handlers to be

BOOL CMyDialog::enumwndfn(HWND hWnd, LPARAM lParam)
{
    EnumParm * p = (EnumParm *)lParam;
    return p->me->enumwndfn(CWnd::FromHandle(hWnd),
                                  p->lParam);
}
BOOL CMyDialog::enumwndfn(CWnd * wnd, LPARAM lParam)
{
    // enumeration handler code here
}

and the problem is solved.


Worker Threads in a class

I use this same technique when using worker threads. I often want the worker thread to operate as a collection of MFC code in the context of my class, although it is not a GUI-based thread. The same casting techniques apply:

static UINT threading(LPVOID p);
void threading();

To start a thread, call

    AfxBeginThread(threading, this);

and the code is

UINT CMyClass::threading(LPVOID p)
{
    CMyClass * me = (CMyClass *)p;
    me->threading();
    return 0;
}
void CMyClass::threading()
{
    // complete class instance access available
}

Summary

Using the raw API for callbacks does not require that you leave the MFC domain and start "hand coding" everything. It is generally easy to switch between the domains, as this essay shows.


The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this article.
Copyright © 1999 <!--webbot bot="Substitution" s-variable="CompanyLongName" startspan -->CompanyLongName<!--webbot bot="Substitution" endspan i-checksum="22147" --> All Rights Reserved.
www.flounder.com/mvp_tips.htm

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Joseph M. Newcomer

United States United States
No Biography provided

Comments and Discussions

 
QuestionI get access violation PinmemberDror Yona4-Mar-09 23:00 
AnswerRe: I get access violation PinmemberJoseph M. Newcomer6-Mar-09 6:06 
QuestionHow to end the thread for the problem below Pinmembermailtochandra2000@yahoo.com14-Nov-06 22:08 
GeneralEnumwindows Pinmembercaykahve31-Aug-05 21:23 
GeneralEnumwindow and callback in c++ Pinmemberarssa202030-Jul-04 7:11 
Hi all,
I would like to send a message to all open Internet Explorer Windows to change thier title (and later, send a string to a text box in that IE window --clicked by a user--
i know that EnumWindows This function enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function.
in my app i did the following,
 

//Global Variable
HWND handleback=0;
 
BOOL onefunc(HWND);
static BOOL CALLBACK myfunc(HWND Handle,LPARAM lParam)
{
return onefunc(Handle);
}
 
BOOL onefunc(HWND Handle)
{
char array [256];
//HWND hWndIE;
if (Handle > 0)
{
//its valid
//PostMessage(hWndIE ,WM_KEYDOWN,/(WPARAM)Buffer'A',0);
handleback=Handle;
GetWindowText(handleback,array, 256);
//get the window ttile,
if(strstr(array,"Microsoft Internet Explorer")!=NULL)
{
SetWindowText(handleback,"Mywindow1");
//i also want to send character to a user
//clicked textbox in the IE window
PostMessage(handleback,WM_KEYDOWN,'A',0);

}
return true ;
}
}
 
//in main
EnumWindows(&myfunc,NULL);
 
>>However, this doesnt seem to work, it gets stuck (calling myfunc and onefunc repeatidly.
how can i implement this? what is wrong with my CALLBACK function?
 
Regards,
Mo
marabo82@hotmail.com
GeneralRe: Enumwindow and callback in c++ PinmemberJoseph M. Newcomer14-Aug-04 17:29 
GeneralNeed help PinmemberSilverBrain18-Sep-03 15:19 
GeneralRe: Need help PinmemberJoseph M. Newcomer18-Sep-03 17:31 
GeneralGetting error trying to do very similar thing Pinmembermellertson31-Jul-03 13:43 
GeneralRe: Getting error trying to do very similar thing PinmemberJoseph M. Newcomer31-Jul-03 21:27 
GeneralRe: Getting error trying to do very similar thing Pinmembermellertson1-Aug-03 19:21 
GeneralInside CMyDialog Pinmemberfrisco29-May-02 6:33 
GeneralThread function in a Class Pinmemberkim547219-Dec-01 4:35 
GeneralRe: Thread function in a Class PinmemberJoseph M. Newcomer19-Dec-01 6:17 
GeneralYou can't pass object ptr to thread Pinmemberspringscrewer29-Apr-01 18:31 
GeneralRe: You can't pass object ptr to thread PinmemberJoseph M. Newcomer29-Apr-01 19:13 
QuestionBut when your callback does not have LPARAM? PinmemberBradster27-Feb-01 9:49 
AnswerRe: But when your callback does not have LPARAM? Pinmemberdylan15-Mar-01 16:49 
GeneralRe: But when your callback does not have LPARAM? PinmemberJoseph M. Newcomer30-Mar-01 6:03 
GeneralRe: But when your callback does not have LPARAM? Pinmemberrainforestor@gmail.com28-Mar-07 4:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 17 May 2000
Article Copyright 2000 by Joseph M. Newcomer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid