您的位置:首页 > 其它

20170512Windows04_Windows消息机制

2017-05-14 16:36 169 查看

Windows消息机制

wWinMain():

1:有wWinMain和winMain两种形式,他们的区别在于使用的宽窄字符集不同。还有_tWinMain,自适应。

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR    lpCmdLine,
_In_ int       nCmdShow)


2:APIENTER,实际上是宏,指向WINAPI,实际最终为__stdcall。

3:在参数传递的时候,是通过栈进行传递的,在函数调用完成之后,栈帧会被清理,栈帧有被调用的函数清理还是调用函数清理的不同。例如A函数调用B函数,B函数有两个参数,当B函数调用的时候,就会有一个call过程,B函数返回调用完成之后,B函数的栈帧会被清理,但是这两个参数会由谁来清理?__stdcall的话,就是由被调用方(B函数)来清理,而__cdecl的话,就是调用放(A来清理)来进行清理。还有__fastcall,他是指不通过栈来进行参数的传递,而是通过寄存器来传递。

4:win32编程里面都是用的__stdcall。而一般的C语言编译器则是使用的__cdecal,

5:参数:

    1:_In_ HINSTANCE hInstance:HANDLE代表内核里面的一个实例对象,实例对象指的是进程。打开文件返回的是HANDLE,他是内核对象里面的文件对象的一个单元,而HINSTANCE是实例内核对象,他和文件系统是不一样的。File是属于IO管理程序,有自己的内核对象,他会直接和硬件产生交互。HINSTANCE是属于进程管理程序的系统服务,他也有自己的内核对象,他不会对用户态暴露,只是留了接口HINSTANCE,我们只能通过这个接口来操作。

    程序能变成进程是因为有操作系统的存在,系统为这个程序创建进程,然后将进程ID(句柄)传递进来,这个参数就是系统传递进来的进程句柄。

    2:_In_opt_ HINSTANCE hPrevInstance:代表是谁打开或启动的这个进程,程序的来源,程序的父进程。这个参数也是由系统传递进来的。

    3:_In_ LPTSTR lpCmdLine:在main函数中,也有这样的参数,它使得进程可以和外部通讯,参数可在快捷方式后面传递进来。

    4:_In_ int nCmdShow:代表窗口如何进行显示的,用户可以实时改变,是用户指定的。

    5:return value:与我们编程关系不大。

窗口创建:

1:这两个宏没有做任何事情。

UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);


2:LoadString:字符串加载,就是对szTitle和szWindowClass进行设置,加载的是资源表里面的东西。设计成加载资源的方式有好处也有坏处。

LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32WINDOWSDEMO, szWindowClass, MAX_LOADSTRING);


3:注册窗口类:WNDCLASS。

ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);//Windows里面任何带有cbSize参数的结构体,都应该对这个参数进行设定,否则很可能导致程序出错,不可用。
//用户态和核心态之间的共享区域中保存这些数据,核心态取数据的时候需要知道大小,这个参数就是让核心态知道取多大数据的。
wcex.style		= CS_HREDRAW | CS_VREDRAW;//窗口类的组合风格。这里的指横纵向可拉伸。
wcex.lpfnWndProc	= WndProc;//窗口消息回调函数。
wcex.cbClsExtra		= 0;//代表附带的一些信息,另一边可取到这些信息。
wcex.cbWndExtra		= 0;//额外的信息
wcex.hInstance		= hInstance;//属于的实例
wcex.hIcon		= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32WINDOWSDEMO));//图标
wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);//光标
wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);//背景色
wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32WINDOWSDEMO);//载入菜单,菜单名
wcex.lpszClassName	= szWindowClass;//窗口类名,是使用这个类的唯一标识符。
wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//

return RegisterClassEx(&wcex);//注册窗口类
}


RegisterClassEx是RegisterClass的升级版本,

4:创建窗口:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;//代表创建窗口后返回的窗口对象。

hInst = hInstance; // 将实例句柄存储在全局变量中

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);//父窗口为NULL。是否带有菜单,句柄,

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);//显示窗口
UpdateWindow(hWnd);//更新窗口

return TRUE;
}


应用程序消息机制:

1:在NT结构中,我们一般关注的是从用户态到核心态做的事情,如果要研究Windows的消息机制,就需要研究从核心态到用户态的过程,软件需要响应硬件的消息。
2:在设计上,核心态是不可以操作用户态的,中间的一堵墙只允许用户态到核心态。核心态没有东西可以出去,消息响应是在Win32子系统里面来做的。

3:整个过程为,当我们的硬件有所操作的时候,他会通过电路的方式通知我们的内核,内核会将这些东西写到一块共享的区域里面去,这块区域会不停地被Win32子系统扫面,当发现里面有鼠标动作的时候,他会讲鼠标的动作翻译成一个新的东西——消息。相同的鼠标动作,在不同的时候,被翻译成的消息是一样的,但执行效果可能会不一样。这个翻译过程之所以没在内核里面来做,是因为内核设计要求非常地精简,要保持其稳定性。

4:在Win32里面定义有一个MSG的结构体,Win32会帮我们把每个应用程序都建立一个消息队列,这个消息队列经过Win32翻译之后就会派发给应用程序。派发过来的消息一定是属于当前程序的消息,不会吧其他程序的消息派发到其他程序里面去。他会使用焦点窗口系统来判断这个消息属于哪一个窗口(应用程序)。

5:GetMessage函数会通过消息队列来Get信息,它会不停地调用来获取来自外部的消息。消息队列不存在会不会满的问题,一边在加,一边在处理,如果电脑速度慢,会出现卡,但是不可能存在消息队列满这一说。消息可能存在优先级。

// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))//保存消息的结构体,get哪个消息的HAND,消息过滤。
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))//判断是否为加速器消息,是则会转换为其他消息来发送。
{
TranslateMessage(&msg);//翻译消息
DispatchMessage(&msg);//派发消息,会进入到callback里面。
}
}


6:MSG结构体:这个结构体就是每一个窗口消息的原型,里面包括了以下信息。

typedef struct tagMSG {
HWND        hwnd;		//消息所在窗口的句柄
UINT        message;	//消息类型代号
WPARAM      wParam;		//消息附加信息
LPARAM      lParam;		//消息附加信息
DWORD       time;		//消息被创建的时间
POINT       pt;		//消息被创建时,鼠标的位置
#ifdef _MAC
DWORD       lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;


里面包含了:接受消息的窗口句柄,信息类型(翻译成了数值),消息的描述,消息描述,消息产生时间,相对窗口位置。LPARAM和WPARAM就是UINT,里面保存的指针。

7:这些消息队列都存在于一个软件里面的消息队列,我们使用GetMessage来获取消息。消息队列也有很多的分类:系统消息队列,进程消息队列,线程消息队列……。消息的派发(DispatchMessage)会触发回调函数,派发到这个线程中。

MFC剖析:

1:在Win32里面,有很长一串WM_开头的,他们都是WindowsMessage。代表了Windows里面的各种消息。

2:消息大致分为:

    1:窗口消息:例如窗口的移动,创建窗口,显示窗口,鼠标在窗口中点击。

    2:命令消息:主要是来做窗口到另一个窗口的用户请求。

    3:控件消息:所有的控件都是一个窗口,窗口中的某些控件消息会通知父窗口,控件都是作用与父窗口。

3:一般,在Win32中,我们只会对我们感兴趣的消息进行处理,WM_COMMAND:菜单栏的消息,里面还会分为每一种具体的消息。可以看出,这样的开发接近于本质,不方便我们高效地开发,Windows就开发了MFC。

4:MFC只是对Win32的一些消息和过程进行了包装,便于我们开发,仅此而已。比如关于对话框,他通过一个类,将里面的数据进行了组合,方便我们来调用。归根结底还是使用的Win32,只是将一些函数,消息封装到了类里面,这些类里面使用的东西还是Win32的。

5:例如,在MFC中,对WM_COMMAND消息的处理:

void CMFCDlgDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);//默认的消息,这些函数在afxwin.h里面,实际代码我们看不到。
}
}


可以看出,他是包装成类来处理的。

6:MFC里面调用的所有函数都是MFC的函数,同样的MessageBox,MFC里面调用的和Win32里面调用的都是不一样的。传递的蚕食可能也会有差别。在MFC里面如果想要调用Win32的函数,可以使用::来表示调用全局的函数,这样就是调用的Win32里面的函数。

7:只需要继承与MFC写好的这些类,就可以很容易的使用。窗口和窗口之间就是两个不同的窗口类,只需要有public的方法,就可以实现窗口之间的信息交流。用Win32开发的话,一个回调函数会有几百行上千行都可能,用Win32编程和使用汇编写程序一样。

8:MFC唯一的缺点在于太过庞大了,而且不支持跨平台,基本没有再更新了,所以之后被QT取代了,后来就流行自己画界面了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: