一.字符集的编码
对字符集的编码可以使用8位和16位进行编码,ASCII编码使用了一个字节进行编码,Unicode用两个字节进行编码,最多可以表示65536个字符;ASCII码当初的设计只是针对美国英语设计的。对于其他国家而言,一个字节最多只能够256个字符,对于汉字,最常用的汉字就多大6000多个。显然用一个字节并不能够完全表示所有的汉字;因此,16位编码可以表示多国言语,对于国际语言的转换变得方便。
宽字符集:使用两个字节对字符进行编码,计算机判断宽字符集的字符串是否结束,通过连续两个‘\0’进行识别,即当出现两个连续的字符'\0'时就认为此字符串结束;unicode属于宽字符集。
多字节字符集:一个字节或者两个字节,当遇到‘\0’就判定是字符串结尾。
因此,使用前必须确定使用的字符集,不然会带来一些不必要的麻烦;windows程序是如何兼容两种字符集的,在tchar.h头文件中有如下的宏定义
#ifdef _UNICODE
#define _tcslen wcslen
#deine TCHAR wchar_t
#define LPTSTR wchar_t *
#define _T(x) L##x
#else
#define _tcslen strlen
#deine TCHAR char
#define LPTSTR char *
#define _T(x) x
#endif
这里只列出部分宏定义,因此我们可以通过宏定义实现两种编码的兼容;幸运地是,这些微软已经帮我们做好了,我们只要会用就行了。
二.预备知识
(1)句柄(HANDLE):句柄是windows中一个必要重要的概念,是windows程序中的一种资源标识符,通过句柄可以区分不同的资源,从而可以方便我们管理资源。windows中有很多资源,包括窗口(HWND)、光标(HCURSOR)、图标(HICON)、画刷(HBRUSH)等。
(2)窗口(window):窗口分为客户区和非客户区;非客户无包括标题栏、菜单栏、工具栏和窗口边框,客户区是用来与客户打交道的区域
(3)消息:windows程序设计是一种事件驱动的方式,基于消息的;操作系统感知特定窗口的事件,并把它打包成消息,放到应用程序的消息队列中,然后应用程序从消息队列中取出消息并对此进行响应。当应用程序需要响应某一个消息时,从消息队列中取出消息,并把此消息分发给操作系统,由操作系统完成对应用程序的消息处理函数的调用。
进队消息:进入消息队列。
不进队消息:系统调用窗口过程时直接发送给窗口。
(4)winmain():winmain函数是windows程序的入口点函数,声明形式如下:
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state);
第一个参数:应用程序的实例句柄,用于标识不同的应用程序
第二个参数:当前实例的前一个实例的句柄
第三个参数:命令行参数;如一个文本应用程序,当文本应用程序打开一个文本文档时,需要把文本文档的路径传给文本应用程序
第四个参数:指定程序的窗口应该如何显示,最大化、最小化、隐藏等。
三.windows内部程序内部运行机制
一般windows程序要经过以下流程,注册窗口、创建窗口、显示和更新窗口、进入消息循环、消息处理!
1.注册窗口类
WNDCLASSEX结构体:
typedef struct _WNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
总共有12个属性:以EX为后缀的函数,代表是原先函数的扩展版本
cbSize:WNDCALSSEX结构体类型的大小 sizeof(WNDCLASSEX)
style:窗口的样式 ;一般有CS_VERDRAW,CS_HREDRAW,
cbWndEXtra: 指定窗口附加内存,一般为0
cbClsEXtra:指定窗口类型额外的内存,一般为0
lpfnWndProc:窗口回调函数
hInstance:窗口实例句柄
hIcon:窗口类的图标句柄 LoadIcon();函数装载
hCursor:窗口类光标句柄LoadCursor()函数装载
hbrBackground:背景颜色,一般用HGDIOBJ GetStockObject(int);获取标准画刷;
lpzsMenuName:菜单资源的名字,菜单资源的ID号,一般需要用MAKEINTERSOURCE宏进行类型转换;
lpszClassName:窗口类的名字
hIconSm:
然后调用ATOM RegisterClassEx( CONST WNDCLASSEX *)对窗口进行注册;
const TCHAR * lpszClassName= _T("MyWinows");
WNDCLASSEX wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.cbSize=sizeof(WNDCLASSEX);
wndcls.hbrBackground=(HBRUSH)COLOR_GRAYTEXT;
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hIconSm=NULL;
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WindowProc;
wndcls.lpszClassName = lpszClassName;
wndcls.lpszMenuName=NULL;
wndcls.style=CS_VREDRAW | CS_HREDRAW;
//12条属性
bool nRet=::RegisterClassEx(&wndcls);
if (!nRet)
{
::MessageBox(NULL,_T("注册窗口类失败"),_T("注册窗口"),MB_OK);
return FALSE;
}
2.创建窗口
HWND CreateWindowEx( DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // window-creation data);
窗口风格dwStyle是以WS_开始的宏,在window.h文件中;(x,y)窗口左上角的坐标,nWidth,nHeight分别是窗口的宽度和高度;
lpParam:作为WM_CREATE消息的附加参数lpParam传入的数据指针;
HWND hWnd=::CreateWindowEx(0,lpszClassName,_T("时间的流逝"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
if (NULL==hWnd)
{
::MessageBox(NULL,_T("创建窗口失败"),_T("创建窗口"),MB_OK);
return FALSE;
}
3.显示和更新窗口
::ShowWindow(hWnd,SW_SHOW);
::UpdateWindow(hWnd);
窗口的显示状态:SW_开头的宏;
4.消息循环
MSG msg;
while (::GetMessage(&msg,NULL,NULL,NULL))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
当GetMessage接收到除WM_QUIT消息之外都返回非零值,即一直在消息循环;
TranslateMessage();用于虚拟键转换成字符消息,比如可以把WM_KEYDOWN和WM_KEYUP转换成WM_CHAR消息,然后投递到消息队列中。
DispatchMessage()把消息分发给窗口过程,由窗口过程函数对消息进行处理,实际上把消息回传给操作系统;
windows消息处理机制:
(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
(2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条条的消息。取出后,以对消息进行一些预处理,如放弃对某些消息的响应,或者调用TranslateMessage产生新的消息
(3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage函数总能进行正确的传递。
(4)系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。
5.消息处理
LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam )
{
switch (uMsg)
{
case WM_CLOSE:
::DestroyWindow(hWnd);
break;
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_PAINT:
HDC hdc;
RECT clientRect;
GetClientRect(hWnd,&clientRect);
PAINTSTRUCT ps;
hdc=BeginPaint(hWnd,&ps); //获取设备描述表
//DrawText(hdc,_T("www.lppmww.com"),_tcslen(_T("www.lppmww.com")),&clientRect,DT_SINGLELINE|DT_BOTTOM);
TextOut(hdc,100,0,_T("www.lppmww.com"),_tcslen(_T("www.lppmww.com")));
EndPaint(hWnd,&ps);
break;
default:
break;
}
return ::DefWindowProc(hWnd,uMsg,wParam,lParam); //默认的窗口过程
}
系统是通过窗口过程函数的地址来调用窗口过程函数的;beginPaint和endPaint只能在WM_PAINT消息的响应函数中;
windows绘图:
一般我们生活中要完成绘图过程,需要以下几个元素:画家、画图工具、画图技巧;windows程序与之相对应的是
画家:设备描述表
画图工具:画笔、画刷、位图等
画图技巧:windows提供的API函数,画点、划线函数;
在windows中,我们要想绘图,
首先应该获取设备描述表,把绘图工具选入设备描述表,然后调用windows提供的API函数;
获取设备描述表的方法:
(1)beginPaint()和EndPaint(),在WM_PAINT消息响应函数里面用
(2)GetDC()和ReleaseDC(),在客户区绘图
(3)GetWindowDC()和ReleaseDC() 在非客户区绘图
代码:
#include <tchar.h>
#include <Windows.h>
//编写兼容两种字符的winMain
LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam );
//编写兼容两种字符的winMain
int WINAPI _tWinMain( HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPTSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
const TCHAR * lpszClassName= _T("MyWinows");
WNDCLASSEX wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.cbSize=sizeof(WNDCLASSEX);
wndcls.hbrBackground=(HBRUSH)COLOR_GRAYTEXT;
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hIconSm=NULL;
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WindowProc;
wndcls.lpszClassName = lpszClassName;
wndcls.lpszMenuName=NULL;
wndcls.style=CS_VREDRAW | CS_HREDRAW;
//12条属性
bool nRet=::RegisterClassEx(&wndcls);
if (!nRet)
{
::MessageBox(NULL,_T("注册窗口类失败"),_T("注册窗口"),MB_OK);
return FALSE;
}
HWND hWnd=::CreateWindowEx(0,lpszClassName,_T("时间的流逝"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
if (NULL==hWnd)
{
::MessageBox(NULL,_T("创建窗口失败"),_T("创建窗口"),MB_OK);
return FALSE;
}
::ShowWindow(hWnd,SW_SHOW);
::UpdateWindow(hWnd);
MSG msg;
while (::GetMessage(&msg,NULL,NULL,NULL))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam )
{
switch (uMsg)
{
case WM_CLOSE:
::DestroyWindow(hWnd);
break;
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_PAINT:
HDC hdc;
RECT clientRect;
GetClientRect(hWnd,&clientRect);
PAINTSTRUCT ps;
hdc=BeginPaint(hWnd,&ps); //获取设备描述表
//DrawText(hdc,_T("www.lppmww.com"),_tcslen(_T("www.lppmww.com")),&clientRect,DT_SINGLELINE|DT_BOTTOM);
TextOut(hdc,100,0,_T("www.lppmww.com"),_tcslen(_T("www.lppmww.com")));
EndPaint(hWnd,&ps);
break;
default:
break;
}
return ::DefWindowProc(hWnd,uMsg,wParam,lParam); //默认的窗口过程
}