Windows sdk程式設計筆記

兔子,你孩子掉了發表於2020-11-02

windows 程式設計 入門



前言

入門了C語言之後,學完資料結構,學完演算法,還是寫不出想要的那種程式,而物件導向的程式設計又好像是另一個語言,除了函式結構寫法差不多之外,與C語言似乎根本不搭邊,一直再尋找C語言做UI的路徑,試過許多圖形庫之後,也嘗試過QT、MFC等等,都不友好。直到學會了SDK程式設計


一、作業系統原理

阿基米德說過,給他一個支點,他就能翹起地球。在學習一個新技能,新語言的時候,往往也需要這樣一個支點,來構建我們對於新技能、新語言的一個整體框架的理解。
在《計算機原理》之類的書籍,會告訴我們現在的主流操作是分時作業系統,會告訴我們是流轉時間片來實現的。
但是具體的實現過程,卻一頭霧水。對於硬體我們一頭霧水,但至少知道了cpu時刻向記憶體中讀取資料。而讀取過程,是CPU向記憶體傳送一個地址,然後記憶體向CPU返回這個地址的值。
而在CPU上電之後,會預設指向一個記憶體地址,比如0xfffe,記憶體就會將這個地址的值返回給cpu,這個記憶體地址存放的是一個特殊的值,它用來告訴cpu上電之後要從哪個地址開始執行程式。除了上電之外,cpu還會有其他需要,來讀取這樣存放特殊值的記憶體地址,這些存放特殊值的記憶體地址,成為向量表。
假設一個cpu上電後指向的地址是0xfffe(這個地址是硬體電路來實現指向的),如果記憶體返回的值是0x0000,那麼cpu會將自己的程式計數器的值設為0x0000,它將讀取記憶體中0x0000的指令。
除了記憶體之外,cpu還需要跟許多硬體打交道,比如顯示卡,比如音效卡,比如鍵盤,比如滑鼠,如果cpu只活在自己的世界,不管外部,就像一個專注自己事情的人,如果我們要使用滑鼠告訴它要執行哪個程式,這是滑鼠就要給cpu傳送一箇中斷請求,差不多就是拍一下cpu的肩膀,這時cpu就會回過頭來跟滑鼠對話,滑鼠是個低階的傢伙,它只會告訴cpu,我左移1畫素、我右移1畫素、我的左鍵被點下了、我的右鍵被點下了,cpu在被滑鼠拍了一下肩膀的時候,就開始完成了許多工作,包括放下手中的筆(儲存現場,實際上是將它的暫存器的值存放到某塊記憶體)然後向記憶體的中斷向量表找滑鼠的響應程式的地址,一旦拿到響應程式的地址,就會跳轉到那個地址去讀指令,這是cpu就會詢問滑鼠都有啥要說的,滑鼠就會告訴cpu,我的左鍵被點下了,但滑鼠不會記憶自己在啥位置,響應程式就需要記錄滑鼠在桌面上是啥位置,然後進行更新,然後判斷是不是點下了選單按鈕。當響應程式執行完了,就需要向cpu傳送一個return,cpu收到就知道已經處理完了,就會從剛才儲存暫存器的那塊記憶體讀回儲存的暫存器的值,這樣就回到了中斷前的狀態。

這些跳轉,都是cpu的硬體電路來實現的,作業系統,就是基於這種中斷機制,設定一個定時器,定時器在一個時間後,就會產生一箇中斷,cpu就會去向量表讀定時器的響應程式,只要給這個定時器的中斷向量填入作業系統核心程式的地址,cpu就會跑去核心執行,核心就會實現排程程式,管理硬體等等。

二、作業系統桌面與程式的關係

在dos時代,使用者與電腦之間的互動,主要是黑色背景下的命令列,使用者輸入一個指令,電腦把執行後的結果主要通過字元的形式輸出到螢幕上。
而桌面作業系統面世後,使用者與電腦之間的互動主要依靠與圖形介面,對於圖形介面的支援,linux和windows的做法不太一樣,linux把圖形介面作為使用者層的程式,而windows為了快速響應使用者的操作,把圖形介面放在核心層。
一說到windows程式設計,就會被告知是基於訊息機制的,一說到訊息機制,又會說好萊塢規則:don’t call us, we’ll call you。從這個地方入門,沒點毅力,就會從入門到放棄。

1.程式時如何切換的

先來看程式間的切換是怎麼實現的,假設系統有2個程式,p1和p2,p1在執行,現在它的時間片到點了,cpu跳轉到了核心,核心把p1執行時暫存器的值都給儲存到一個程式控制的記憶體塊裡,然後把p2程式執行時的暫存器的值返回給cpu,這樣就實現了程式的切換,如果此時p2執行完了,那麼就會return,p2就會從程式列表裡被核心給銷燬,連帶p2佔用的記憶體空間也會被回收,為了讓p2繼續執行,p2得有一個迴圈,以便把自己一直卡在程式表裡。
但是如果此時p1需要讀取使用者敲了鍵盤的值,p1如何得知呢,在實際過程中,我們敲下鍵盤的瞬間,比如qq聊天視窗,在系統裡,就被切換到了迅雷下載的程式去,如何保證我們輸入的鍵盤,不被錯誤的讀到迅雷裡去,形成神祕程式碼呢。這就需要訊號量。
前面說過,當滑鼠被點選,就會給cpu一箇中斷請求,cpu就會去跑到滑鼠響應程式,讓它問滑鼠咋地啦。鍵盤也是一樣的機制,但是當我們敲下鍵盤的時候,如何才能知道應該告訴哪個程式去響應。這就需要一個訊號量機制,當p1需要獲得一個鍵盤輸入時,p1就會設定一個阻塞,讓自己處於阻塞態,以便核心不會把自己再次放入cpu執行,直到等到使用者鍵盤得輸入時,核心發現了,就會將p1程式切換為就緒態,這是p1就能去讀取鍵盤輸入的值了。

2.系統如何管理圖形介面

但是在圖形介面下,如果圖形介面全部由程式自己來維護,效率低切開發難度高。作業系統就提供模板,由作業系統來繪製,不但使開發難度降低,還能提升重新整理的效率。但是使用者程式如何獲知使用者點選了按鈕或者移動滑鼠等等呢,於是就有了訊息機制。由作業系統來告訴使用者程式,使用者點選了按鈕,使用者程式只需要不停的去獲取屬於自己的訊息,然後按照訊息的型別來響應就行。

在windows中,使用者程式用來獲取訊息的部分,叫訊息迴圈。訊息迴圈在主函式部分,這樣就保證了使用者程式能夠卡在程式列表裡,不被銷燬。

	MSG msg;
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}

而用以響應訊息的部分,叫視窗過程。

LRESULT	CALLBACK WinProc(HWND,UINT,WPARAM,LPARAM);

可以看到,這個函式由CALLBACK來修飾,前面我們說到了中斷向量表,發生特定中斷時,cpu會從特定地址讀取一個值,來跳轉到特定的記憶體地址去讀指令(也就是跳轉到該記憶體地址繼續執行),借鑑這樣的機制,作業系統裡也會有類似的機制,只是這種機制由軟體來實現,而cpu的中斷則是由硬體來實現,CALLBACK修飾的函式,就會在系統的某個向量表新增自己的地址,如果有需要響應的訊息,作業系統就會呼叫這個函式。

經過這樣的改造之後,核心與使用者程式之間的關係,就不再是真實模式下那種平等,大家都是程式,也不是保護模式下那種核心是內部人員,可以使用特權指令。

3.影視公司與演員的自我修養

在windows下,系統與使用者程式的關係,更像是影視製作公司與演員的關係,影視公司包攬了大多的事,包攬各種雜活,使用者介面就像是一個個演員,他們按照導演的安排,各就各位。
當使用者點選了一個按鈕之後,導演就會告訴演員,使用者讓你哭,導演不會替演員哭,所以響應按鈕點選的訊息,需要在視窗過程中由程式設計師來寫,這就是演員的自我修養,如果演員沒有哭的技能,就要卡在那裡,為了不卡在那裡,演員沒有的技能,導演就會把這個劇情處理掉

return DefWindowProc(hwnd,message,wParam,lParam);

但是如果演員會哭,只是他醞釀感情需要2-3天,程式真的就會卡在那裡,這就有了工作管理員介面上看到的【未響應】

總結

所以,windows程式的框架是這樣的

#include <windows.h>
LRESULT	CALLBACK WinProc(HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance,LPSTR pCmdLine,int nCmdShow)
{
	MSG msg;
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return msg.wParam;
}

LRESULT	CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
	switch(message)
	{
		case WM_CREATE:
		{
			return 0;
		}
	return DefWindowProc(hwnd,message,wParam,lParam);
}

相關文章