1. 程式人生 > >Windows程序間共享核心物件幾種方式

Windows程序間共享核心物件幾種方式

核心物件的控制代碼是程序相關的,這樣更可靠,更安全;Windows提供下列N種機制來允許程序共享核心物件。

使用物件控制代碼繼承

只有程序間存在父子關係才能使用物件控制代碼繼承;系統會遍歷父程序的控制代碼表將所有有效的控制代碼完整的複製到子程序的控制代碼表中(這個動作只會在建立子程序的進行一次,後續父程序再建立控制代碼物件,子程序不會再繼承),並且增加控制代碼的使用計數(類似於智慧指標,Close等操作只是將使用計數減一)。

CreateProcess建立程序,指定引數bInheritHandles為TRUE指定為父子程序,進行物件控制代碼繼承,控制代碼跟程序有關,只有繼承後子程序才能使用父程序控制代碼;

使用程序間通訊技術傳遞

  1. 例如:通過命令列引數將控制代碼值傳遞給子程序)
  2. 通過設定父程序環境變數,子程序獲取GetEnvironmentVariable;
  3. 如果子程序是有視窗的程式 通過函式WaitForInputIdle等待視窗程式初始化完成後 向視窗發訊息;

改變控制代碼狀態(控制哪些子程序可以繼承核心物件控制代碼)

父程序跟孫程序通訊,不讓子程序關閉控制代碼。通過SetHandleInfomation改變具柄狀態,但是子程序也可以改這個狀態從而關閉控制代碼(這就很尷尬了)。

BOOL WINAPI SetHandleInformation(
  _In_ HANDLE hObject,//要設定其資訊的物件的控制代碼。
  _In_ DWORD dwMask,
  _In_ DWORD dwFlags
);

//每個控制代碼都關聯了兩個標誌
#define HANDLE_FLAG_INHERIT  0x00000001
#define HANDLE_FLAG_PROTECT_FROM_CLOSE  0x00000002

//開啟核心物件控制代碼的繼承標誌
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
//關閉核心物件控制代碼的繼承標誌
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, 0) ;
//HANDLE_FLAG_PROTECT_FROM_CLOSE標誌告訴系統不允許關閉控制代碼
SetHandleInformation(hObj, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
CloseHandle(h0bj); //會引發異常

命名物件

通過物件的名稱來共享核心物件,並不需要父子程序關係; windows大多數物件都可以進行命名,但是並沒有提供保證為核心物件指定的名稱是唯一的,所以命名可以以“公司名稱+隨機字元+名稱”等方式來命名。以這樣的私有命名方式,防止單例項程式被攻擊(命名衝突,導致一直不能啟動程式)。

呼叫( Create )系列函式可以通過呼叫GetLastError來判斷是打開了一個已用的物件還是建立了一個新的物件。(Open)系列函式如果不存在否則返回NULL。

應用例子:可以利用命名物件來防止程式執行多個例項。

HANDLE CreateMutex( 
  LPSECURITY_ATTRIBUTES lpMutexAttributes, 
  BOOL bInitialOwner, 
  LPCTSTR lpName //物件名稱 傳入NULL建立匿名物件
);

	HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, “Company_0DntbbRl5qP3_AppName”);
	if ( hMutex )
	{
	    // 應用程式已存在例項
	}
	else
	{
	    // 不存在,建立命名物件
		hMutex = ::CreateMutex(0, FALSE, “Company_0DntbbRl5qP3_AppName”);
	}

複製物件控制代碼

獲得一個程序的控制代碼表的一個記錄項,然後在另外一個程序的控制代碼表中建立這個記錄項的一個副本。同樣的目標程序並不知道它能訪問什麼核心物件,必須使用程序間通訊方式告訴它控制代碼值;這時已經啟動,所以不能使用命令列引數或程序的環境變數。

BOOL WINAPI DuplicateHandle(
  _In_  HANDLE   hSourceProcessHandle,     // 源程序核心物件
  _In_  HANDLE   hSourceHandle,     // 源任何型別核心物件;不能與呼叫DuplicateHandle函式的程序相關,必須與hSourceProcessHandle控制代碼所標識的程序相關(除非hSourceProcessHandle就是呼叫DuplicateHandle函式的程序,即當前程序)
  _In_  HANDLE   hTargetProcessHandle,     // 目標程序核心物件
  _Out_ LPHANDLE lpTargetHandle,     // HANDLE變數的地址,用來接收復製得到的HANDLE值;不能呼叫CloseHandle(除非hTargetProcessHandle就是呼叫DuplicateHandle函式的程序,即當前程序)
  _In_  DWORD    dwDesiredAccess,
  _In_  BOOL     bInheritHandle,
  _In_  DWORD    dwOptions
);

BTW:子程序與父程序通訊不應該使用程序ID,應該使用核心物件或者視窗控制代碼等更持久的通訊機制。因為系統分配程序ID是會回收重新使用的。