+ -
当前位置:首页 → 问答吧 → 关于MFC中CFrameWnd,CWnd的一点疑问。。

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

时间:2011-12-22

来源:互联网

描述
我借助没有文档视图结构的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的流程非常感兴趣,也跟踪了好久,遇到比较简单的问题也都解决了很多,但是唯独到了这里就进行不下去了,请大家帮忙。非常感谢!

作者: yiruirui0507   发布时间: 2011-12-22

可以这样
CMyFrameWnd* pFrmWnd = new CMyFrameWnd; //CMyFrameWnd从CFrameWnd派生
CCreateContext cc;
cc.m_pCurrentFrame = pFrmWnd;
VERIFY(pFrmWnd->Create(NULL, _T("我的窗口")));
cc.m_pNewViewClass = RUNTIME_CLASS(CMyView); //关联一个视图 CMyView从CView派生
pFrmWnd->CreateView(&cc);
pFrmWnd->ShowWindow(SW_SHOW);

作者: heksn   发布时间: 2011-12-22

http://yancai.web-103.com/ 

作者: liyan881226   发布时间: 2011-12-22