首页
社区
课程
招聘
[求助]关于MFC中CFrameWnd,CWnd的一点疑问。。
发表于: 2011-12-22 17:38 6901

[求助]关于MFC中CFrameWnd,CWnd的一点疑问。。

2011-12-22 17:38
6901
描述
我借助没有文档视图结构的MFC来实现简单的窗口程序HELLO WORLD。
思路:从MFC的窗口类中派生出一个我自己的类CMainWindow,然后调用MFC系统类提供的创建窗口的函数创建窗口,然后添加消息映射机制,实现消息处理。
在选择从哪个MFC的窗口类派生自己的窗口类时思路不是很清晰,所以2个都进行了测试。请大家耐心点看吧。
情况一:
.h
class CMyApp : public CWinApp
{
public:
    virtual BOOL InitInstance ();
};

class CMainWindow : public [COLOR="Red"]CFrameWnd//直接从CFrameWnd类派生[/COLOR]{
public:
    CMainWindow ();

protected:
    afx_msg void OnPaint ();
    DECLARE_MESSAGE_MAP ()
};

Hello.cpp
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_PAINT ()
END_MESSAGE_MAP ()

CMainWindow::CMainWindow ():m_ptCaretPos(0,0)
{
    [COLOR="red"]Create (NULL, _T ("The Hello Application"));//创建窗口,指定窗口类名为NULL[/COLOR]
}

情况二:
.h
class CMainWindow [COLOR="red"]: public CWnd//从CWnd派生[/COLOR],请考虑如何创建窗口?

.cpp
CMainWindow::CMainWindow ()
{
。。。。。
[COLOR="red"]CreateEx (0, NULL, _T ("Hello World"),[/COLOR]
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL);//请注意第二个参数(NULL)

情况一没任何问题,情况二运行出错,查了MSDN说
CWnd::CreateEx的第二个参数不能为NULL.
跟踪结果分析:
情况一之所以能够成功,在于Create函数的第一个参数为NULL,表示使用系统帮忙注册的窗口类去创建窗口,系统是啥时候帮我们注册的窗口类,是这样的,Create会调用基类CWnd::CreateEx,它又继续调用PreCreateWindow函数(注意虚函数),就是在这里进行的注册窗口类AfxDeferRegisterClass。注册的窗口类叫啥名字呢?经过调试是叫“AfxFrameOrView42d”的窗口类名。继续跟踪MFC发现此时执行的是下面的代码:
	if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
	{
		// SDI Frame or MDI Child windows or views - normal colors
		wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
		if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
			fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
	}

不晓得AFX_WNDFRAMEORVIEW_REG是啥意思,这段代码的含义其实不是很懂。请懂的童鞋帮忙解释一下,感激涕零。。
情况二:
执行顺序CWnd::CreateEx----CWnd::PreCreateWindow--------这里有必要贴下代码
// for child windows
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
	if (cs.lpszClass == NULL)
	{
		// make sure the default window class is registered
		[COLOR="red"]VERIFY(AfxDeferRegisterClass(AFX_WND_REG));[/COLOR]

		// no WNDCLASS provided - use child window default
		ASSERT(cs.style & WS_CHILD);
		cs.lpszClass = _afxWnd;
	}
	return TRUE;
}

继续执行到达 AfxEndDeferRegisterClass,也就是进入了注册窗口类的函数当中,发现执行的是下面的代码:
	// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
	[COLOR="red"]if (fToRegister & AFX_WND_REG)[/COLOR]
	{
		// Child windows - no brush, no icon, safest default class styles
		wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpszClassName = _afxWnd;
		if (AfxRegisterClass(&wndcls))
			fRegisteredClasses |= AFX_WND_REG;
	}

情况一出现的是AFX_WNDFRAMEORVIEW_REG,情况二则是AFX_WND_REG.请问大家这两个是啥东西呢。网上也没找到相对应的说明。
OK,继续执行这个函数到if (AfxRegisterClass(&wndcls)),
然后跳转到:
// for child windows
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
	if (cs.lpszClass == NULL)
	{
		// make sure the default window class is registered
		VERIFY(AfxDeferRegisterClass(AFX_WND_REG));

		// no WNDCLASS provided - use child window default
		ASSERT(cs.style & WS_CHILD);
		cs.lpszClass = _afxWnd;
	}
	return TRUE;
}

到这里ASSERT(cs.style & WS_CHILD);此断言错误导致崩溃不能继续。断言需要WS_CHILD,但是没有出现,所以失败,然后进入断言失败的函数中。。。。。。
到了这里,我再次把WS_CHILD添加到了窗口风格中进行测试:
    CreateEx (0, NULL, _T ("Tic-Tac-Toe"),
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX|[COLOR="Red"]WS_CHILD[/COLOR],
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL);

然后ASSERT(cs.style & WS_CHILD);这里没问题了,继续跟踪到这里再次崩溃
DWORD CWnd::GetExStyle() const
{
	[COLOR="red"]ASSERT(::IsWindow(m_hWnd));//这里的断言导致崩溃[/COLOR]

	if (m_pCtrlSite == NULL)
		return (DWORD)GetWindowLong(m_hWnd, GWL_EXSTYLE);
	else
		return m_pCtrlSite->GetExStyle();
}

所以我认为是创建窗口失败,因为必定是hWnd=::CreateWindowEx(API)创建的,然后m_hWnd=hWnd;进行赋值的吧。
疑问一:到底是哪里出错了呢?为什么hWnd为空呢?为啥传递NULL就运行出错不能创建窗口呢?系统不是默认也帮我们注册了叫 AFX_WND_REG窗口类吗?(我不确实我的叫法是否对)
疑问二:大家告诉我m_hWnd=hWnd;到底是在什么时候赋值的呢?我该如何去跟踪呢?我的想法是在Detach的时候,但是在那里打了断点,压根就没到达啊,请问这里是啥情况?

谢谢大家,小弟对MFC的流程非常感兴趣,也跟踪了好久,遇到比较简单的问题也都解决了很多,但是唯独到了这里就进行不下去了,请大家帮忙。非常感谢!

[课程]Linux pwn 探索篇!

收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 33
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
你create的这个窗口没有父窗口吧?在precreatewindow里面首先判断了class是否为空,如果空,则判断是否是子窗口,如果不是则抛出断言。如果你要创建的确实是子窗口,msdn上说应该在dwexstyle里面指定。
2011-12-22 21:01
0
雪    币: 132
活跃值: (214)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
3
感谢你的回答,你说的这些能贴出MSDN中的描述信息吗,我的MSDN中没有这些?
然后按照你说的我把WS_CHILD放到了扩展样式dwexstyle中
    CreateEx (WS_CHILD, NULL, _T ("Tic-Tac-Toe"),
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL);
运行还是崩溃。。。
2011-12-23 00:30
0
雪    币: 132
活跃值: (214)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
4
http://topic.csdn.net/u/20111222/19/1dc0ad66-15f3-46a4-a2a2-1788bb04b3ed.html?22199
算是OK了。
2011-12-25 14:26
0
游客
登录 | 注册 方可回帖
返回
//