浅谈 MFC 的子类化机制和该机制的一个应用(1)

浅谈 MFC 的子类化机制和该机制的一个应用

众所周知:

afx_msg int CWnd::OnCreate( LPCREATESTRUCT lpCreateStruct );

 

是一个经常被重载的 MFC 窗体函数,他负责处理窗体的 WM_CREATE 消息,这个消息的发送时机在窗体刚刚创建以后, CreateWindow(Ex) 返回之前。

 

可以发现在 MFC 里,系统控件和对话框也可以得到这个消息,例如 CEdit CPrintDialog CFileDialog ,他们内部调用 CreateWindowEx PrintDlg GetOpen Save FileName 完全掩盖了窗体创建的过程,这些函数返回时,窗体已经收到过 WM_CREATE 消息而且不会得到第二次通知。

 

因此,为了得到这些窗体的 WM_CREATE 通知,必须采用有点特殊的方法,能够在 CreateWindowEx 返回之前就替换掉窗体的 WindowProc

 

使用 WH_CBT 钩子是不错的选择。当一个窗体产生, CBTProc 会在 WindowProc 收到 WM_CREATE 之前得到 HCBT_CREATEWND 通知,如果此时子类化窗体,就能在子类化后的窗体过程中得到 WM_CREATE 通知。

 

MFC 的做法和这类似:

void AfxHookWindowCreate( CWnd *pWnd );

 

负责安装 WH_CBT 钩子,其参数 pWnd 指向一个创建中的 CWnd 实例, MFC 通过某种全局变量把这个实例的指针传给执行中的 CBTProc


BOOL AfxUnhookWindowCreate();

 

它卸下 WH_CBT 钩子,并且复原 AfxHookWindowCreate 改变过的 MFC 全局状态 消息。

LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam);

 

这个就是 MFC 安装的 CBTProc 回调。他只处理 HCBT_CREATEWND 通知,然后 Attach 之前得到的 CWnd 实例到正在创建的窗体句柄、替换掉窗体的 WindowProc ,最后 CallNextHookEx

 

现在来考虑如何应用 MFC 给我们提供的这个便利。 AfxHookWindowCreate AfxUnhookWindowCreate 之间创建的第一个非 IME (输入法)窗体可以被所给的 CWnd 实例子类化。我们可以这样调用一些 API ,把自己的 CWnd 实例与 API 创建的窗体连结起来,如果我给的是一个 CWnd 派生类的实例,重载过的消息就可以改变原有窗体的行为。以下的代码示例如何按照这样的思路创建一个带 Dump 输出的 MessageBox

 

class CDumpMsgBox : public CWnd

{

DECLARE_DYNAMIC(CDumpMsgBox)

 

// Constructors

public:

CDumpMsgBox();

 

// Attributes

public:

CEdit m_editDump; // Dumping edit control added to the message box.

 

CMemFile m_fileDump; // Dumping context's target file.

 

CDumpContext m_dumpContext; // The dump context object.

 

// Operations

public:

int DoMessageBox(UINT nIDPrompt, UINT nType = MB_OK, UINT nIDHelp = (UINT)-1);

 

AFX_INLINE CDumpContext& GetDumpContext() { return m_dumpContext; };

 

AFX_INLINE void RemoveAll() { m_fileDump.SetLength(0); };

 

// Implementations

public:

virtual ~CDumpMsgBox();

 

virtual void DoDumpObject(CObject* pDumpObject);

 

virtual int DoMessageBox(LPCTSTR lpszText, UINT nType = MB_OK, UINT nIDHelp = 0);

 

#ifdef _DEBUG

virtual void AssertValid() const;

 

virtual void Dump(CDumpContext& dc) const

#endif

 

protected:

virtual BOOL OnDumpOut(LPSTR pszDumpBuffer, UINT nBufferSize);

 

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CChildFrame)

//}}AFX_VIRTUAL

 

// Generated message map functions

protected:

//{{AFX_MSG(CMainFrame)

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

// NOTE - the ClassWizard will add and remove member functions here.

// DO NOT EDIT what you see in these blocks of generated code!

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

 

// Identify of the edit control CDumpMsgBox::m_editDump.

#define IDC_DUMPMSGBOX_EDITBOX 1047

 

//////////////////////////////////////////////////////////////////////

// CDumpMsgBox

 

IMPLEMENT_DYNAMIC(CDumpMsgBox, CWnd)

 

BEGIN_MESSAGE_MAP(CDumpMsgBox, CWnd)

//{{AFX_MSG_MAP(CWnd)

// NOTE - the ClassWizard will add and remove mapping macros here.

// DO NOT EDIT what you see in these blocks of generated code !

ON_WM_CREATE()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

////////////////////////////////////////////////////////////////////////////

// CDumpMsgBox construction/destruction

 

CDumpMsgBox::CDumpMsgBox() : m_fileDump(512),

m_dumpContext(&m_fileDump)

{

m_fileDump.AssertValid();

 

m_dumpContext.SetDepth(1);

m_dumpContext << "Dump From Objects: \r\n========================================\r\n";

}

 

CDumpMsgBox::~CDumpMsgBox()

{

BYTE* pszDumpBuffer = (BYTE*)m_fileDump.Detach();

 

if (pszDumpBuffer)

free(pszDumpBuffer);

 

m_dumpContext.m_pFile = NULL;

}

 

////////////////////////////////////////////////////////////////////////////

// CDumpMsgBox Implementations

 

int CDumpMsgBox::OnCreate(LPCREATESTRUCT lpcs)

{

if (CWnd::OnCreate(lpcs) == -1)

{

TRACE0("CDumpMsgBox::OnCreate error: call CWnd::OnCreate return FALSE.\n");

return -1;

}

 

CRect rectBox;

 

GetWindowRect(&rectBox);

 

// adjust message box sizes to fill a dump edit control.

if (rectBox.Width() < 350)

rectBox.right = rectBox.left + 350;

 

rectBox.bottom += 130;

 

MoveWindow(&rectBox, FALSE);

 

// create edit control to display dump texts

ScreenToClient(&rectBox);

 

CRect rectEdit(rectBox.left + 8, rectBox.bottom - 132,

rectBox.right - 8, rectBox.bottom - 8);

 

if (!m_editDump.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP |

ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,

rectEdit, this, IDC_DUMPMSGBOX_EDITBOX))

{

TRACE0("CDumpMsgBox::OnCreate error: m_editDump.Create return FALSE.\n");

 

return -1;

}

 

// set WS_EX_CLIENTEDGE style to edit control (let it has a drop edge)

m_editDump.ModifyStyleEx(0L, WS_EX_CLIENTEDGE,

SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);

 

// set edit control's font to DEFAULT_GUI_FONT.

CFont Font;

 

Font.Attach((HFONT)GetStockObject(DEFAULT_GUI_FONT));

 

m_editDump.SetFont(&Font);

 

// write end-dump text and flush all data to file

m_dumpContext << "\r\n========================================\r\nDump End";

m_dumpContext.Flush();

 

m_fileDump.Write("\0", sizeof(CHAR));

 

UINT nBufferSize = m_fileDump.GetLength();

 

LPSTR pszDumpBuffer = (LPSTR)m_fileDump.Detach();

 

if (!OnDumpOut(pszDumpBuffer, nBufferSize))

{

TRACE0("CDumpMsgBox::OnCreate error: OnDumpOut return FALSE.\n");

 

m_fileDump.Attach((BYTE*)pszDumpBuffer, nBufferSize, 512);

 

return -1;

}

 

// attach used dump buffer for next dump.

m_fileDump.Attach((BYTE*)pszDumpBuffer, nBufferSize, 512);

 

return 0;

}

 

未完,剩余部分请参考 浅谈 MFC 的子类化机制和该机制的一个应用( 2 )。

 

上一篇:win7如何恢复以前的ie版本


下一篇:浅谈 MFC 的子类化机制和该机制的一个应用(2)