注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

从C开始

 
 
 

日志

 
 

《VC++深入详解》学习笔记(1) - Windows程序内部运行机制  

2010-11-13 11:11:11|  分类: MFC |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

创建一个完整的Win32程序的一般步骤:

1.       WinMain函数的定义

2.       创建一个窗口

1)设计一个窗口类

2)注册窗口类

3)创建窗口

4)显示及更新窗口

3.       进行消息循环

4.       编写窗口过程函数

 

1.WinMain函数的定义

int WINAPI WinMain(
  HINSTANCE hInstance, 
  HINSTANCE hPrevInstance, 
  LPWSTR lpCmdLine, 
  int nShowCmd 
); 

 

2.创建一个完整窗口的步骤:

1)设计一个窗口类

2)注册窗口类

3)创建窗口

4)显示及更新窗口

 

1)设计一个窗口类

typedef struct {

    UINT style;

    WNDPROC lpfnWndProc;

    int cbClsExtra;

    int cbWndExtra;

    HINSTANCE hInstance;

    HICON hIcon;

    HCURSOR hCursor;

    HBRUSH hbrBackground;

    LPCTSTR lpszMenuName;

    LPCTSTR lpszClassName;

} WNDCLASS, *PWNDCLASS;

注:要去掉style变量所具有的CS_VREDRAW样式,我们可以编写代码style = style & ~CS_VREDRAW

 

hIcon指定窗口类的图标句柄。我们可以用LoadIcon函数来加载一个图标资源。这个函数返回系统分配给该图标的句柄。

HICON LoadIcon(

  HINSTANCE hInstance,

  LPCTSTR lpIconName

);

这个函数的第二个参数是一个LPCTSTR 类型的,因此我们需要用MAKEINTRESOURCE宏把资源ID标识符转换为需要的LPCTSTR类型。

 

hbrBackground参数用来指定窗口类的背景画刷句柄。当窗口发生重绘时,系统使用这里指定的画刷来擦除窗口的背景。

我们可以调用GetStockObject函数来得到系统的标准画刷。

HGDIOBJ GetStockObject(int fnObject );

GetStockObject函数不仅可以用于获取画刷的句柄,还可以用于获取画笔,字体和调色板的句柄。由于GetStockObject函数可以返回多种资源对象的句柄,因此在实际调用该函数前无法确定它返回哪一种资源对象的句柄,因此它的返回值的类型被定义为HGDIOBJ,在实际使用时,需要进行类型转换。

例如:

wndclass.hbrBackground = HBRUSHGetStockObject(BLACK_BRUSH);

 

2)注册窗口类

设计完窗口类后需要调用RegisterClass函数对其进行注册,注册成功后,才可以创建该类型的窗口。

ATOM RegisterClass(

  const WNDCLASS* lpWndClass

);

 

(3)创建窗口

设计好窗口类并且成功注册之后,就可以用CreateWindow函数产生这种类型的窗口了。

HWND CreateWindow(

  LPCTSTR lpClassName,

  LPCTSTR lpWindowName,

  DWORD dwStyle,

  int x,

  int y,

  int nWidth,

  int nHeight,

  HWND hWndParent,

  HMENU hMenu,

  HANDLE hInstance,

  PVOID lpParam

);

 

4)显示及更新窗口

创建窗口之后,我们就要让它显示出来

BOOL ShowWindow(

  HWND hWnd,

  int nCmdShow

);

在调用ShowWindow之后,我们紧接着调用UpdateWindow来刷新窗口。

BOOL UpdateWindow(HWND hWnd);

UpdateWindow函数通过发送一个     WM_PAINT消息来刷新窗口,这个函数将WM_PAINT消息直接发送给了窗口过程函数进行处理,而没有放到消息队列中。

 

3. 进行消息循环

在创建了窗口之后,我们需要编写一个消息循环,不断的从消息队列中取出消息,并进行响应。

BOOL GetMessage(

  LPMSG lpMsg,

  HWND hWnd,

  UINT wMsgFilterMin,

  UINT wMsgFilterMax

);

lpMsg参数指向一个消息MSG结构体,GetMessage函数从线程的消息队列中去除的消息保存在该结构体对象中。

hWnd参数指定接收属于哪一个窗口的消息。通常我们将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。

wMsgFilterMin参数用于指定要获取的消息的最小值,通常设置为0

wMsgFilterMax参数指定要获取的消息的最大值。如果这个参数和前一个参数都为0,则接收所有消息。

GetMessage函数接收到除WM_QUIT外的消息均返回非零值。

 

通常我们编写的消息循环如下:

MSG msg

while(GetMessage(&msg, NULL, 0, 0))

{

       TranslateMessage(&msg);

       DispatchMessage(&msg);

}

TranslateMessage函数用来将虚拟键消息转换为字符消息。再把字符消息投递到调用线程的消息队列中,当下次再调用GetMessage函数时取出。

当我们敲击键盘上的某个字符时,系统将产生WM_KEYDOWNWM_KEYUP消息。这两个消息的附加参数(wParamlParam)包含的是虚拟键代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII码,TranslateMessage函数就可以将WM_KEYDOWNWM_KEYUP消息组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII码),并将转换后的新消息投递到调用线程的消息队列。

注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。

DispatchMessage函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。它实际上是将消息传给操作系统,由操作系统调用窗口过程函数对消息进行出来。

 

下面来归纳一下Windows应用程序的消息处理过程:

(1)       操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。

(2)       应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理。例如,放弃对某些消息的处理,或者调用TranslateMessage产生新的消息。

(3)       应用程序调用DispatchMessage,将消息回传给操作系统。

(4)       系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。

 

4.编写窗口过程函数

窗口过程函数主要用于处理发送给窗口的消息。一个Windows应用程序的主要代码部分就集中在窗口过程函数中。

窗口过程函数的定义形式如下:

LRESULT CALLBACK WindowProc(

  HWND hwnd,

  UINT uMsg,

  WPARAM wParam,

  LPARAM lParam

);

一个程序可以有多个窗口,窗口过程函数的第一个参数hwnd就标识了接收消息的特定窗口。

在窗口过程函数内部使用switch/case语句来确定窗口过程接收的是什么消息,以及如何对这个消息进行处理。

 

注意

1.在响应WM_PAINT消息的代码中,要得到窗口的DC,我们必须调用BeginPaint函数。BeginPaint函数只能用在WM_PAINT消息的响应代码中使用,在其他地方,只能使用GetDC来得到DC的句柄。此外,BeginPaint函数得到DC,必须用EndPaint函数去释放。

 

2.当我们需要提示用户是否需要退出时,应该在WM_CLOSE消息中进行判断。例如:

case WM_CLOSE:

       if(IDYES == MessageBox(hwnd, “是否真的退出程序?”, ”message”, MB_YESNO)

       {

              DestroyWindow(hwnd);

       }

       break;

DestroyWindow函数在销毁窗口后会向窗口过程发送WM_DESTROY消息。对于WM_CLOSE消息的响应并不是必须的,如果应用程序没有对该消息进行响应,系统会将这条消息传给DefWindowProc函数,而DefWindowProc函数会自动调用DestroyWindow函数来响应这条WM_CLOSE消息。

      

3. DestroyWindow函数在销毁窗口后会向窗口过程发送WM_DESTROY消息。我们在该消息的响应代码中调用PostQuitMessage函数。PostQuitMessage函数向应用程序的消息队列投递一条WM_QUIT消息并返回。

GetMessage函数只有在收到WM_QUIT消息时才返回0,此时消息循环结束,程序退出。

想要让程序正常退出,我们必须响应WM_DESTROY消息,并在消息响应代码中调用PostQuitMessage,向应用程序的消息队列中投递WM_QUI消息。

 

4.   DefWindowProc函数函数调用默认的窗口过程,对应用程序没有处理的其他消息提供默认处理。

用法:

default

return DefWindowProchWnd uMsg wParam lParam);

 

变量的命名约定--匈牙利命名法

《VC++深入详解》学习笔记(1) - Windows程序内部运行机制 - Fly - y1生倾于晴
 《VC++深入详解》学习笔记(1) - Windows程序内部运行机制 - Fly - y1生倾于晴

 

示例程序:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_ERROR);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = WinProc;
wndcls.lpszClassName = TEXT("mzfClass");
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;

RegisterClass(&wndcls);

HWND hwnd = CreateWindow(TEXT("mzfClass"), TEXT("MyFirstApp"), WS_OVERLAPPEDWINDOW,
0, 0, 600, 400, NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);

MSG msg;
BOOL bOk;
while (bOk = GetMessage(&msg, NULL, 0, 0))
{
//防止窗口销毁后,hwnd变为NULL, GetMessage返回-1而进入死循环
//当GetMessage函数的第二个参数为NULL时可忽略,但是当表示特定的窗口句柄时必须加上这个判断
if (bOk == -1)
{
return -1;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}



LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDc;
switch (uMsg)
{
case WM_CHAR:
TCHAR szChar[20];
sprintf(szChar, TEXT("char code is %d"), wParam);
MessageBox(NULL, szChar, TEXT("message"), MB_OK);
break;

case WM_PAINT:
PAINTSTRUCT ps;
hDc = BeginPaint(hwnd, &ps);
TextOut(hDc, 0, 0, TEXT("我的的第一个应用程序!"), lstrlen(TEXT("我的的第一个应用程序!")));
EndPaint(hwnd, &ps);
break;

case WM_LBUTTONDOWN:
MessageBox(NULL, TEXT("鼠标左键被按下!"), TEXT("message"), MB_OK);
hDc = GetDC(hwnd);
TextOut(hDc, 0, 50, TEXT("鼠标左键被按下!"), lstrlen(TEXT("鼠标左键被按下!")));
ReleaseDC(hwnd, hDc);
break;

case WM_RBUTTONDOWN:
MessageBox(NULL, TEXT("鼠标右键被按下!"), TEXT("message"), MB_OK);
hDc = GetDC(hwnd);
TextOut(hDc, 0, 100, TEXT("鼠标右键被按下!"), lstrlen(TEXT("鼠标右键被按下!")));
ReleaseDC(hwnd, hDc);
break;

case WM_CLOSE:
if (IDYES == MessageBox(NULL, TEXT("是否退出?"), TEXT("message"), MB_YESNO))
{
DestroyWindow(hwnd);
}
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
  评论这张
 
阅读(309)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018