Windows开发的常识
1)窗口
Windows中最基本的概念也许就是窗口了,每一个前台程序都至少有一个窗口,一个窗口也是你可以看到的部分,比如,QQ有如下的登录窗口
基本上你在Windows中可见的都是一个窗口,窗口也是Windows中用于用户直接交互的基本元素(GUI程序)。
2)句柄
窗口、文件、socket、信号量、管道、邮槽(mailslot)……都是Windows平台中的基本对象,为了操作这些对 象,我们需要一个能够引用这些对象的东西,这个引用这些对象的东西就是句柄(Handle)。句柄对于资源就像遥控器对于电视机,用遥控器能更好地操作电视机而不用关心内部实现的细节,句柄也是这样的,用句柄你能更好地操作Windows对象,而不需要关系其内部的实现细节。事实上,你想操作Windows对象也只能通过句柄操作。比如,你想操作一个线程,你看看SuspendThread的原型如下所示:(第一个参数就是一个线程句柄)
Syntax
DWORD WINAPI SuspendThread(
__in HANDLE hThread
);
又比如,你想读一个文件,其函数ReadFile的原型如下:(
第一个参数就是一个文件的句柄。)
BOOL WINAPI ReadFile(
__in HANDLE hFile,
__out LPVOID lpBuffer,
__in DWORD nNumberOfBytesToRead,
__out_opt LPDWORD lpNumberOfBytesRead,
__inout_opt LPOVERLAPPED lpOverlapped
);
…………类似的例子还有很多,基本上想操作Windows对象都需要指向它们的句柄,句柄在语义上与C中的
指针一样,但是,语法上是完全不一样的。
3)进程
一些操作系统教材上对进程的定义为:进程是一个程序的一次运行过程。这个定义其实是对的,但是,也是错的,为什么呢?在Windows之前还有许多其它的操作系统,比如OS 360、UNIX……进程的概念提出来的时候Windows 还根本没出生呢。不过,在Windows中,进程的概念完全不是这样的,在Windows里面,进程是程序隔离的基本单元,每一个32位应用程序在自己的进程空间里面运行,进程只为这个程序提供4G的虚拟地址空间,并且,不同的进程之间互相不干扰(当然,后面会讲到,进程之间会通信)。
4)线程
在Windows里面,真正运行程序的其实是线程,线程在进程提供的4G虚拟地址空间里面运行,它执行PE文件的.text段。也就是说,线程才是真正的执行体。
基本的正常的Windows程序
代码
创建一个工程,还是按照上次的方法,在上一节里面有,然后输入以下代码:
#include <windows.h>
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("BossJue");
HWND hwnd;
MSG msg;
WNDCLASSEX wndclassex = {0};
wndclassex.cbSize = sizeof(WNDCLASSEX);
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = WndProc;
wndclassex.cbClsExtra = 0;
wndclassex.cbWndExtra = 0;
wndclassex.hInstance = hInstance;
wndclassex.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclassex.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclassex.lpszMenuName = NULL;
wndclassex.lpszClassName = szAppName;
wndclassex.hIconSm = wndclassex.hIcon;
if (!RegisterClassEx (&wndclassex))
{
MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,
szAppName,
TEXT ("WindowTitle"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hwnd, iCmdShow);
UpdateWindow (hwnd);
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const LPTSTR text = TEXT("Hello,World");
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
return (0);
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
TextOut (hdc, 0, 0, text, lstrlen(text));
EndPaint (hwnd, &ps);
return (0);
case WM_DESTROY:
PostQuitMessage (0);
return (0);
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
运行
按下Ctrl + F5,你可能会碰到如下的链接错误:
不要急,这个错误的设置是因为Windows把这个程序当作console程序,而console程序默认入口函数是main,而我们在这里面并没有定义main,而是定义了一个叫WinMain的入口函数,所以链接器不认,而我们希望的却是编译器认为我们这个程序是一个GUI程序,GUI程序的入口函数是WinMain(如果你用#pragma comment(linker…………)修改的话当然也是可以的,这种方法我在第一章里面就写到了。当然,我们还可以直接在Visual Studio IDE里面直接设置,方法是打开项目属性,然后如下设置:
这样设置之后,再按Ctrl + F5,有没有看到如下的一个窗口呢?
对,就是这样,你看到的就是一个正常的Windows程序的编写。
代码解释
编写Windows程序的过程
编写一个Windows程序,你需要做的是三步,第一步是注册一个窗口类,第二步是创建窗口,第三步是编写消息响应函数。其中第一步和第二步是基本固定的,除了个别的参数需要自己调整之外,最重要的是第三步,
一个Windows程序中,代码量最大的基本都在第三步。在Windows应用程序里面,如果我们想要接收用户的输入、在窗口上面显示一些信息……我们都需要处理相关的消息,Windows把与这个程序相关的事件都以消息告诉程序,至于怎么处理这些消息,则是我们自己的事情。上面的RegisterClassEx (&wndclassex)其实就是注册窗口类CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,
szAppName,
TEXT ("WindowTitle"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL); 其实就是创建窗口,LRESULT CALLBACK WndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam)其实就是那个消息处理函数,只不过,这个消息处理函数不需要我们自己调用,当有消息的时候,Windows会自己调用这个函数来处理,我们只需要编写处理代码,但是,编写的代码不需要我们自己手动调用(这也许就是Callback函数的原因)。
剩下的
剩下的如果还有不懂的自己可以读msdn,比如,对CreateWindow不熟悉,可以直接在MSDN里面搜索CreateWindow,MSDN不仅会告诉你这个函数怎么用,还会告诉你消息循环是什么之类的,在此我就不仔细展开说明了。