关于MFC中CFrameWnd,CWnd的一点疑问。。

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

代码:
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance ();
};

class CMainWindow : public CFrameWnd//直接从CFrameWnd类派生{
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)
{
Create (NULL, _T ("The Hello Application"));//创建窗口,指定窗口类名为NULL
}
情况二:
.h

代码:
class CMainWindow : public CWnd//从CWnd派生,请考虑如何创建窗口?
.cpp

代码:
CMainWindow::CMainWindow ()
{
。。。。。
CreateEx (0, NULL, _T ("Hello World"),
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
VERIFY(AfxDeferRegisterClass(AFX_WND_REG));

// 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
if (fToRegister & AFX_WND_REG)
{
// 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|WS_CHILD,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL);
然后ASSERT(cs.style & WS_CHILD);这里没问题了,继续跟踪到这里再次崩溃

代码:
DWORD CWnd::GetExStyle() const
{
ASSERT(::IsWindow(m_hWnd));//这里的断言导致崩溃

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的流程非常感兴趣,也跟踪了好久,遇到比较简单的问题也都解决了很多,但是唯独到了这里就进行不下去了,请大家帮忙。非常感谢!
...全文
1084 52 打赏 收藏 转发到动态 举报
写回复
用AI写文章
52 条回复
切换为时间正序
请发表友善的回复…
发表回复
yiruirui0507 2012-01-09
  • 打赏
  • 举报
回复
#ifndef USE_NEW
CMainFrame cMyWnd;//
MSG msg;
//m_pMainWnd=&cMyWnd;
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return FALSE;

测试发现,这样确实可以,在返回之前加个消息循环,用return false来去掉pThread->Run();的执行。。。。。虽说看起来有点古怪吧,但起码不用m_pMainWnd了。。这种写法跟MFC的实现区别在哪里呢?总感觉很怪怪的哦。
自己的分析:消息队列是针对线程的而非针对窗口,每个线程维护一个消息队列,这个线程也刚好(在本例中)就是主线程,而CMyApp代表应用程序类,其实线程的消息队列就是他来维护:
1 MFC的做法,他利用一个m_pMainWnd来指向线程的主窗口,当这个窗口为NULL,他就结束线程。
2 我们的做法,不需要定义一个变量来指向某个窗口,而是把消息循环写在主线程中(SDK就是这样吧)。
xzj1987125 2012-01-09
  • 打赏
  • 举报
回复
新手同问,到底要不要追究该类问题。请人推荐书目,大家都对《深入浅出MFC》很推崇。《深入浅出MFC》就是大多在讲MFC的来龙去脉。大家是怎么看待是否需要剖析MFC深层次的内容的?
schlafenhamster 2012-01-09
  • 打赏
  • 举报
回复
可以2种窗口:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{//
pWnd=new CWnd;//
#ifdef CREATE_CHILD//
pWnd->Create(NULL, "", WS_SYSMENU | WS_CHILD | WS_VISIBLE | WS_BORDER,
CRect(10, 50, 280, 350), this, 2345);
#else // POPUP
pWnd->CreateEx(0, AfxRegisterWndClass(0, theApp.LoadStandardCursor(IDC_ARROW),(HBRUSH)GetStockObject(WHITE_BRUSH)),
_T("A CWnd"), WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU, 100, 150, 300, 300, *this, NULL);
// background
#endif
yiruirui0507 2012-01-06
  • 打赏
  • 举报
回复
[Quote=引用 45 楼 schlafenhamster 的回复:]
我给你的代码是在我机器上(XP)运行正常的,而且点关闭按钮,[color=#FF0000]没有异常出现/color]。
再给你拷一次,不需要。h文件:

C/C++ code


// 本程序只是测试,说明堆和栈产生对象的不同。
#include <afxwin.h>

class CMyApp : public CWinApp
{
public:
virtual ……
[/Quote]
#define USE_NEW
把这句注释掉:还是一闪而过。
schlafenhamster 2012-01-06
  • 打赏
  • 举报
回复
这回真好了:
// 本程序只是测试,说明堆和栈产生对象的不同。
#include <afxwin.h>

//#define USE_NEW

class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance ();
};

class CMainFrame : public CFrameWnd
{
public:
CMainFrame ();
~CMainFrame();
#ifndef USE_NEW
virtual void PostNcDestroy();
#endif
CPoint m_ptCaretPos;
CStatic m_wndPushButton;
CWnd* pWnd;

protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint ();
afx_msg void OnSetFocus();
afx_msg void OnKillFocus();
DECLARE_MESSAGE_MAP ()
};
///////

CMyApp theApp;


///////////////////////////////////////////////////////////
BOOL CMyApp::InitInstance ()
{
#ifndef USE_NEW
CMainFrame cMyWnd;//
MSG msg;
//m_pMainWnd=&cMyWnd;
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return FALSE;
#else
m_pMainWnd = new CMainFrame;
m_pMainWnd->ShowWindow (m_nCmdShow);
m_pMainWnd->UpdateWindow ();
return TRUE;
#endif
}

/////////////////////////////////////////////////////////////////////////
// CMainFrame message map and member functions

BEGIN_MESSAGE_MAP (CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT ()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
END_MESSAGE_MAP ()

CMainFrame::CMainFrame ():m_ptCaretPos(0)
{
Create(NULL, _T ("The Hello Application"));
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
pWnd=new CWnd;//WS_BORDER |
pWnd->Create(NULL, _T("A CWnd"), WS_SYSMENU | WS_CHILD | WS_CAPTION | WS_VISIBLE, CRect(40, 50, 300, 350), this, 0);//x11);
ASSERT(pWnd->GetSafeHwnd());

m_wndPushButton.Create(_T("STATIC"), WS_CHILD | WS_VISIBLE,CRect(400, 80, 500, 180), this, 1234);
//
return 1;
}

void CMainFrame::OnPaint ()
{
CPaintDC dc (this);

CRect rect;
GetClientRect (&rect);

dc.DrawText (_T ("CMainFrame:Hello, MFC"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
dc.SetTextAlign(TA_CENTER|TA_BOTTOM);
// dc.TextOut(300,40,_T("YIRUIRUI"));
}

void CMainFrame::OnSetFocus()
{
CreateSolidCaret(12,35);
SetCaretPos(m_ptCaretPos);
ShowCaret();
}
void CMainFrame::OnKillFocus()
{
HideCaret();
m_ptCaretPos=GetCaretPos();
::DestroyCaret();
}
//CFrameWnd的PostNcDestroy()会使用 delete this !
#ifndef USE_NEW
void CMainFrame::PostNcDestroy()
{
if(pWnd->GetSafeHwnd())
{
pWnd->DestroyWindow();
}
delete pWnd;
pWnd=NULL;
//
DestroyWindow();
PostQuitMessage(0);

}
//
CMainFrame::~CMainFrame()
{
}
#else
//
CMainFrame::~CMainFrame()
{
if(pWnd->GetSafeHwnd())
{
pWnd->DestroyWindow();
}
delete pWnd;
pWnd=NULL;
//在析构函数中销毁在堆上分配的内存空间
}
#endif
schlafenhamster 2012-01-06
  • 打赏
  • 举报
回复
其实那个Run()就是:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
你改改试试。
不过程序退不出来了。
schlafenhamster 2012-01-06
  • 打赏
  • 举报
回复
我搞错了,
#define USE_NEW
没注释掉,我再试试。
yiruirui0507 2012-01-04
  • 打赏
  • 举报
回复
[Quote=引用 43 楼 schlafenhamster 的回复:]
1》。CMainFrame 不是MFC的类。
2》。:
ULONG kk=0;
while (kk< 1000000000)
{//请在这段时间内退出。
Run();//Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.
kk++;
}
以上几句有点古怪,改成:
CMainFrame cMyWnd……
[/Quote]

ULONG kk=0;
while (kk< 1000000000)
{//请在这段时间内退出。
Run();//Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.
kk++;
}
运行崩溃(VC6),改成while(Run()) return false; 一闪而过。
还是一样的不能避开m_pMainWnd,主要这个变量是来自CWinThread跟主线程相关的,因此才规定指向主线程对应的那个主窗口,看来微软就是故意这么设计的。
CWinThread::m_pMainWnd
Remarks

Use this data member to store a pointer to your thread’s main window object. The Microsoft Foundation Class Library will automatically terminate your thread when the window referred to by m_pMainWnd is closed. If this thread is the primary thread for an application, the application will also be terminated. If this data member is NULL, the main window for the application’s CWinApp object will be used to determine when to terminate the thread. m_pMainWnd is a public variable of type CWnd*.

Typically, you set this member variable when you override InitInstance. In a worker thread, the value of this data member is inherited from its parent thread.

schlafenhamster 2012-01-04
  • 打赏
  • 举报
回复
我给你的代码是在我机器上(XP)运行正常的,而且点关闭按钮,[color=#FF0000]没有异常出现/color]。
再给你拷一次,不需要。h文件:

// 本程序只是测试,说明堆和栈产生对象的不同。
#include <afxwin.h>

class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance ();
};

class CMainFrame : public CFrameWnd
{
public:
CMainFrame ();
~CMainFrame();
CPoint m_ptCaretPos;
CStatic m_wndPushButton;
CWnd* pWnd;

protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint ();
afx_msg void OnSetFocus();
afx_msg void OnKillFocus();
DECLARE_MESSAGE_MAP ()
};
///////




CMyApp theApp;


#define USE_NEW

///////////////////////////////////////////////////////////
BOOL CMyApp::InitInstance ()
{
#ifndef USE_NEW
CMainFrame cMyWnd;//
//m_pMainWnd=&cMyWnd;
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
while (Run());
return FALSE;
#else
m_pMainWnd = new CMainFrame;
m_pMainWnd->ShowWindow (m_nCmdShow);
m_pMainWnd->UpdateWindow ();
return TRUE;
#endif
}

/////////////////////////////////////////////////////////////////////////
// CMainFrame message map and member functions

BEGIN_MESSAGE_MAP (CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT ()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
END_MESSAGE_MAP ()

CMainFrame::CMainFrame ():m_ptCaretPos(0)
{
Create(NULL, _T ("The Hello Application"));
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
pWnd=new CWnd;//WS_BORDER |
pWnd->Create(NULL, _T("A CWnd"), WS_SYSMENU | WS_CHILD | WS_CAPTION | WS_VISIBLE, CRect(40, 50, 300, 350), this, 0);//x11);
ASSERT(pWnd->GetSafeHwnd());

m_wndPushButton.Create(_T("STATIC"), WS_CHILD | WS_VISIBLE,CRect(400, 80, 500, 180), this, 1234);
//
return 1;
}
void CMainFrame::OnPaint ()
{
CPaintDC dc (this);

CRect rect;
GetClientRect (&rect);

dc.DrawText (_T ("CMainFrame:Hello, MFC"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
dc.SetTextAlign(TA_CENTER|TA_BOTTOM);
// dc.TextOut(300,40,_T("YIRUIRUI"));
}

void CMainFrame::OnSetFocus()
{
CreateSolidCaret(12,35);
SetCaretPos(m_ptCaretPos);
ShowCaret();
}
void CMainFrame::OnKillFocus()
{
HideCaret();
m_ptCaretPos=GetCaretPos();
::DestroyCaret();
}
CMainFrame::~CMainFrame()
{
//在析构函数中销毁在堆上分配的内存空间
if(pWnd->GetSafeHwnd())
{
pWnd->DestroyWindow();
}
delete pWnd;
pWnd=NULL;
}




schlafenhamster 2012-01-02
  • 打赏
  • 举报
回复
1》。CMainFrame 不是MFC的类。
2》。:
ULONG kk=0;
while (kk< 1000000000)
{//请在这段时间内退出。
Run();//Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.
kk++;
}
以上几句有点古怪,改成:
CMainFrame cMyWnd;//
//m_pMainWnd=&cMyWnd;
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
while (Run());
return FALSE;
3》。用replace 把头文件和Cpp中的CMainWindow全改为CMainFrame。
schlafenhamster 2012-01-02
  • 打赏
  • 举报
回复
"你的CMainFrame用的是MFC提供的。。。不是我的CMainWindow的"!
就是你的CMainWindow只不过改了一个名,不认得了?
改为CMainFrame好理解些。
你运行一下我给你程序,比较一下你原来的。
yiruirui0507 2012-01-01
  • 打赏
  • 举报
回复
[Quote=引用 40 楼 schlafenhamster 的回复:]
整理后的程序:
#include <afxwin.h>
#include "w32cwnd.h"

CMyApp theApp;


#define USE_NEW

///////////////////////////////////////////////////////////
BOOL CMyApp::InitInstance ()
{
#ifndef USE_N……
[/Quote]
你的CMainFrame用的是MFC提供的。。。不是我的CMainWindow的,另外对于你分析的原因还不是很清楚。。
这里的“// Create(NULL, _T ("The Hello Application"));}// 2次Create
”你分析错了吧,不是这个,你看32楼33楼的问题。
schlafenhamster 2012-01-01
  • 打赏
  • 举报
回复
整理后的程序:
#include <afxwin.h>
#include "w32cwnd.h"

CMyApp theApp;


#define USE_NEW

///////////////////////////////////////////////////////////
BOOL CMyApp::InitInstance ()
{
#ifndef USE_NEW
CMainFrame cMyWnd;//
//m_pMainWnd=&cMyWnd;
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
ULONG kk=0;
while (kk< 1000000000)
{//请在这段时间内退出。
Run();//Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.
kk++;
}
return TRUE;
#else
m_pMainWnd = new CMainFrame;
m_pMainWnd->ShowWindow (m_nCmdShow);
m_pMainWnd->UpdateWindow ();
return TRUE;
#endif
}

/////////////////////////////////////////////////////////////////////////
// CMainFrame message map and member functions

BEGIN_MESSAGE_MAP (CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT ()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
END_MESSAGE_MAP ()

CMainFrame::CMainFrame ()
{
Create(NULL, _T ("The Hello Application"));
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
pWnd=new CWnd;//WS_BORDER |
pWnd->Create(NULL, _T("A CWnd"), WS_SYSMENU | WS_CHILD | WS_CAPTION | WS_VISIBLE, CRect(40, 50, 300, 350), this, 0);//x11);
ASSERT(pWnd->GetSafeHwnd());

m_wndPushButton.Create(_T("STATIC"), WS_CHILD | WS_VISIBLE,CRect(400, 80, 500, 180), this, 1234);
return 1;
}
void CMainFrame::OnPaint ()
{
CPaintDC dc (this);

CRect rect;
GetClientRect (&rect);

dc.DrawText (_T ("Hello, MFC"), -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
dc.SetTextAlign(TA_CENTER|TA_BOTTOM);
dc.TextOut(300,40,_T("YIRUIRUI"));
}

void CMainFrame::OnSetFocus()
{
CreateSolidCaret(12,35);
SetCaretPos(m_ptCaretPos);
ShowCaret();
}
void CMainFrame::OnKillFocus()
{
HideCaret();
m_ptCaretPos=GetCaretPos();
::DestroyCaret();
}
CMainFrame::~CMainFrame()
{
//在析构函数中销毁在堆上分配的内存空间
if(pWnd->GetSafeHwnd())
{
pWnd->DestroyWindow();
}
delete pWnd;
pWnd=NULL;
}
//主要就是:
CMainWindow cMyWnd;是在栈中产生的(new是在堆),InitInstance ()就不能退出,。
否则cMyWnd就不能销毁。
schlafenhamster 2012-01-01
  • 打赏
  • 举报
回复
其实是构造和析构错了:
1.CMainWindow::CMainWindow ():m_ptCaretPos(0,0)
{
// Create(NULL, _T ("The Hello Application"));}// 2次Create

CMainWindow cMyWnd;
m_pMainWnd=&cMyWnd;
cMyWnd.Create (NULL, _T ("The Hello Application"));
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
ULONG kk=0;
while (kk< 1000000000) kk++;
2.
//cMyWnd.Detach();// 引起2次析构。
CMainWindow::~CMainWindow()

schlafenhamster 2011-12-31
  • 打赏
  • 举报
回复
不行就算了,没别的办法了。
yiruirui0507 2011-12-30
  • 打赏
  • 举报
回复
[Quote=引用 36 楼 schlafenhamster 的回复:]
清除 m_pMainWnd=&cMyWnd;这句试试
[/Quote]
你终于来啦,不行的m_pMainWnd是绕不开的,必须初始化,而且还得是有效初始化。
我就是想绕过这个东西,始终不能得逞。
奋斗小青年 2011-12-30
  • 打赏
  • 举报
回复
帅哥加美女 2011-12-30
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 yiruirui0507 的回复:]
目前所能得出的结论是m_pMainWnd一定不能为NULL或指向其他无效的窗口类对象。
如下进行了测试
test 1

C/C++ code

BOOL CMyApp::InitInstance ()
{
CMainWindow cMyWnd;
m_pMainWnd=&cMyWnd;
cMyWnd.Create (NULL, _T ("The H……
[/Quote]

这个可以试试
schlafenhamster 2011-12-30
  • 打赏
  • 举报
回复
清除 m_pMainWnd=&cMyWnd;这句试试
yiruirui0507 2011-12-29
  • 打赏
  • 举报
回复
目前所能得出的结论是m_pMainWnd一定不能为NULL或指向其他无效的窗口类对象。
如下进行了测试
test 1
BOOL CMyApp::InitInstance ()
{
CMainWindow cMyWnd;
m_pMainWnd=&cMyWnd;
cMyWnd.Create (NULL, _T ("The Hello Application"));
cMyWnd.ShowWindow(m_nCmdShow);
cMyWnd.UpdateWindow();
cMyWnd.Detach();
GetLastError();
return TRUE;
}

效果:运行崩溃。估计错误原因:m_pMainWnd在此函数执行完毕之后指向了无效的窗口类对象(cMyWnd是在栈上分配的局部变量,会自动销毁)。具体哪里崩溃的现在不记得了。。。。
test2
把cMyWnd定义成全局变量,然后继续给m_pMainWnd=&cMyWnd;
效果:正常运行不崩溃。

莫非就必须用m_pMainWnd。。。。。。绕开这个不行吗?我就不喜欢在InitInstance中new 一个CFrameWnd的派生类对象不可以吗???????
加载更多回复(30)
CWnd类提供了微软基础类库所有窗口类的基本功能。 CWnd对象与Windows的窗口不同,但是两者有紧密联系。CWnd对象是由CWnd的构造函数和析构函数创建或销毁的。另一方面,Windows的窗口是Windows的一种内部数据结构,它是由CWnd的Create成员函数创建的,而由CWnd的虚拟析构函数销毁。DestroyWindow函数销毁Windows的窗口,但是不销毁对象。 CWnd类和消息映射机制隐藏了WndProc函数。接收到的Windows通知消息通过消息映射被自动发送到适当的CWnd OnMessage成员函数。你可以在派生类重载OnMessage成员函数以处理成员的特定消息。 CWnd类同时还使你能够为应用程序创建Windows的子窗口。先从CWnd继承一个类,然后在派生类加入成员变量以保存与你的应用程序有关的数据。在派生类实现消息处理成员函数和消息映射,以指定当消息被发送到窗口时应该如何动作。 你可以经过两个步骤来创建一个子窗口。首先,调用构造函数CWnd以创建一个CWnd对象,然后调用Create成员函数以创建子窗口并将它连接到CWnd对象。 当用户关闭你的子窗口时,应销毁CWnd对象,或者调用DestroyWindow成员函数以清除窗口并销毁它的数据结构。 在微软基础类库,从CWnd派生了许多其它类以提供特定的窗口类型。这些类有许多,包括CFrameWnd,CMDIFrameWnd,CMDIChildWnd,CView和CDialog,被用来进一步派生。从CWnd派生的控件类,如CButton,可以被直接使用,也可以被进一步派生出其它类来。
什么是句柄? 句柄,是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序为8字节)长的数值,来标识应用程序的不同对象和同类对象的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件的信息。如果句柄不用在I/O文件,它是毫无用处的。 句柄是Windows用来标志应用程序建立的或是使用的唯一整数,Windows使用了大量的句柄来标志很多对象。 一、MFC AppWizard 1、MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++ 类的集合,是一套面向对象的函数库,以类的方式提供给用户使用 2、MFC AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们自动生成基于MFC框架的源代码 二、基于MFC的程序框架剖析 1、MFC程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于CWinApp (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd类是MFC一个非常重要的类,它封装了与窗口相关的操作 4、MFC类的简化组织结构图(图) 5、MFC程序也有一个WinMain函数,程序是在编译时,由链接器将它链接到程序 6、MFC程序具有一个CTestApp类的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main函数之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作 8、实例句柄与全局对象 (1)对于Win32 SDK程序,应用程序的实例是由实例句柄(WinMain函数的hInstance参数)来标识的 (2)对于MFC程序,应用程序的实例是由全局对象(每一个MFC程序有且仅有一个从应用程序类CWinApp派生的类, 如CTestApp,它实例化theApp全局对象)来标识的 9、基类构造函数this指针的指向问题 在构造子类对象时,会自动调用父类的构造函数,此时在父类的构造函数的this指针所指向的是子类对象地址 10、AfxWinMain函数 MFC程序的WinMain函数是通过调用AfxWinMain函数来完成它的功能的 注:Afx前缀的函数代表应用程序框架(Application Framework)函数,它们可以在程序的任何地方被调用 11、CTestApp::InitInstance函数 在AfxWinMain函数,通过调用InitInstance函数来完成MFC内部管理方面的工作 12、AfxEndDeferRegisterClass函数 MFC提供了一些默认的标准窗口类,我们只需要选择所需的窗口类就行。然后,调用AfxEndDeferRegisterClass 函数来注册窗口类 13、CMainFrame::PreCreateWindow函数 MFC程序具有两个窗口(框架窗口和视类窗口),在框架窗口产生之前会调用PreCreateWindow函数 14、CWnd::CreateEx函数 在MFC程序,窗口的创建是由CreateEx函数实现的 15、CWnd::CreateWindowEx函数 主要作用是当修改了CreateEx函数的CREATESTRUCT参数时,CreateWindowEx函数会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:MFC后缀名为Ex的函数都是扩展函数 16、CMainFrame::ShowWindow函数和CMainFrame::UpdateWindow函数 用于显示应用程序框架窗口和更新这个窗口 17、CWinThread::Run函数和CWinThread::PumpMessage函数 用于完成消息循环 18、DefWindowProc函数 默认的窗口过程,但MFC程序对消息的处理实际上是通过消息映射机制来完成的 19、MFC程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)调用全局应用程序对象的构造函数,从而就会调用其基类CWinApp的构造函数,以完成应用程序的一些初始化 (3)进入WinMain函数 (4)进入消息循环 20、MFC程序的主要过程 theApp-> CTestApp::CTestApp构造函数-> CWinApp::CWinApp构造函数-> _tWinMain(WinMain函数的宏)-> AfxWinMain函数-> CTestApp::InitInstance函数-> AfxEndDeferRegisterClass函数-> CMainFrame::PreCreateWindow函数-> CFrameWnd::PreCreateWindow函数-> AfxDeferRegisterClass(AfxEndDeferRegisterClass函数的宏)-> CFrameWnd::Create函数-> CWnd::CreateEx函数-> CMainFrame::PreCreateWindow函数-> CWnd::CreateEx函数-> CMainFrame::ShowWindow函数-> CMainFrame::UpdateWindow函数-> CWinThread::Run函数-> CWinThread::PumpMessage函数 21、框架窗口(整个应用程序外框所包括的部分)是视类窗口(框架窗口空白的地方)的一个父窗口 22、MFC提供了一个文档/视类的结构,文档是指CDocument类,视类是指CView类。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、文档对象和视类对象是通过一个单文档模板指针来有机地组织在一起,并利用AddDocTemplate函数 把这个单文档模板添加到文档模板,从而把这三个类组织成为一个整体 24、MFC程序的CAboutDlg类继承于CDialog类,用于为用户提供一些与程序相关的帮助信息 三、窗口类、窗口类对象与窗口 1、以“::”开始的函数是一个全局函数,表示调用的是Platform SDK的函数 2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了吗? (1)当一个窗口销毁时,它会调用CWnd::DestroyWindow函数,该函数销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口类对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它 们之间的纽带仅仅在于这个C++窗口类内部的成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 的句柄 (3)但是,当C++窗口类对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口显示按钮 (1)CButton按钮类继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个按钮的话,还需调用 CButton::Create函数,把按钮窗口与CButton对象关联起来 (3)MFC程序的窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate函数来捕获。对于框架窗口来说,MFC直 接把OnCreate函数提供到了CMainFrame;而在视类窗口没有提供该函数,如需使用,要用户自行添加 (4)通常对MFC程序的操作,都是在CTestView视类窗口进行的 (5)在窗口创建之后,要显示该窗口可以通过调用ShowWindow函数或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView类,添加CButton类型的私有成员m_btn B:在CTestView类,添加WM_CREATE消息的OnCreate处理函数 C:在CTestView类,通过GetParent函数可以获得CMainFrame框架窗口对象的指针 D:实现一(在视类窗口通过ShowWindow函数显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.ShowWindow(SW_SHOWNORMAL); return 0: } E:实现二(在视类窗口通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程: 设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在MFC是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。 (2)WinMain函数体在APPMODUL.CPP文件,定义如下: extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其#define _tWinMain WinMain (3)AfxWinMain函数体在WINMAIN.CPP文件,里面有如下两句话: CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); 其实这里得到的这两个指针都是指向全局的对象theApp的; 接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类的InitInstance()函数。该函数很重要,在对该函数的调用就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。 接下来,该函数会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。 (4)进入CXXXApp::InitInstance()函数体,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类->生成窗口。 再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。 (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); (7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用 case AfxSig_vv: // normal command or control notification ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED ASSERT(pExtra == NULL); (pTarget->*mmf.pfn_COMMAND)(); (8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument(),往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_bRunEmbedded = FALSE; m_bRunAutomated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()响应)、ID_FILE_SAVE(在CDocument::OnFileSave()响应)等等在MFC向导为我们生成的单文档类找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。 (9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体会调用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表的头,也就是第一个文档模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表是否只有一项,如果链表保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实现了该函数,因此就会进入到子类的函数体了。 (10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构文档、框架、视图“三位一体”的模式,在这一个函数同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的文档类的pDocument->OnNewDocument()了。 (11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) (12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); (13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程,我们会发现还会进入到AfxEndDeferRegisterClass函数进行设计和注册窗口类,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。 (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); (15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs); (16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs) (17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类 (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) (19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) (20)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),调用if (!PreCreateWindow(cs)) ,接下来调用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到生成窗口的地方了——函数::CreateWindowEx! (21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1) (22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext); (23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1) (24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default(); (25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam); (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext)) (28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL) (29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) (30)进入BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); (31)进入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),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。 调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 写到这里,基本上就清楚了,间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。 总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的函数体,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环。 消息循环的过程是这个样子的: (1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数的pThread->Run() (2)进入int CWinApp::Run(),调用return CWinThread::Run(); (3)进入int CWinThread::Run(),调用if (!PumpMessage()) (4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int CWinThread::Run()消息循环的部分代码: do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); 这段代码其实本质上与我们基于Win32 SDK手写的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 1,寻找WinMain人口: 在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。 路径:MFC|SRC|APPMODUL.CPP: _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 注意:(#define _tWinMain WinMain) 2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。 所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){} 说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 3,通过构造应用程序对象过程调用基类CWinApp的构造函数,在CWinApp的构造函数对程序包括运行时一些初始化工作完成了。 CWinApp构造函数:MFC|SRC|APPCORE.CPP CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下: (在CWinApp类定义, CWinApp(LPCTSTR lpszAppName = NULL); ) 注意:CWinApp()函数: pThreadState->m_pCurrentWinThread = this; pModuleState->m_pCurrentWinApp = this (this指向的是派生类CTEApp对象,即theApp) 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){} 4,_tWinMain函数通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。 AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP: 在AfxWinMain()函数: CWinApp* pApp = AfxGetApp(); 说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。 //_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() // { return afxCurrentWinApp; } 调用pThread->InitInstance() 说明:pThread也指向theApp,由于基类virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。 nReturnCode = pThread->Run(); 说明:pThread->Run()完成了消息循环。 5,注册窗口类:AfxEndDeferRegisterClass(); AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...} 说明:设计窗口类:在MFC事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序 ->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow进行注册)//之后进入创建窗口阶段(以下再不做调试) 6,PreCreateWindow()://主要是注册窗口类 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; } 说明: CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册 cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background //把注册后的窗口类名赋给cs.lpszClass } if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE; if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } 其: virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。 #define VERIFY(f) ASSERT(f) #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) define AFX_WNDFRAMEORVIEW_REG 0x00008 const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件,定义为全局数组。 //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") 7,创建窗口: Create()函数路径:MFC|SRC|WINFRM.CPP: CFrameWnd::Create(...){ ... CreateEx(...);//从父类继承来的,调用CWnd::CreateEx(). ... } CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP BOOL CWnd::CreateEx(...){ ... if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。 { PostNcDestroy(); return FALSE; } ... HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。 HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCTSTR lpszName; LPCTSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT; 8,显示和更新窗口: CTEApp类,TEApp.cpp m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口 m_pMainWnd->UpdateWindow();//更新窗口 说明: class CTEApp : public CWinApp{...} class CWinApp : public CWinThread{...} class CWinThread : public CCmdTarget { ... public: CWnd* m_pMainWnd; ... ... } 9,消息循环: int AFXAPI AfxWinMain() { ... // Perform specific initializations if (!pThread->InitInstance()){...} //完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。 nReturnCode = pThread->Run(); //继承基类Run()方法,调用CWinThread::Run()来完成消息循环 ... } //////////////////////////////////////////////////////////////// CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP int CWinThread::Run() { ... // phase2: pump messages while available do//消息循环 { // pump message, but quit on WM_QUIT if (!PumpMessage())//取消息并处理 return ExitInstance(); ... } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ... } 说明: BOOL PeekMessage(,,,,)函数说明 The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure. If a message is available, the return value is nonzero. If no messages are available, the return value is zero. ///////////////////////////////////////////////////////////// BOOL CWinThread::PumpMessage() { ... if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息 {...} ... // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换 ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。) } return TRUE; } 9,文档与视结构: 可以认为View类窗口是CMainFram类窗口的子窗口。 DOCument类是文档类。 DOC-VIEW结构将数据本身与它的显示分离开。 文档类:数据的存储,加载 视类:数据的显示,修改 10,文档类,视类,框架类的有机结合: 在CTEApp类CTEApp::InitInstance()函数通过文档模板将文档类,视类,框架类的有机组织一起。 ... CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTEDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧