win32编程(一) 创建一个窗口

这篇讲如何使用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
		  );


使用一个函数来调用Createwindow,传入的参数pszClassName必须是已注册好的窗体类型,才能成功创建窗体
//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)); //窗口图标 

重新编译链接就 

 可以看到标题栏也有图标了



  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值