News | Articles | Libraries | Developer Tools | Books | Forum Links | Search   
Sections:
 

QA: Why does MFC add "WCE_" prefix to my classes?

By Alexander Shargin, January 14, 2003.
Print version

Question

I created a window of my custom class using MFC and noticed that the class name of the window got "WCE_" prefix. Why was it added? How can I get rid of it?

Answer

Background

"WCE_" prefix is added by MFC library. This happens in wce_PreCreateWindow function (called by CWnd::CreateEx) just prior to the window creation:

#define WCE_CLASS_PREFIX _T("WCE_") // Register a "hybrid" class (from the current one), substituting a new // ClassName and WndProc. This ensures that the first message is sent // to wce_FirstDefWindowProc. // NOTE: It is okay to "own" the ThreadState here because the baseline does // something similar in AfxHookWindowCreate() with // pThreadState->m_pWndInit. BOOL AFXAPI wce_PreCreateWindow( CREATESTRUCT& cs) { // Standalone menus are not supported in ::CreateWindowEx, so we // need to filter it out. // We'll cache it later in wce_PostCreateWindow. // If it is a control ID, then its HIWORD == NULL if(HIWORD(cs.hMenu) != NULL) cs.hMenu = NULL; _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); // Get the old class. If it doesn't exist, then return FALSE. WNDCLASS wndclass; if (!GetClassInfo( cs.hInstance, cs.lpszClass, &wndclass)) return FALSE; // Don't do anything with classes that begin with "Afx." The // wce_FirstDefWindowProc is already set for those in // AfxRegisterClass() and AfxEndDeferRegisterClass() if(_tcsnicmp(cs.lpszClass,_T("Afx"),3) == 0) { pThreadState->m_pOldProc = ::DefWindowProc; // We leave cs.lpszClass alone return TRUE; // don't do anything on MFC windows } // Compose a new class name (e.g. "WCE_oldname"). We account for both // ways new might fail (exceptions vs. NULL ptr) TRY { wndclass.lpszClassName = new TCHAR[_tcslen(cs.lpszClass) + _tcslen(WCE_CLASS_PREFIX) + 1]; } CATCH_ALL(e) { return FALSE; } END_CATCH_ALL if(wndclass.lpszClassName == NULL) return FALSE; _tcscpy( (TCHAR*)wndclass.lpszClassName, WCE_CLASS_PREFIX ); _tcscat( (TCHAR*)wndclass.lpszClassName, cs.lpszClass ); // Change the window procedure, saving the old one. WNDPROC OldProc = wndclass.lpfnWndProc; wndclass.lpfnWndProc = (WNDPROC)wce_FirstDefWindowProc; // Register the new class (if not already registered) if (!AfxRegisterClass(&wndclass)) { delete[] (void*)wndclass.lpszClassName; return FALSE; } // If successful, commit our change to pThreadState and cs. pThreadState->m_pOldProc = OldProc; cs.lpszClass = wndclass.lpszClassName; // Note: cs.lpszClass will be deleted in wce_PostCreateWindow return TRUE; }

Why MFC is doing this? The matter is that when the window is created using CreateWindow(Ex) API it recieves some messages (WM_CREATE etc.) before CreateWindow returns. Normally these messages get to the window proc and MFC class associated with the window doesn't have a chance to handle them. But MFC wants to give you a chance to do this. So it needs to subclass the window and assign AfxWndProc window proc to it BEFORE CreateWindow returns.

There are only 2 ways to do this. First way is to use Win32 hooks. Hooks are used in the desktop version of MFC. But on CE the hooks are not supported, so the developers from Microsoft had to use the other way. This way is to change the window proc associated with the window class to wce_FirstDefWindowProc. This proc is called only once. It emulates CBT hook and then assigns AfxWndProc to the window. You can find its implementation in wcealt.cpp file from MFC sources.

But there is one problem with this approach. Win32 API doesn't allow one to change window proc of an existing window class. So MFC registers a new one. This new class is exactly the same as the original one but has another window proc and "WCE_" prefix in its name. This is exactly what MFC does in the wce_PreCreateWindow function I showed above.

Note: from the code above it is obvious that window classes with "Afx" prefix are not reregistered with "WCE_" prefix. Such classes are internally registered by MFC and have AfxWndProc assigned to them by default.

Problems

So the trick with early subclassing works, but has some unpleasant side-effects. For example, when you create a window of your own class and then try to search for it (using FindWindow) by the name of your class (e. g. from another instance of your app to activate the first instance), you will surprised to find that the window with such class name can't be found. If you use SIPPREF control to provide smart SIP handling in your dialogs, it will not work with edit boxes created dynamically. When the SIPREF control is created it searches for all edit boxes in the dialog box. Naturally it searches them by the window class ("EDIT" for edit boxes). But if you create the edit box dynamizally using MFC class CEdit, the control gets "WCE_EDIT" class name. Oops!

Solutions

In some cases you will not want to change this MFC behaviour. All you need to know is that "WCE_" prefix is appended to your class names so you can just use these wce-prefixed names to work with your window. For example, if you need to find the window by its window class, specify wce-prefixed name in the search criteria:

HWND hWnd = ::FindWindow(_T("WCE_MyWndClass"), NULL);

Next, you can use a class name with "Afx" prefix to make MFC believe this class is registered by it internally and it is not required to change it.

Finally, you can create the window using "pure" Win32 API calls and then attach the created window to the MFC class. Thus, to create an edit box programmatically and have its class name unchanged (in order that SIPPREF could recognize it), you can use this code:

// Create edit control dynamically using CreateWindow API. HWND hWnd = ::CreateWindow( _T("EDIT"), _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, rect.left, rect.top, rect.right, rect.bottom, GetSafeHwnd(), (HMENU)IDC_DYNEDIT, AfxGetInstanceHandle(), NULL); // m_wndEdit is an object of CEdit class. Attach it to the control. m_wndEdit.SubclassWindow(hWnd);

Note: if you prevent MFC from doing its wce_PreCreateWindow tricks you will not be able to handle WM_CREATE and other initialization messages through MFC message maps.

Related resources:

Discuss

Discuss this article. Here you can write your comments and read comments of other developers.
Rate this article:     Poor Excellent    
 12345 
© 2001-2005 Pocket PC Developer Network, a division of Spb Software House