1. 程式人生 > WINDOWS開發 >win32郵槽和程序的概念(三)

win32郵槽和程序的概念(三)

接《win32管道技術和程序通訊例項(二)》,win32還有一種方法實現程序的通訊,就是郵槽。

郵槽

郵槽是基於廣播通訊體系設計出來的,擁有一個服務端程式和一個客戶端程式,服務端用來接收資料,客戶端用來發送資料。

郵槽服務端編寫步驟:

①使用CreateMailslot建立一個郵槽並且指定郵槽的名字和返回郵槽服務端的控制代碼。如果郵槽名稱已經存在,那麼會發生錯誤。

HANDLE CreateMailslot(
  LPCTSTR lpName,// pointer to string for mailslot name
  DWORD nMaxMessageSize,// maximum message size
DWORD lReadTimeout,// milliseconds before read time-out LPSECURITY_ATTRIBUTES lpSecurityAttributes // pointer to security structure );

第一個引數郵槽的名字,它的格式是\\.\mailslot\[path]name;第二個引數,寫入郵槽的最大位元組數,如果設定為0表示,大小不限制(據說我們應該把傳輸的位元組控制在424位元組以下);第三個引數,以毫秒為單位設定超時時間,也可以使用0或者MAILSLOT_WAIT_FOREVER,前者表示當前沒有訊息就立即返回,後者表示一直等待訊息。第四個引數,指向安全屬性結構,主要設定建立的物件能都被子程序繼承,可以設定為NULL,表示不可繼承。(這樣看來,郵槽也是個核心物件?)

②使用ReadFile讀取資料。

郵槽客戶端編寫程式

①使用CreateFile開啟郵槽

②使用WriteFile寫入資料

看上去挺簡單的,下面是一個例項:

服務端:

#include<windows.h>
#include<iostream.h>

int main(){
    //建立郵槽
    HANDLE hMailslot=CreateMailslot("\\\\.\\mailslot\\MailslotForTest",0,MAILSLOT_WAIT_FOREVER,NULL);
    if(INVALID_HANDLE_VALUE==hMailslot){
        cout
<<"Fail to create mailslot..."<<endl; } char readBuff[100]; ZeroMemory(readBuff,sizeof(readBuff)); if(!ReadFile(hMailslot,readBuff,100,NULL)){ cout<<"Fail to read from mailslot..."<<endl; CloseHandle(hMailslot); } cout<<readBuff<<endl; CloseHandle(hMailslot); return 0; }

客戶端:

#include<windows.h>
#include<iostream.h>

int main(){
    HANDLE hMailslot=CreateFile("\\\\.\\mailslot\\MailslotForTest",GENERIC_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hMailslot){
        cout<<"Fail to open mailslot..."<<endl;
        return 0;
    }
    
    char *writeBuff="Hello!I‘am Client,I‘ll tell u something...";
    if(!WriteFile(hMailslot,writeBuff,strlen(writeBuff),NULL)){
        cout<<"Fail to write to mailslot..."<<endl;
        CloseHandle(hMailslot);
        return 0;
    }

    CloseHandle(hMailslot);
    return 0;
}

執行的時候先開啟服務端,讓它先建立一個郵槽並一直等待客戶端開啟郵槽,並往郵槽寫入資料;再開啟客戶端開啟郵槽並且寫入資料。

程序的概念

前面瞭解了程序是一個核心物件,程序的建立,子程序如何繼承父程序的控制代碼表,以及程序的通訊,程序的誕生到死亡的過程等。那麼程序的概念是什麼?系統如何建立一個程序核心物件來管理每個程序?如何利用程序關聯的核心物件來操作程序?下面的內容可以看作《windows核心程式設計》第四章的學習筆記。

①程序的定義
一般將程序定義成一個正在執行的一個例項。它由兩部分構成:
一個核心物件,作業系統用它來管理程序。核心物件也是系統儲存程序統計資訊的地方。這是不是有點繞?
一個地址空間,其包含所有可執行檔案或DLL模組的程式碼和資料。此外,它還包含動態記憶體分配,比如執行緒堆疊和堆的分配。
程序是有“惰性”的。程序要做任何事情,都必須讓一個執行緒在它的上下文中執行。該執行緒負責執行程序地址空間包含的程式碼。一個程序可以有多個執行緒,它們在地址空間“同時”執行程式碼。為此,每個執行緒都有它自己的一組CPU暫存器和它自己的堆疊。程序至少要有一個主執行緒。使用CreateProcess建立程序的時候,其實也建立了一個主執行緒。沒有了執行緒,程序中的程式碼得不到執行,程序也失去了意義,系統就把程序銷燬了。

②對於標準的win32應用程式還有什麼可說的

想一下,我們編寫一段win32程式的程式碼,這段程式碼被編譯連結為一個可執行檔案,雙擊這個可執行檔案,系統為它分配資源,一個程序就誕生了。這不就是一個程序的誕生嗎?不管程式裡的靜態資原始檔有沒有參與編譯,exe就是我們的程式生成的最終的程式。系統為這個程式配備資源,這個程式才最終得以執行,成為一個程序。我想的是我們通過編寫程式,可以控制程序的哪些東西?哪些是我們沒法控制的?

來看看一個標準的win32程式值得注意的?來看看WinMain入口函式:

int WINAPI WinMain(
  HINSTANCE hInstance,// handle to current instance
  HINSTANCE hPrevInstance,// handle to previous instance
  LPSTR lpCmdLine,// pointer to command line
  int nCmdShow          // show state of window
);

第一個引數hInstance,可執行檔案的例項控制代碼,載入到程序地址空間的每一個可執行檔案或者DLL檔案都被賦予了一個獨一無二的例項控制代碼。在需要載入資源的函式呼叫中,一般要用到此控制代碼,比如LoadIcon(hInstance,pszIcon)就是表示,從可執行檔案的映像中載入圖示資源。hInstance引數的實際值是一個記憶體基地址;系統將可執行檔案的映像載入到程序地址空間中的這個位置。可以使用GetModileHandle函式返回一個控制代碼/基地址。

第二個引數hPrevInstance,在win32程式中總是設為NULL,目的相容16位Windows系統。

第三個引數lpCmdLine指向命令列引數,系統建立新程序的時候,會傳一個命令給它。試了一下,可以這樣獲得命令列引數:

case WM_CREATE:
        {
            LPSTR lpCmdLine=GetCommandLine();
            MessageBox(hwnd,lpCmdLine,"msg",MB_OK);
            return 0;
        }

直接執行,輸出

技術分享圖片

也可以在dos下輸入命令“ProcessTest.exe param1 param2”,執行,得到的結果是:

技術分享圖片

這樣看來跟console程式的argc和argv沒有差別嘛。事實上,我們也可以使用ShellAPI.h檔案中申明並由Shell32.dll匯出的函式CommandLineToArgvW(將任何Unicode字串分解為單獨的標記)來lpCmdLine分解為argc和argv。例如

LPSTR lpCmdLine=GetCommandLine();
            //MessageBox(hwnd,"msg",MB_OK);
            int argc;
            PWSTR *ppArgv=CommandLineToArgvW((LPCWSTR)lpCmdLine,&argc);
            MessageBox(hwnd,(char *)ppArgv[0],"",MB_OK);

③程序的環境變數

每個程序都有一個與它關聯的環境塊(Environment block),這是程序地址空間內分配的一塊記憶體,其中包含字串與下面相似。
=::=::\ ...
VarName1=VarValue1\0
VarName2=VarValue2\0
VarName3=VarValue3\0
VarNameX=VarValueX\0
\0
意思是“環境變數的名稱=變數的值”

這裡又需要理解了,程序的環境變數有什麼用?

之前需要在dos下不帶檔案路徑直接輸命令的時候,經常配置這樣的環境變數。而且通常是手工配置的,比如win10環境變數設定介面是一個這樣的東東

技術分享圖片

環境變數相當於給系統或使用者應用程式設定一些引數,具體其什麼作用視具體的環境變數而定。比如path,是告訴系統,當要求系統執行一個程式而沒有告訴它程式所在的完整路徑時,系統出了在當前目錄下尋找此程式外,還應到哪些目錄下去尋找。在tc或者vc++中,set include=path1;path2;是告訴編譯程式到哪裡去找.h型別的檔案。(就是比如在vc6依次開啟工具-選項-目錄,裡面可以看到目錄和路徑的關係。在裡面設定include=path;set include=是使用dos命令設定。具體可以參考《配置VC++環境變數》)當然不僅僅是指定什麼路徑,還有其他的作用,如set dircmd=/4設定一個環境變數的作用是在使用dir命令時會把/4作為預設的引數新增到dir命令之後,它實際上是給命令解釋程式command設定的一個環境變數,並且是給dir這個內部命令設定的。(百度百科)

我們可以通過GetEnvironmentStrings函式來獲得完整的環境塊。得到的環境塊格式與前面描述的完全一致,因此需要從中提取環境變數和內容。(未完待續...)