学完了汇编和C语言,想做个小游戏,需要编写带窗口界面的程序,所以接下来学习一些WIndows编程的知识。参考书籍为《Windows程序设计 第五版》,由于教材比较老,一些内容可能已经过时。
C语言中,入门程序是:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
Windows对应程序为:
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
MessageBox(NULL, TEXT("Hello, Windows 98!"), TEXT("HelloMsg"), 0);
return 0;
}
头文件windows.h
是一个最重要的包含文件,里面包含了另外几个最重要且最基本的头文件:
WINDEF.H 基本数据类型定义。
WINNT.H 支持Unicode的类型定义。
WINBASE.H 内核函数。
WINUSER.H 用户界面函数。
WINGDI.H 图形设备接口函数。
WINAPI
标志符定义在WINDEF.H
中:
#define WINAPI _stdcall
_stdcall
是一种函数调用约定,约定了参数传递顺序等调用细节。之前学习的C语言函数代码都是使用的默认调用约定,所以省略了。在stdio.h
等文件内可以看到大量带有调用约定的函数声明。
WinMain
的第一个参数一般叫做“实例句柄”(Instance Handle),它唯一标识了这个程序。第二个参数已不使用,通常总是NULL
(定义为0)。
第三个参数是PSTR
类型,它与LPSTR
都定义在WINNT.H
中,如下:
typedef char CHAR;
typedef CHAR *PCHAR,*LPCH,*PCH,*NPSTR,*LPSTR,*PSTR;
指针运算符*
是自右向左结合,所以PSTR
和LPSTR
都被定义为指向字符串的指针,等同于:
typedef CHAR* LPSTR;
typedef CHAR* PSTR;
前缀LP
是长指针(Long Pointer)的缩写,STR
是字符串(String)的缩写。
WinMain
的第三个参数是用来运行程序的命令行(Command Line),有些Windows程序在启动时用它来把文件装入内存。第四个参数用来指明程序最初如何显示:正常显示或最大化最小化。
MessageBox
函数是用来显示段信息的。它的第一个参数通常是一个窗口句柄。第二个参数就是要显示的文本字符串。第三个参数是标题。第四个参数是以前缀MB_
打头的一些常量的组合,用来表示在对话框里希望有哪种按钮,0代表只显示OK按钮。这些常量定义在WINUSER.H
中:
#define MB_OK 0
#define MB_ABORTRETRYIGNORE 2
#define MB_APPLMODAL 0
#define MB_DEFBUTTON1 0
#define MB_OKCANCEL 1
#define MB_RETRYCANCEL 5
#define MB_YESNO 4
#define MB_YESNOCANCEL 3
...还有几十个
第二和第三个参数的字符串都被包在TEXT
宏代码里,TEXT
在WINNT.H
中定义如下:
#ifdef UNICODE
#define __TEXT(q) L##q
#else
#define __TEXT(q) q
#endif
#define TEXT(q) __TEXT(q)
##
是连接两段的内容,也就是说,如果定义了UNICODE
,TEXT()
括号中的字符串
会被替换为L字符串
(如"HelloMsg"
会变成L"HelloMsg"
);如果没定义UNICODE
,那TEXT()
不起效果,括号中的字符串维持原样。
一般来说不需要把所有字符串都打包到TEXT
宏代码里,这样做是为了把程序转换成Unicode时会方便很多。
若这个Windows程序命名为HelloMsg.c
,可以执行如下命令对其编译:
gcc HelloMsg.c -o HelloMsg.exe
执行结果:
MessageBox
函数创建的这个用来提示信息的消息框小窗口,灵活性有限,下面用CreateWindow
函数创建自己的窗口,代码如下:
/*hellowin.c -- 在窗口客户区显示“Start Game”*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)){
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
//TEXT("The Hello Program"),
TEXT("Snake"),
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)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message){
case WM_CREATE:
//PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Start Game"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
这段代码中有两个函数,一个是入口函数WinMain
,另一个是窗口过程函数WndProc
。代码大多是创建窗口的一些初始化信息,后面会慢慢熟悉,这里不再详细分析。
编译执行后效果:
由于创建窗口的代码大多都是固定的,所以可以将它们封装一下,比如放在新建的
myWindow.h
中,这样代码就可以简化成:
#include <windows.h>
#include <myWindows.h>
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
myCreateWindow(hInstance, hPrevInstance, szCmdLine, iCmdShow);
}
好了,一个新的窗口创建完成了,接下来要向窗口中添加内容,完成一个贪吃蛇的小游戏。
网友评论