这篇讲如何使用windows api 建立一个窗体,并且给它加上图标
一 编译器
我使用的是VC++6.0,使用命令行编译的, 下面讲在本篇编译过程中用到的三个工具
编译器 CL.EXE
将原代码编译目标代码. OBJ文件.
链接器 Link.EXE
将OBJ文件和库函数链接.生成EXE.
资源编译器 rc.exe
将资源脚本编译成可以链接的文件(RES).
二 常用的头文件和库
windows.h包含了windows 常用的定义等.其他,还包含了一些其他的 头文件:
windef.h - 定义了各种的数据类型
winbase.h- 定义了kernel的相关函数
wingdi.h - 定义了绘图和文字
winuser.h- 窗口 控件等函数
winnt.h - 提供Unicode支持
kernel32.lib - 提供进程/线程/内存等等 API函数支持
user32.lib - 包括窗户/界面等等. (本篇中由于要建立窗体 所有要使用这个库)
gdi32.lib - 提供绘图/文字等。
三 Hello World程序
1 入口函数
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pszCmdLine,
int nShowCmd )
函数的参数:
hInstance - 应用程序的实例句柄
hPrevInstance - 该应用程序的前一个实例句柄
pszCmdLine - 命令行参数
nShowCmd - 窗口的显示方式
该函数的作用和main是相同的
LPSTR LPCSTR 的意思:
L -long (32字节 针对以前的16位)
P -pointer
C const STR字符串
T -TCHAR - char
2 MessageBox
在winMain中显示一个MessageBox #include <windows.h>
int WinMain(
HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPSTR pszcmdline,
int nShowCmd)
{
MessageBox(NULL,"Hello World","FirstWin32",MB_OK);
return 0;
}
3 编译环境的准备
在VC98的BIN目录下VCVARS32.BAT ,作用是添加环境变量
添加之后你就可以在CMD中 使用编译器 连接器了
3.1 编译器 CL.EXE
CL的常用选项/c 只编译不链接
/TC 编译C文件
/TP 编译C++文件
如果不指定,CL会根据文件类型自动区分
/I 头文件所在路径
字符大小写敏感
用法:
cl helloworld.c /c
生成一个helloworld.obj
3.2 链接LINK.EXE
LINK常用选项:/out:文件名,链接成指定文件名的程序
/LIBPATH:LIB文件存放的路径, /LIBPATH:"E:\XXX"
/SUBSYSTEM: CONSOLE/WINDOW
字符大小写不敏感
D:\code\helloworld>link helloworld.obj 时出错了
helloworld.obj : error LNK2001: unresolved external symbol __imp__MessageBoxA@16
此时MESSAGEBOX需要的库也连接进来
D:\code\helloworld>link helloworld.obj user32.lib
生成了 helloworld.exe
查看帮助
cl -?
link -?
3.3 执行
正常执行或带参数执行
四 第一个窗口程序
4.1 入口函数WinMain
int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pszCmdLine,
int nShowCmd )
{
}
hInstance是这本应用程序的句柄, 在创建窗体时会用到
4.2 定义一个 窗口处理函数
当窗口处理消息事件时,系统调用该函数
LRESULT CALLBACK WindowProc(
HWND hwnd, // 窗口的句柄
UINT uMsg, // 消息的ID号
WPARAM wParam, // 消息所对应的参数
LPARAM lParam // 消息所对应的参数
);
如果图简单,不想做处理的话,可以直接返回缺省的消息处理程序
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
//调用缺省的消息处理程序
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
4.3 注册 窗口类型
窗口类型用一个结构体表示
typedef struct _WNDCLASS {
UINT style; //窗口类的风格
WNDPROC lpfnWndProc; //窗口处理函数
int cbClsExtra; //窗口类附加数据大小
int cbWndExtra; //窗口附加数据大小
HANDLE hInstance; //当前应用程序的实例句柄
HICON hIcon; //窗口图标
HCURSOR hCursor; //窗口的鼠标
HBRUSH hbrBackground; //窗口的背景画刷
LPCTSTR lpszMenuName; //菜单
LPCTSTR lpszClassName; //类型名称 (窗口的名字)
} WNDCLASS;
其中style有如下参数:
C_HREDRAW 如果当前窗口被横向拉伸会重新刷新窗口
C_VREDRAW 如果当前窗口被纵向拉伸会重新刷新窗口
CS_DBCLICKS 相应 双击窗口
定义完了窗口类型,再调用 注册函数 RegisterClass
//3 注册"窗口类型"
BOOL MyRegister( LPSTR pszClassName )
{
WNDCLASS wc = {0};
ATOM nAtom = 0;
//构造注册窗口的参数
wc.style = CS_VREDRAW|CS_HREDRAW; //窗口风格
wc.lpfnWndProc = WndProc;//窗口处理函数 (需要自己定义!)
wc.cbClsExtra = 0;//窗口类附加数据大小
wc.cbWndExtra = 0;//窗口附加数据大小
wc.hInstance = g_hInst;//当前应用程序的实例句柄
wc.hIcon = LoadIcon(g_hInst,MAKEINTRESOURCE(100)); //窗口图标
wc.hCursor = NULL; //窗口鼠标
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE);//窗口背景色
wc.lpszMenuName = NULL;//菜单名称
wc.lpszClassName = pszClassName; //类型名称
//注册窗口
nAtom = RegisterClass( &wc );
if( 0 == nAtom )
{
MessageBox( NULL, "Register Failed",
"Error", MB_OK|MB_ICONWARNING );
return FALSE;
}
else
{
MessageBox( NULL, "Register Successed",
"Successed", MB_OK );
}
return TRUE;
}
注册完窗体类型,就可以在创建窗口时使用这个窗体类型了
4.4 创建窗口
HWND CreateWindow(
LPCTSTR lpClassName, //窗口类型名称
LPCTSTR lpWindowName, //窗口名称
DWORD dwStyle, //窗口类型
int x, //窗口的左上角X坐边
int y, //窗口的左上角X坐边
int nWidth, //窗口的宽度
int nHeight, //窗口的高度
HWND hWndParent, //父窗口句柄
HMENU hMenu, //窗口菜单句柄
HANDLE hInstance, //应用程序的实例句柄
LPVOID lpParam //创建的参数,一般为NULL
);
//4 窗口创建
HWND MyCreate( LPSTR pszClassName )
{
HWND hWnd = NULL;
//创建窗口
hWnd = CreateWindow( pszClassName, //窗口的类型名称(需要先注册)
"HelloWnd",//窗口名称(标题)
WS_OVERLAPPEDWINDOW,//窗口风格
100, 100, 300, 500, NULL, NULL, //位置 父窗口句柄 菜单句柄
g_hInst,//应用程序实例句柄 WinMain中传入
NULL ); //创建的参数 一般为空
if( NULL == hWnd )
{
MessageBox( NULL, "CreateWnd Failed",
"Error", MB_OK );
return NULL;
}
MessageBox( NULL, "CreateWnd Successed",
"Successed", MB_OK );
return hWnd;
}
4.5 显示窗口
显示窗口分两部,显示和是刷新
ShowWindow 显示窗口
BOOL ShowWindow(
HWND hWnd, //显示的窗口句柄
int nCmdShow //显示的方式
);
UpdateWindow 刷新窗口
//5 显示窗口
void DisplayWnd( HWND hWnd )
{
//显示
ShowWindow( hWnd, SW_SHOW );
//刷新
UpdateWindow( hWnd );
}
hWnd是创建窗体时返回的窗体句柄
显示窗体之后,你会发现窗体一闪而过,就显示了一秒不到. 原因是要没有加入消息处理
4 .6 消息处理
GetMessage 获取消息BOOL GetMessage(
LPMSG lpMsg, // address of structure with message
HWND hWnd, // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
DispatchMessage 派发消息
LONG DispatchMessage(
CONST MSG *lpmsg // pointer to structure with message
);
//6 消息处理
void Message( )
{
MSG msg = { 0 };
//消息循环处理,获取消息
while( GetMessage( &msg, NULL, 0, 0 ) )
{
//派发消息
DispatchMessage( &msg );
}
}
使用一个循环,不停接受消息.再派发出去 ,窗体就可以保持住了.
4.7 程序退出
但此时窗体退出后,在进程表中仍存在,是因为没有真正销毁这个应用,于是在winProc消息处理函数中加点东西:
窗口销毁时,退出程序。在WndProc中实现。
WM_DESTROY: 当窗口销毁时,会调用WndProc 将WM_DESTROY消息传递给使用者。所以一旦接收到WN_DESTROY需要销毁窗口.
VOID PostQuitMessage(
int nExitCode // exit code
);
函数功能:该函数向系统表明有个线程有终止请求。通常用来响应WM_DESTROY消息。
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
//判断消息ID
switch( nMsg )
{
case WM_DESTROY: //窗口销毁的消息
PostQuitMessage( 0 ); //发送退出消息
return 0;
}
//调用缺省的消息处理程序
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
完整的代码如下:
#include <windows.h>
HINSTANCE g_hInst = NULL;
//2 窗口处理函数
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
//判断消息ID
switch( nMsg )
{
case WM_DESTROY: //窗口销毁的消息
PostQuitMessage( 0 ); //发送退出消息
return 0;
}
//调用缺省的消息处理程序
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
//3 注册"窗口类型"
BOOL MyRegister( LPSTR pszClassName )
{
WNDCLASS wc = {0};
ATOM nAtom = 0;
//构造注册窗口的参数
wc.style = CS_VREDRAW|CS_HREDRAW; //窗口风格
wc.lpfnWndProc = WndProc;//窗口处理函数 (需要自己定义!)
wc.cbClsExtra = 0;//窗口类附加数据大小
wc.cbWndExtra = 0;//窗口附加数据大小
wc.hInstance = g_hInst;//当前应用程序的实例句柄
wc.hIcon = LoadIcon(g_hInst,MAKEINTRESOURCE(100)); //窗口图标
wc.hCursor = NULL; //窗口鼠标
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE);//窗口背景色
wc.lpszMenuName = NULL;//菜单名称
wc.lpszClassName = pszClassName; //类型名称
//注册窗口
nAtom = RegisterClass( &wc );
if( 0 == nAtom )
{
MessageBox( NULL, "Register Failed",
"Error", MB_OK|MB_ICONWARNING );
return FALSE;
}
else
{
MessageBox( NULL, "Register Successed",
"Successed", MB_OK );
}
return TRUE;
}
//4 窗口创建
HWND MyCreate( LPSTR pszClassName )
{
HWND hWnd = NULL;
//创建窗口
hWnd = CreateWindow( pszClassName, //窗口的类型名称(需要先注册)
"HelloWnd",//窗口名称(标题)
WS_OVERLAPPEDWINDOW,//窗口风格
100, 100, 300, 500, NULL, NULL, //位置 父窗口句柄 菜单句柄
g_hInst,//应用程序实例句柄 WinMain中传入
NULL ); //创建的参数 一般为空
if( NULL == hWnd )
{
MessageBox( NULL, "CreateWnd Failed",
"Error", MB_OK );
return NULL;
}
MessageBox( NULL, "CreateWnd Successed",
"Successed", MB_OK );
return hWnd;
}
//5 显示窗口
void DisplayWnd( HWND hWnd )
{
//显示
ShowWindow( hWnd, SW_SHOW );
//刷新
UpdateWindow( hWnd );
}
//6 消息处理
void Message( )
{
MSG msg = { 0 };
//消息循环处理,获取消息
while( GetMessage( &msg, NULL, 0, 0 ) )
{
//派发消息
DispatchMessage( &msg );
}
}
//1 入口函数
int WINAPI WinMain( HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR pszCmdLine,
int nShowCmd )
{
HWND hWnd = NULL;
g_hInst = hInst;
//注册窗口类型
MyRegister( "MyWnd" );//将MyWnd作为classname注册了一个窗口
//创建注册类型的窗口
hWnd = MyCreate( "MyWnd" ); //创建的classname 必须是我们已经注册好的类型 才能创建成功
//显示窗口
DisplayWnd( hWnd );
//消息处理
Message( );
return 0;
}
如果你细心,你会发现在注册窗体时
wc.hIcon = LoadIcon(g_hInst,MAKEINTRESOURCE(100)); //窗口图标
这是设置应用程序的图标 接下来就讲
如果你想将上面的代码编译成功,先暂时把hIcon设置成 NULL
五 资源的处理
1 资源和资源脚本
资源:ICON/CUSRUR/BITMAP/MENU......资源脚本: 扩展名为rc文件。
res.rc 写法
100 ICON "Book.ico"
编号 类型 位置
同时将Bool.ico拷贝到当前文件夹下来
2 编译资源
使用rc.exe编译资源,生成RES文件rc res.rc
生成res.RES
3 链接资源
使用link.exe将RES文件链接到程序当中
link helloworld.obj res.RES user32.lib
再看生成helloworld.exe 它有图标了 但执行helloworld是标题栏上没有图标
4 ICON的使用
LoadIcon从指定程序中加载ICON资源。HICON LoadIcon(HINSTANCE hInstance, //应用程序实例句柄
LPCTSTR lpIconName); //图标的名称或者标识符 MAKEINTRESOURCE(int) 将数字转换成字符串
将helloworld.c中的注册窗体类型中的hIcon设置成下面的样子
wc.hIcon = LoadIcon(g_hInst,MAKEINTRESOURCE(100)); //窗口图标
可以看到标题栏也有图标了