如何自定义MFC的窗口类名

来源:互联网 发布:python 脚本语言 编辑:程序博客网 时间:2024/06/05 02:26
作者:沧桑不尽,请尊重原创,转载注明。
MFC中封装很多常用的控件,把类名也给封装了,没有提供明显的接口出来,用win api写窗口程序,第一步就是注册窗口类

此时类名和标题名是一起注册的,所以能把标题很好地让用户来设定,类名也应该是很简单的,可惜的是MFC没有这样做,原因也许是window name可以不停的改,而类名不能。窗口的类名是有Create来确定的,要在Create前,给窗口选择一个已经注册的窗口类名,作为参数窗口Create就ok了,CWnd的Create最终还是到了CreateEx中来,看看CreateEx就会清楚许多

 

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
LPVOID lpParam
/* = NULL */)
{
return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right
- rect.left, rect.bottom - rect.top,
pParentWnd
->GetSafeHwnd(), (HMENU)(UINT_PTR)nID, lpParam);
}

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
ASSERT(lpszClassName
== NULL || AfxIsValidString(lpszClassName) ||
AfxIsValidAtom(lpszClassName));
ENSURE_ARG(lpszWindowName
== NULL || AfxIsValidString(lpszWindowName));

// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle
= dwExStyle;
cs.lpszClass
= lpszClassName;
cs.lpszName
= lpszWindowName;
cs.style
= dwStyle;
cs.x
= x;
cs.y
= y;
cs.cx
= nWidth;
cs.cy
= nHeight;
cs.hwndParent
= hWndParent;
cs.hMenu
= nIDorHMenu;
cs.hInstance
= AfxGetInstanceHandle();
cs.lpCreateParams
= lpParam;

if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}

AfxHookWindowCreate(
this);
HWND hWnd
= ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

#ifdef _DEBUG
if (hWnd == NULL)
{
TRACE(traceAppMsg,
0, "Warning: Window creation failed: GetLastError returns 0x%8.8X/n",
GetLastError());
}
#endif

if (!AfxUnhookWindowCreate())
PostNcDestroy();
// cleanup if CreateWindowEx fails too soon

if (hWnd == NULL)
return FALSE;
ASSERT(hWnd
== m_hWnd); // should have been set in send msg hook
return TRUE;
}

可以看到最后到了::AfxCtxCreateWindowEx,可以很容易地知道这里调用了CreateWindowEx来创建一个窗口

 

在前面有一个PreCreateWindow(cs),而cs经过PreCreateWindow处理后,交给::AfxCtxCreateWindowEx处理

::AfxCtxCreateWindowEx在中转给CreateWindowEx,cs.lpszClass就是类名,可以清楚了AfxCtxCreateWindowEx的用心良苦


我们可以重载的PreCreateWindow,来修改类名,如下的代码:

 

// TODO: 在此添加专用代码和/或调用基类
//VERIFY(AfxDeferRegisterClass(AFX_WND_REG));
//AfxEndDeferRegisterClass(AFX_WND_REG);
//cs.lpszClass = AfxRegisterWndClass(NULL);
WNDCLASS wndcls;

memset(
&wndcls, 0, sizeof(WNDCLASS)); // start with NULL
// defaults

wndcls.style
= CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

//you can specify your own window procedure
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.hInstance
= AfxGetInstanceHandle();
wndcls.hIcon
= NULL; // or load a different icon
wndcls.hCursor =NULL;
wndcls.hbrBackground
= (HBRUSH) (COLOR_WINDOW + 1);
wndcls.lpszMenuName
= NULL;

// Specify your own class name for using FindWindow later
wndcls.lpszClassName = _T("MyNewClass");

// Register the new class and exit if it fails
if(!AfxRegisterClass(&wndcls))
{
TRACE(
"Class Registration Failed/n");
return FALSE;
}


cs.lpszClass
= wndcls.lpszClassName;

return TRUE;
//return CWnd::PreCreateWindow(cs);

 

其实就是为了把一个已经注册的类名字符串传给CreateWindowEx,从上面代码中的注释中来看,我还用了一种让系统来生成className的方法AfxRegisterWndClass。CWnd::PreCreateWindow不符合我的心意,注释掉了,其实里面也没什么就是判断而已。而在MFC中CWnd其他派生类就不这么简单了,不过单纯的修改类名,就重载这个方法大多就ok了。

是的,只是大多数可以的,可惜的是这个方法,对于Dialog来说并不行,因为它不用CWnd::Create,也就绕不到

PreCreateWindow上来了,你可以重载对话框的这个方法,断点,是断不下来的。因为CDialog的创建可以直接用系统的api来搞,不用再劳驾CWnd来中转到CReateWindowEx了,所以就不能够用上述方法来改对话框的类名了。

看下它的创建飞鸽传书代码了:

 

BOOL CDialog::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd)
{
ASSERT(IS_INTRESOURCE(lpszTemplateName)
||
AfxIsValidString(lpszTemplateName));

m_lpszTemplateName
= lpszTemplateName; // used for help
if (IS_INTRESOURCE(m_lpszTemplateName) && m_nIDHelp == 0)
m_nIDHelp
= LOWORD((DWORD_PTR)m_lpszTemplateName);

#ifdef _DEBUG
if (!_AfxCheckDialogTemplate(lpszTemplateName, FALSE))
{
ASSERT(FALSE);
// invalid dialog template name
PostNcDestroy(); // cleanup if Create fails too soon
return FALSE;
}
#endif //_DEBUG

HINSTANCE hInst
= AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
HRSRC hResource
= ::FindResource(hInst, lpszTemplateName, RT_DIALOG);
HGLOBAL hTemplate
= LoadResource(hInst, hResource);
BOOL bResult
= CreateIndirect(hTemplate, pParentWnd, hInst);
FreeResource(hTemplate);

return bResult;
}

 

 

可以看出来CDialog是靠资源来创建的,可以这样来来搞的,在资源脚本中定义Class “对话框类名”在对话框domodal或者Create前注册这个类名,然后等着modal和Create后就可以了。

 

 

这段时间,一直忙一些其他方面了,对MFC封装机制淡忘了不少,跟踪下代码,算是温习一下了。

原创粉丝点击