windows程序運行機制

一.字符集的編碼

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章