看过许多windows消息循环的内容了。但还是感觉没有系统的掌握windows窗口显示的原理。 句柄啊,GDI啊,windows消息循环啊,wndproc回调函数啊。 窗体与控件的区别啊, 容器与控件的关系啊, 等等。 尤其是当一个windows窗体程序,从运行到显示的过程中,几个关键点是什么,操作系统先创建了什么??再创建了什么,再根据什么执行的GDI中的代码从而在界面上绘制出了窗体,窗体容器与控件之间的关系 在代码中如何体现的。 比如,我在c#中,一个控件在一个窗体上显示,代码一般是 new 一个控件。然后添加到form.controls集合中。在窗体的构造函数中执行此代码。 但窗体类,控件类,执行的过程 与windows创建句柄,前后的关系是什么?
写的比较乱,因为思维就比较乱。 望能尽可能系统的讲一下。前后的逻辑关系与对应关系。
写的比较乱,因为思维就比较乱。 望能尽可能系统的讲一下。前后的逻辑关系与对应关系。
句柄:句柄是窗口的标识,用来唯一的识别一个窗口
GDI:Win32下图形接口
windows消息循环:
先来看一下一个GUI程序的执行流程:
code=C/C++] while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}[[/code]
应用程序通过一个循环,不停的从消息队列里提取消息,并进行处理,可以看出,一个WinForm程序基本上是一个“死循环”,因为只要有消息,GetMessage就返回true。
这就是消息循环。
WndProc窗口过程:在上面的代码中,DispatchMessage函数用于将消息提交到窗口过程WndProc中,在这里你可以进行消息处理。
再来看看通常WndProc里的代码:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc; switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
每个消息包括4部分:窗口句柄(标识将要发送给哪个窗口),消息号(标识不同的消息,例如WM_KEYDOWN标识键盘按下),wParam,lParam这是两个额外的参数,在16位机器中,l表示WORD,占2个字节,l表示LONG,占4个字节,但现在32位的机器都是4个字节。
系统已经帮你处理了一些很重要的消息,例如窗体重绘,窗体销毁等。而像一些事件,例如单击,也是从这里开始的,当系统检索到左键按下,左键抬起消息时,就将其处理成单击,并调用OnClick方法,并最终调用了Click事件。
而这个消息会返回false,从而退出消息循环。
常说的 1 设计窗口类
2 注册窗口类
3 创建窗口
4 显示及更新窗口。
这么个过程。对于窗体这个窗口也比较理解了。
比如,我们常见的 new form1();这是对应到了创建窗口了吧?? 而注册窗口类的环节,net给隐藏了吧?? 那对应的控件 new button1(); 这是创建子窗口-- 控件吗?? 但,显示时。不把控件添加到父窗口的控件集合属性,按钮是不会显示出来的。但是,按钮的visiable属性却是true。
所以现在疑惑比较集中在,字窗口 也就是控件 与容器 父窗口的关系上。
1.
theForger's Win32 API Tutorial
http://www.winprog.org/tutorial/
2.
Win32 Tips, Tricks and Tutorials
http://www.catch22.net/tuts
3.
Stromcode Win32/C++
http://www.stromcode.com/category/code/
4.
A programming tutorials on C++ language, C language, MFC Windows
http://www.tenouk.com/其他的 FunctionX, DevX, codeproject, codeguru 就不列了.
谢谢。视觉树这个概念,也许能解释我的疑惑了。控件必须在容器上绘制。而不添加到父窗口的控件集合属性中,就没有添加到视觉树中。不过,除了在窗体代码中 显示新的窗体, 需要 show方法外。 控件的显示 父窗体的显示,都没有调用show方法。
show是父级窗体的显示方法,它将触发回调,并以此窗体为树根进行树的整个遍历ONPAINT。还有,每个控件都是一个WINDOW,所以都会有同样的回调接口。
你想理解这些建立你学习c++与windows核心开发记和创建一个窗体是使用createwinprc函数消息泵由windows自己维护(windows会自动将消息发给当前激活状态的窗体[控件])
如果还是学生的,先学一下C++,或Delphi,
Delphi还可以直接看原代码,很好的,可惜Delphi已经.....
如果要更好的理解可以看看
WINSDK编程或者深入了解MFC!
我个人对这个也不是很懂, 大概说, 就是有一个消息队列, 默认就能运行的了。写WIN32的话, 就是对消息队列相关的消息再进行需要的变化。
看MFC是体现不出这个的, 只能看WIN32。
句柄?就是学号呗。学校是不管你的姓名的, 只管你的学号。就这个意思
wndproc?就是填写相关的消息用的函数呗。
就mfc窗口程序来说,
1. 给C***App填上虚拟函数 virtual BOOL PumpMessage();
填上代码:
BOOL CTestmfcApp::PumpMessage()
{
ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
return FALSE;
}
if (m_msgCur.message==WM_ENDSESSION) {
AfxMessageBox("WM_ENDSESSION");
}
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
theGroup++; // 这个为全局变量,只在这里被增加! // 这个是输出函数,将消息利用OutputDebugString打印到 Dbgview.exe里面
// 格式自己定义,theMSGSTR 为一个类,里面封装了 消息字符串到消息值的映射
// 将消息的个数 theGroup 也打印出来
SOutputDebugString("CRibbonApp",&theMSGSTR,m_msgCur.message,theGroup);
::DispatchMessage(&m_msgCur);
}
return TRUE;
//return CWinApp::PumpMessage();
}
2. 给 CMainFrame添上虚函数WindowProc ,在虚函数里面调用上面那个SOutputDebugString
3. 给 C***View添上虚函数WindowProc ,在虚函数里面调用上面那个SOutputDebugString
4. 设置模式为debug模式,编写SOutputDebugString 函数
5. 你会发现,mfc程序启动之后,有很多消息,呵呵,一清二楚,不用再猜了,如此如此,定可搞定windows应用编程,不是吹的
我的图片如下:
有兴趣加我:1123392038
原理很简单,很实用,我处理一些windows绘图技术,就是通过这种手段研究windows消息的规律
不久,我就可以发布一些windows规律了
建议:每个人用这个方法研究一些windows api函数,把研究得出来的消息等结果最后汇总起来,公布!
不久我就给出具体的意见
Dbgview.exe 是 sysinternals 工具箱里面的一个.原理:就是将程序里面的每个消息都变成对应的字符串,发送到一个小程序Dbgview.exe
1.CWinApp里面有个消息循环,其中这个类有一个虚函数PumpMessage,利用source insight 查阅mfc源码可以看到
利用vc 6.0 class wizard 不能添加,只能手动覆盖掉PumpMessage.将 mfc里面的源码拷贝出来,去掉一些不重要的调试代码就可以了2.每次DispatchMessage之前,将特定格式的字符串利用OutputDebugString输出到Dbgview.exe.
我使用的信息: 1). GetMessage被调用的次数(创建一个全局变量,每次DispatchMessage之前递增)
2). 消息循环的类名称,如下面的"CMainFrame",当前消息的指针(GetCurrentMessage来获取)
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
//SOutputDebugString("CMainFrame",&theMSGSTR,message,theGroup);
SOutputDebugString("CMainFrame",&theMSGSTR,GetCurrentMessage(),theGroup,time);
return CFrameWnd::WindowProc(message, wParam, lParam);
}
3.CMainFrame里面的虚函数WindowProc 用来处理所有CMainFrame能接受到的界面消息,在这个函数里面也将消息格式化成字符串后发送给Dbgview.exe
3.C***View里面的虚函数WindowProc 用来处理所有C***View能接受到的界面消息,在这个函数里面也将消息格式化成字符串后发送给Dbgview.exe 先运行Dbgview.exe,再启动被调试的程序,你就会发现所有的windows消息都流到Dbgview.exe里去了.见上图
很多源码里面没有的,通过验证可以查得到,比如未定义的WM_SYSCOMMAND消息,当你拖动窗口右边的边框时,会产生WM_SYSCOMMAND,wParam为0xF002好好利用source insight,利用里面的查询工具
里面的symbol 不一定能搜索出所有的东西,Search Projects 命令也不能搜索出所有的,Search Files能搜索出很多东西,只是太泛了好好利用Dbgview.exe
以前,图形界面我怎么也调试不到,总是用AfxMessageBox来探测运行到哪儿来了,可这个家伙总会破坏程序的消息队列,你会被结果搞得一团糟
将所有的消息打印到Dbgview.exe,是观察windows 消息转递机制的好办法.
我利用这个技术,已经将鼠标缩放窗口的所有消息的规律分析出来了,自己设计了一些缩放窗口时只绘制缩放的那一个很窄的条条的算法,效率还不错,满意总之,你把windows 消息搞清楚了,windows图形编程就不再是难事了,真的
我觉得挺经典的