windows核心程式設計--程式

Mobidogs發表於2020-04-04
程式通常被定義為一個正在執行的程式的例項,它由兩個部分組成:
一個是作業系統用來管理程式的核心物件。核心物件也是系統用來存放關於程式的統計資訊的地方。
另一個是地址空間,它包含所有可執行模組或 D L L 模組的程式碼和資料。它還包含動態記憶體分配的空間。如執行緒堆疊和堆分配空間。
 
程式是不活潑的。若要使程式完成某項操作,它必須擁有一個在它的環境中執行的執行緒,該執行緒負責執行包含在程式的地址空間中的程式碼。
當建立一個程式時,系統會自動建立它的第一個執行緒,稱為主執行緒。然後,該執行緒可以建立其他的執行緒,而這些執行緒又能建立更多的執行緒。



程式的例項控制程式碼
 
載入到程式地址空間的每個可執行檔案或 D L L 檔案均被賦予一個獨一無二的例項控制程式碼。可執行檔案的例項作為 ( w ) Wi n M a i n 的第一個引數 h i n s t E x e 來傳遞。對於載入資源的函式呼叫來說,通常都需要該控制程式碼的值。例如,若要從可執行檔案的映象來載入圖示資源,需要呼叫下面這個函式:
HICON LoadIcon( HINSTANCE hinst, PCTSTR pszIcon);
L o a d I c o n 的第一個引數用於指明哪個檔案(可執行檔案或 D L L 檔案)包含你想載入的資源。
注意 : 實際情況說明, H M O D U L E H I N S TA N C E 是完全相同的物件。如果函式的文件指明需要一個 H M O D U L E ,那麼可以傳遞一個 H I N S TA N C E ,反過來,如果需要一個 H I N S TA N C E ,也可以傳遞一個 H M O D U L E 。之所以存在兩個資料型別,原因是在 1 6 Wi n d o w s 中, H M O D U L E H I N S TA N C E 用於標識不同的東西。
G e t M o d u l e H a n d l e 函式返回可執行檔案或 D L L 檔案載入到程式的地址空間時所用的控制程式碼 / 基地址:
HMODULE GetModuleHandle( PCTSTR pszModule);
當呼叫該函式時,你傳遞一個以 0 結尾的字串,用於設定載入到呼叫程式的地址空間的可執行檔案或 D L L 檔案的名字。如果系統找到了指定的可 執行檔案或 D L L 檔名, G e t M o d u l e H a n d l e 便返回該可執行檔案或 D L L 檔案映象載入到的基地址。如果系統沒有找到該檔案,則 返回 N U L L 。也可以呼叫 G e t M o d u l e H a n d l e ,為 p s z M o d u l e 引數傳遞 N U L L G e t M o d u l e H a n d l e 返回撥 用的可執行檔案的基地址。
 
程式的命令列
當一個新程式建立時,它要傳遞一個命令列。該命令列幾乎永遠不會是空的,至少用於建立新程式的可執行檔案的名字是命令列上的第一個標記。當 C 執行期的啟動程式碼開始執行的時候,它要檢索程式的命令列,跳過可執行檔案的名字,並將指向命令列其餘部分的指標傳遞給 Wi n M a i n p s z C m d L i n e 引數。
程式的環境變數
每個程式都有一個與它相關的環境塊。環境塊是程式的地址空間中分配的一個記憶體塊。每個環境塊都包含一組字串,其形式如下:
VarName1=VarValue1/0
VarName2=VarValue2/0
VarName3=VarValue3/0
...
VarNameX=VarValueX/0
/0
每個字串的第一部分是環境變數的名字,後跟一個等號,等號後面是要賦予變數的值。
DWORD GetEnvironmentVariable(
   PCTSTR pszName,
   PTSTR pszValue,
   DWORD cchValue);
當呼叫 G e t E n v i r o n m e n t Va r i a b l e 時, p s z N a m e 指向需要的變數名, p s z Va l u e 指向用於存放變數值的快取, c c h Va l u e 用於指明快取的大小(用字元數來表示)。該函式可以返回拷貝到快取的字元數,如果在環境中找不到該變數名,也可以返回 0
BOOL SetEnvironmentVariable(
   PCTSTR pszName,
   PCTSTR pszValue);
該函式用於將 p s z N a m e 引數標識的變數設定為 p s z Va l u e 引數標識的值。如果帶有指定名字的變數已經存在, S e t E n v i r o n m e n t Va r i a b l e 就修改該值。如果指定的變數不存在,便新增該變數,如果 p s z Va l u e N U L L ,便從環境塊中刪除該變數。
程式的親緣性
一般來說,程式中的執行緒可以在主計算機中的任何一個 C P U 上執行。但是一個程式的執行緒可能被強制在可用 C P U 的子集上執行。這稱為程式的親緣性,
程式的錯誤模式
程式可以告訴系統如何處理每一種錯誤。方法是呼叫 S e t E r r o r M o d e 函式:
UINT SetErrorMode(UINT fuErrorMode);
f u E r r o r M o d e 引數是表 4 - 3 的任何標誌按位用 O R 連線在一起的組合。
表4-3 fuError Mode 引數的標誌
標誌
說明
SEM_FAILCRITICALERRORS
系統不顯示關鍵錯誤控制程式碼訊息框,並將錯誤返回給呼叫程式
SEM_NOGOFAULTERRORBOX
系統不顯示一般保護故障訊息框。本標誌只應該由採用異常情況處理程式來處理一般保護(G P)故障的除錯應用程式來設定
SEM_NOOPENFILEERRORBOX
當系統找不到檔案時,它不顯示訊息框
SEM_NOALIGNMENTFAULTEXCEPT
系統自動排除記憶體沒有對齊的故障,並使應用程式看不到這些故障。本標誌對x 8 6處理器不起作用
程式的當前驅動器和目錄
通過呼叫下面兩個函式,執行緒能夠獲得和設定它的程式的當前驅動器和目錄:
				
						 
				
		
DWORD GetCurrentDirectory(
   DWORD cchCurDir,
   PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
CreateProcess 函式
可以用 C r e a t e P r o c e s s 函式建立一個程式:
BOOL CreateProcess(
   PCTSTR pszApplicationName,
   PTSTR pszCommandLine,
   PSECURITY_ATTRIBUTES psaProcess,
   PSECURITY_ATTRIBUTES psaThread,
   BOOL bInheritHandles,
   DWORD fdwCreate,
   PVOID pvEnvironment,
   PCTSTR pszCurDir,
   PSTARTUPINFO psiStartInfo,
   PPROCESS_INFORMATION ppiProcInfo);
當一個執行緒呼叫 CreateProcess 時,系統就會建立一個程式核心物件,其初始使用計數是 1
當第一個引數為 NULL , C r e a t e P r o c e s s 也按下面的順序搜尋該可執行檔案:
1) 包含呼叫程式的 . e x e 檔案的目錄。
2) 呼叫程式的當前目錄。
3) Wi n d o w s 的系統目錄。
4) Wi n d o w s 目錄。
5) PAT H 環境變數中列出的目錄。
當然,如果檔名包含全路徑,系統將使用全路徑來檢視可執行檔案,並且不再搜尋這些目錄。如果系統找到了可執行檔案,那麼它就建立一個新程式,並將可執行檔案的程式碼和資料對映到新程式的地址空間中。然後系統將呼叫 C / C + + 執行期啟動例程。正如前面我們講過的那樣, C / C + + 執行期啟動例程要檢視程式的命令列,並將地址作為 ( w ) Wi n M a i n p s z C m d L i n e 引數傳遞給可執行檔案的名字後面的第一個引數。
終止程式的執行
若要終止程式的執行,可以使用下面四種方法:  
主執行緒的進入點函式返回(最好使用這個方法)。  
程式中的一個執行緒呼叫 E x i t P r o c e s s 函式(應該避免使用這種方法)。  
另一個程式中的執行緒呼叫 Te r m i n a t e P r o c e s s 函式(應該避免使用這種方法)。  
程式中的所有執行緒自行終止執行(這種情況幾乎從未發生)。  
主執行緒的進入點函式返回
始終都應該這樣來設計應用程式,即只有當主執行緒的進入點函式返回時,它的程式才終止執行。這是保證所有執行緒資源能夠得到正確清除的唯一辦法。讓主執行緒的進入點函式返回,可以確保下列操作的實現:
該執行緒建立的任何 C + + 物件將能使用它們的解構函式正確地撤消。  
作業系統將能正確地釋放該執行緒的堆疊使用的記憶體。  
系統將程式的退出程式碼(在程式的核心物件中維護)設定為進入點函式的返回值。  
系統將程式核心物件的返回值遞減 1
ExitProcess 函式
當程式中的一個執行緒呼叫 E x i t P r o c e s s 函式時,程式便終止執行:
				
						VOID ExitProcess(UINT fuExitCode);
				
		
當主執行緒的進入點函式( WinMain wWinMain main wmain )返回時,它將返回給 C / C + + 執行期啟動程式碼,它能正確地清除該程式使用的所有的 C 執行期資源。當 C 執行期資源被釋放之後, C 執行期啟動程式碼就顯式呼叫 E x i t P r o c e s s ,並將進入點函式返回的值傳遞給它。這解釋了為什麼只需要主執行緒的進入點函式返回,就能夠終止整個程式的執行。請注意,程式中執行的任何其他執行緒都隨著程式而一道終止執行。
注意,呼叫 E x i t P r o c e s s E x i t T h r e a d 可使程式或執行緒在函式中就終止執行。就作業系統而言,這很好,程式或執行緒的所有作業系統資源都將被全部清除。但是, C / C + + 應用程式應該避免呼叫這些函式,因為 C / C + + 執行期也許無法正確地清除。
TerminateProcess 函式
呼叫 Te r m i n a t e P r o c e s s 函式也能夠終止程式的執行:
BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode);
該函式與 E x i t P r o c e s s 有一個很大的差別,那就是任何執行緒都可以呼叫 Te r m i n a t e P r o c e s s 來終止另一個程式或它自己的程式的執行。 h P r o c e s s 引數用於標識要終止執行的程式的控制程式碼。
只有當無法用另一種方法來迫使程式退出時,才應該使用 Te r m i n a t e P r o c e s s 。終止執行的程式絕對得不到關於它將終止執行的任何通知,因為應用程式無法正確地清除,並且不能避免自己被撤消(除非通過正常的安全機制)。
程式終止執行時出現的情況
當程式終止執行時,下列操作將啟動執行:  
1) 程式中剩餘的所有執行緒全部終止執行。  
2) 程式指定的所有使用者物件和 G D I 物件均被釋放,所有核心物件均被關閉(如果沒有其他 程式開啟它們的控制程式碼,那麼這些核心物件將被撤消。但是,如果其他程式開啟了它們的控制程式碼, 核心物件將不會撤消)。  
3) 程式的退出程式碼將從 S T I L L _ A C T I V E 改為傳遞給 E x i t P r o c e s s Te r m i n a t e P r o c e s s 的程式碼。  
4) 程式核心物件的狀態變成收到通知的狀態(關於傳送通知的詳細說明,參見第 9 章)。系 統中的其他執行緒可以掛起,直到程式終止執行。  
5) 程式核心物件的使用計數遞減 1
注意,程式的核心物件的壽命至少可以達到程式本身那麼長,但是程式核心物件的壽命可能大大超過它的程式壽命。當程式終止執行時,系統能夠自動確定它的核心物件的使用計數。如果使用計數降為 0 ,那麼沒有其他程式擁有該物件開啟的控制程式碼,當程式被撤消時,物件也被撤消。
子程式
子程式 , 能夠處理比執行緒更復雜的東西 , 也能夠保持相對的獨立 , 但是就會有程式間的資料共享 , Wi n d o w s 提供了若干種方法,以便在不同的程式中間傳送資料,比如動態資料交換( D D E )、 O L E 、管道和郵箱等。共享資料最方便的方法之一是,使用記憶體對映檔案 . 大多數情況下,應用程式將另一個程式作為獨立的程式來啟動。這意味著程式建立和開始執行後,父程式並不需要與新程式進行通訊,也不需要在完成它的工作後父程式才能繼續執行。這就是 E x p l o r e r 的執行方式。當 E x p l o r e r 為使用者建立一個新程式後,它並不關心該程式是否繼續執行,也不在乎使用者是否終止它的執行。
若要放棄與子程式的所有聯絡, E x p l o r e r 必須通過呼叫 C l o s e H a n d l e 來關閉它與新程式及它的主執行緒之間的控制程式碼。下面的程式碼示例顯示瞭如何建立新程式以及如何讓它以獨立方式來執行:
PROCESS_INFORMATION pi;
//Spawn the child process.
BOOL fSuccess = CreateProcess(..., π);
if(fSuccess)
{
   //Allow the system to destroy the process & thread kernel
   //objects as soon as the child process terminates.
   CloseHandle(pi.hThread);
   CloseHandle(pi.hProcess);
}
 

相關文章