风中之歌

我是世界的光

用户操作
[即时聊天] [发私信] [加为好友]
linzhenqunID:linzhengqun
81782次访问,排名1201好友9人,关注者27
程序,设计,音乐,
linzhengqun的文章
原创 49 篇
翻译 0 篇
转载 0 篇
评论 119 篇
linzhenqun的公告

我们生的时候啊,
要像夏季的花一样灿烂;
而在死的时候,
须如秋天的叶一般静美!

seo sydney
seo sydney Counter

最近评论
linzhengqun:呵,不好意思我不是底层的开发人员,所以对Ring0的东西其实知之甚少。
不过作为一个优秀的程序员,应该有能力自学这些东西。
hzfch:看了你的文章和提供的代码对我帮助很大
谢谢了
希望你写一些ring0 hook的文章
wwp3321:只能说声谢谢了,谢谢楼主的共享精神
trytobegood:好东东!!!收藏了
yunhaisoft://你的这个版本中文乱码,我参考Cnpack的代码帮着你改了一下,你看看。经测试已经没有乱码了。
procedure TConvRTF.ChangeSpeString(var S: String);
var
Str: string;
i: Integer;
Len: Integer;
tmpWide, tmpStr:……
文章分类
收藏
相册
CodeGear纪念
GDI绘制
MFC文档视图
MFC消息分派
玻璃效果
汇编与高级语言
接口的实现
增量搜索
最简单的MFC程序
友情链接
Ari
天蝎蝴蝶的专栏
还猪哥哥
醉到天亮说晚安
风中之歌-非技术Blog
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 MFC-窗口封装收藏

新一篇: MFC-对话框与数据交换 | 旧一篇: MFC-命令分派

 

       MFCWindow作了一层比较浅的封装,其创建过程基本与SDK差不多。它简化了窗口的注册,并将窗口过程与类关联起来;后者是比较重要的封装,它使消息能够分流给“类的窗口过程”,既而通过消息映射,才能到达各个处理函数。

       使用传统的SDK来创建一个窗口有点繁琐,伪代码是这样的:

       if (RegisterClass(…))

    {

        CreateWindowEx(…);

        ShowWindow(…);

        UpdateWindow(…);

    }

       RegisterClass根据一个ClassName注册一个窗口类,并指定窗口过程;CreateWindowEx创建该窗口,其中的参数用于设置样式、位置等。

       这段代码的繁琐之处其实在于函数的参数,CreateWindowEx12个参数,这常常让初学者望而生畏。作为良好封装的窗口框架,一个重要任务是简化这些参数,让它成为可分别设置的属性。

窗口的创建

       要看MFC如何创建一个窗口,CView应该是最合适的了,CFrameWnd::CreateView创建了一个视图窗口:

CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)

{

    CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();

    if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,

        CRect(0,0,0,0), this, nID, pContext))

    ... ...

    return pView;

}

       要创建CView,只需要一个CWnd::Create就可以了,不过这里面必然隐藏着一些事情,包括注册窗口类,指定窗口过程:

BOOL CWnd::Create调用下面的函数

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, ...)

{

    //设置创建结构

CREATESTRUCT cs;

    cs.dwExStyle = dwExStyle;

    cs.lpszClass = lpszClassName;

    ... ...

    //创建之前做了什么?

    PreCreateWindow(cs);

    //创建窗口

    AfxHookWindowCreate(this);

    HWND hWnd = ::CreateWindowEx(...);

    AfxUnhookWindowCreate();

return TRUE;

}

       CreateEx直接调用CreateWindowEx将窗口创建出来,我们注意到上面调用Create时传给lpszClassName的是一个空值,那么窗口类在什么时候注册的呢,只有一个地方,那就是PreCreateWindowPreCreateWindow是一个虚函数,CWnd在这里默认地为窗口注册名字为AfxWnd42的窗口类,而派生类可以覆盖这个函数注册其他的窗口类名,比如CView

BOOL CView::PreCreateWindow(CREATESTRUCT & cs)

{

    if (cs.lpszClass == NULL)

    {

        VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

        cs.lpszClass = _afxWndFrameOrView;

    }

    return TRUE;

}

       CViewafxWndFrameOrView注册窗口类,它的具体名字是:AfxFrameOrView42。注册的行为就在AfxDeferRegisterClass里面,不过到这里我们可以打住了。

指定窗口过程

       注册窗口类的时候要指定窗口过程,AfxDeferRegisterClass是一个宏,会调用AfxEndDeferRegisterClass作具体的调用,在函数里看到这一句代码:wndcls.lpfnWndProc = DefWindowProc;窗口过程竟指定给系统的默认窗口过程,真正的窗口过程是在什么时候指定的呢?

       CWnd::CreateEx里面,CreateWindowEx的前后各有一行代码,从字面上可以推断MFC监视了窗口的创建:

void AFXAPI AfxHookWindowCreate(CWnd* pWnd)

{

    _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();

    pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,

        _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());

    pThreadState->m_pWndInit = pWnd;

}

       MFC使用一个CBT钩子来监视窗口的创建,当pWnd的窗口句柄创建时,_AfxCbtFilterHook将被调用,这个函数的用意已经非常明显,将新创建的窗口句柄附加到pWnd,并为pWnd指定窗口过程。这个函数较长,我就不列代码了,只是将大概的几点列举如下:

1.      通过CWnd::Attach将句柄附加给pWndAttach还做了另一件事,建立pWnd与句柄的哈表,这是为了后面处理消息时可以找到正确的CWnd对象。哈表在MFC中大量地被使用。

2.         子类化窗口过程,将窗口过程指定为AfxWndProc,并保存旧的窗口过程;通过PreSubclassWindow,你还可以指定自己的窗口过程,不过似乎只在AfxWndProc之后才能被调用。

由于整个进程的所有窗口创建都会先被_AfxCbtFilterHook钩住,所以里面也进行了一些过滤,比如IME窗口。这种情况对于多线程是否有效呢,也许_afxThreadState可以确保在多线程情况窗口创建的顺序,我并没有去深究。

       总而言之,窗口过程最后被替换为AfxWndProc

LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

    CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

    if (pWnd == NULL || pWnd->m_hWnd != hWnd)

        return ::DefWindowProc(hWnd, nMsg, wParam, lParam);

    return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

}

       通过FromHandlePermanent从哈表找到与句柄对应的窗口类,执行点又进入AfxCallWndProc,接下来我们就会看到熟悉的WindowProc,也就是前面文章所说的消息处理的进入点:

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,

    WPARAM wParam = 0, LPARAM lParam = 0)

{

    _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();

    MSG oldState = pThreadState->m_lastSentMsg;   // save for nesting

    pThreadState->m_lastSentMsg.hwnd = hWnd;

    pThreadState->m_lastSentMsg.message = nMsg;

    pThreadState->m_lastSentMsg.wParam = wParam;

    pThreadState->m_lastSentMsg.lParam = lParam;

    LRESULT lResult;

 

    // delegate to object's WindowProc

    lResult = pWnd->WindowProc(nMsg, wParam, lParam);

 

    pThreadState->m_lastSentMsg = oldState;

    return lResult;

}

       pThreadState似乎与线程同步有关,从这一点可以看出MFC在多线程方面做得比VCL好得多,找个时间再来探索这个主题。

       到此,窗口创建到消息处理就连惯起来了。尽管不同的窗口类在处理上有些差异,但核心流程大概就是这样。

 

       MFC使用哈表将窗口句柄与窗口类关联起来,很多API的对象与MFC的对象即是基于此关联起来的;VCL用了另一种方法,若想了解参见我的另一篇文章从普函数到对象方法

 

发表于 @ 2007年12月08日 12:22:00|评论(loading...)|编辑

新一篇: MFC-对话框与数据交换 | 旧一篇: MFC-命令分派

评论:没有评论。

发表评论  


当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
Csdn Blog version 3.1a
Copyright © linzhenqun