天天看点

1.一个简单Win32窗口程序分析

  首先我们用VS2013, 或者VC6.0等工具创建一个WIN32项目,我这里用的是VS2013, 由于工具的不同,代码会有一些差异,但是本质上还是一样的。

1.一个简单Win32窗口程序分析

创建一个简单的WIN32程序生成了如图所示的代码,我们打开 Win32Generic.cpp ,可以看到自动生成的代码接近两百行。

程序的入口:

在编写C/C++ 的时候,我们的程序入口叫做 main

而在WIN32的程序中,入口程序变成了:

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

接下来我们看到了两行代码:

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

UNREFERENCED_PARAMETER 是一个宏, 表示 对没有使用的 已经声明的变量不发出警告。

就是  当你定义了一个变量,但是在接下来的代码中,你却不去使用它,编译器标会发出警告,有变量未使用,如果使用这个宏,就不会生成这些警告

再往下 发现调用了一个函数:LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

int LoadString(      

    HINSTANCE hInstance,
    UINT uID,
    LPTSTR lpBuffer,
    int nBufferMax
);      

函数的作用:将字符串资源加载到制定的进程中。

第一个参数:hInstance ,代表一个进程的句柄,句柄的可以理解为代表,进程是实际存在于系统的事物, 而我们要去找到这个进程的就必须通过它的句柄。这个类似:人是存在于世界中的具体事物,如果我们要找到这个人的话,我们就得知道这个人的一个唯一代表性的东西,例如人的相貌, 或者身份证号,相貌和身份号就可以成为人的句柄。 你会奇怪那我们怎么会事先拥有了句柄这个东西呢,我们可看我们的入口函数,第一个参数便是 hInstance,这是程序在调用开始运行的时候,系统传递给我们的。

第二个参数:uID, 这个表示资源ID, ID和句柄类似, 唯一的代表着一个资源 。如果不大理解的话我们可以看一下  Resource.h在头文件中

#define IDS_APP_TITLE103
#define IDR_MAINFRAME128
#define IDD_WIN32GENERIC_DIALOG102
#define IDD_ABOUTBOX103
#define IDM_ABOUT104
#define IDM_EXIT105
#define IDI_WIN32GENERIC107
#define IDI_SMALL108
#define IDC_WIN32GENERIC109
#define IDC_MYICON2
#ifndef IDC_STATIC
#define IDC_STATIC-1

我们可以看到 资源文件中又许多宏 , 那他们 代表什么意思呢, 我们需要在打开一个Win32Generic.rc 文件(在资源文件夹中)

可以看到:

1.一个简单Win32窗口程序分析

在String table 中 又 如上图: IDS_APP_TITLE  值  Win32Generic 意思为 103号资源  是字符串 “Win32Generic”

所以函数   LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);   的功能是 将103号资源的字符串 复制到stTitle 中。

第三个参数:lpbuffer , 用来存放这些字符串的字符数组指针nBufferMax

第四个参数:nBufferMax , 需要加载的字符串的最大长度

窗口类注册:

接下来我们看到的是 MyRegisterClass(HINSTANCE hInstance)

我们看看这个函数

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW; 
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32GENERIC));(窗口左上角的图标)
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32GENERIC);
	wcex.lpszClassName	= szWindowClass;
<div style="text-indent: 28px;"></div>	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//窗口的小图标(任务栏上的图标),如果为NULL,就和hIcon使用同一个图标

	return RegisterClassEx(&wcex);
}
           

LoadXXX函数意义和之前的LoadString 意义相同只是加载的资源不一样

它首先定义了一个结构体:WNDCLASSEX  有些是 WINDCLASS 这不大重要 都可以理解为WINDCLASS , EX的意思为扩展 extend 新版本的MFC对旧版本做的一些扩展,但是这些功能暂时还不需要讲到。

我们可以看到这里对结构体的各个属性进行赋值,而这些属性基本都可以 望文知意 

<span style="white-space:pre">	</span>typedef struct tagWNDCLASSW {
  <span style="white-space:pre">	</span>  UINT        style; //窗口的显示风格
  <span style="white-space:pre">	</span>  WNDPROC     lpfnWndProc;<span style="white-space:pre">	</span>//窗口的处理函数, 通过回调来执行消息的处理程序。(如果理解的话可以当做是窗口中发生了鼠标点击,或者<span style="white-space:pre">		</span>菜单被点击时,你需要做什么处理,你就把代码写到这个函数中去,当对应的事件发生,系统自动帮你执行对应的代码)
    <span style="white-space:pre">	</span>  int         cbClsExtra;
   <span style="white-space:pre">	</span>  int         cbWndExtra;
   <span style="white-space:pre">	</span>  HINSTANCE   hInstance;//窗口属于哪一个进程的,一般为本进程
   <span style="white-space:pre">	</span>  HICON       hIcon;//窗口的图标
   <span style="white-space:pre">	</span>  HCURSOR     hCursor;//窗口使用的光标
   <span style="white-space:pre">	</span>  HBRUSH      hbrBackground;//背景颜色
   <span style="white-space:pre">	</span>  LPCWSTR     lpszMenuName;//菜单的名字
  <span style="white-space:pre">	</span>  LPCWSTR     lpszClassName;//窗口的名称,后面会用到
<span style="white-space:pre">	</span>} WNDCLASSW;
           

当所有的属性设置好以后,

<span style="white-space:pre">		</span>RegisterClassEx
           

将窗口注册到系统中(窗口类都需要注册才可以使用)。

生成/显示窗口 :

InitInstance (hInstance, nCmdShow)接着就是 InitInstance

<span style="white-space:pre">	</span>BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
<span style="white-space:pre">	</span>{
  <span style="white-space:pre">	</span> HWND hWnd;

  <span style="white-space:pre">	</span> hInst = hInstance; // 将实例句柄存储在全局变量中

  <span style="white-space:pre">	</span> hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
   <span style="white-space:pre">	</span>   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

  <span style="white-space:pre">	</span> if (!hWnd)
  <span style="white-space:pre">	</span> {
  <span style="white-space:pre">	</span>    return FALSE;
  <span style="white-space:pre">	</span> }

  <span style="white-space:pre">	</span> ShowWindow(hWnd, nCmdShow);
  <span style="white-space:pre">	</span> UpdateWindow(hWnd);
<span style="white-space:pre">	</span>
   <span style="white-space:pre">	</span>return TRUE;
<span style="white-space:pre">	</span>}
           

              这里我们主要分析 CreateWindow函数:                                          

HWND CreateWindow(      

    LPCTSTR lpClassName,
    LPCTSTR lpWindowName,
    DWORD dwStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam
);      

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  第一个参数: 便是之前注册窗口时填写的窗口类名称,只需要填入对应的名称就可以生成对应的窗口。

第二个参数: 窗口的标题栏名称                                                                                                                                                                                                    第三个参数:窗口的风格, WS_OVERAPPEDWINDW,  代表创建的窗口有菜单栏, 细边框, 可以最小化,也可以最大化 。如图

1.一个简单Win32窗口程序分析

x,y 表示窗口的位置。

width,height 窗口的宽高

                                                                                                                                                                                                                                                   hWndParent    这个窗口的父窗口。                                                                                                                                                                                               hInstance 和这个窗口有关联的进程, 注册的时候写的 hInstance , 因为窗口注册进入了你指定的进程句柄,当需要创建的时该窗口的时候,就需要去对应的进程中去寻找, 所这里需要填入 窗口注册时填写的进程句柄

 lpParam  需要传递给窗口的额外信息。这里暂时填空NULL

如果函数成功就会返回窗口的句柄,之后你要对这个窗口进行操作的话,就需要通过这个句柄才行。

 ShowWindow(hWnd, nCmdShow);

    UpdateWindow(hWnd);

显示窗口和更新窗口。

hWnd 为窗口句柄

这里 nCmdShow的值为 SW_SHOW , 表示 需要显示窗口 nCmdShow    的更多选项可以查看MSDN

消息循环设置:

我们先来看大致的消息传递流程:

1.一个简单Win32窗口程序分析

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

{

if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

继续阅读