一.字符集的編碼
對字符集的編碼可以使用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); //默認的窗口過程
}