描述
我借助没有文档视图结构的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 探索篇!