關于在通過 事件對象 在服務程序和普通桌面應用程序相互之間通信的問題,分類情況進行討論:
1、普通桌面應用程序中創建事件,服務程序中打開事件
XP的情況
普通桌面應用程序中創建:
-
m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT(
"{67BDE5D7-C2FC-49f5-9096-C255AB791B75}"
));
服務程序中打開并置其為有信號:
-
HANDLE
hEvent = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT(
"{67BDE5D7-C2FC-49f5-9096-C255AB791B75}"
));
-
DWORD
dwErr = ::GetLastError();
-
::SetEvent(m_hEvent);
vista下情況
vista下有點問題是,如果像上面那樣寫的話,服務程序在打開該事件對象時報錯“系統找不到指定的文件。”,原因是XP下服務程序和應用程序創建的內核對象的命名空間默認是全局的,而vista下則不是,服務創建的內核對象默認在session0下,而用戶創建的內核對象默認在各自的session下(session1,session2……),解決此問題的方法很簡單,就是在創建命名時間對象時指定名字是全局的,也就是將CreateEvent和OpenEvent的最后一個參數設置為TEXT("Global\\{67BDE5D7-C2FC-49f5-9096-C255AB791B75}")。
2、服務程序中創建事件,普通桌面應用程序中打開事件
下面就不分系統說明,只說說根本的問題。
服務程序中創建:
-
m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT(
"{67BDE5D7-C2FC-49f5-9096-C255AB791B75}"
));
普通桌面應用程序中打開:
-
HANDLE
hEvent = ::OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT(
"{67BDE5D7-C2FC-49f5-9096-C255AB791B75}"
));
-
::SetEvent(hEvent);
上面的代碼不能正常工作,在普通桌面應用程序中打開事件對象時,報錯“拒絕訪問。”,并且獲得的事件句柄是NULL,原因是這樣的,在服務程序中創建的內核對象,默認情況下桌面程序無法打開這個對象,每個內核對象都是有訪問控制的,而服務中創建的內核對象權限比較高,當LPSECURITY_ATTRIBUTES這個參數傳NULL的時候,將使用默認訪問控制。普通桌面應用程序自然沒有權限訪問了,解決方法如下,在服務程序創建事件對象時,指定確定的安全描述符。
-
-
SECURITY_DESCRIPTOR secutityDese;
-
::InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION);
-
::SetSecurityDescriptorDacl(&secutityDese,TRUE,NULL,FALSE);
-
-
SECURITY_ATTRIBUTES securityAttr;
-
-
-
securityAttr.nLength =
sizeof
SECURITY_ATTRIBUTES;
-
securityAttr.bInheritHandle = FALSE;
-
securityAttr.lpSecurityDescriptor = &secutityDese;
-
-
m_hEvent = ::CreateEvent(&securityAttr, FALSE, FALSE, TEXT(
"{67BDE5D7-C2FC-49f5-9096-C255AB791B75}"
));
-
這樣普通桌面應用程序再去打開該事件對象就沒有問題了。(注:vista下事件對象的名字仍然要指定全局空間)
另外再談談Windows編程中的session,最近遇到一些很郁悶的問題。一直在折騰Vista下的服務程序啟動進程的問題,有了點小小的體會,記下來,希望能幫到跟我遇到一樣問題的朋友。Windows xp、Vista中,服務程序都是運行在session0中,而后面的第1、2、...、N個用戶則分別運行在session1、session2、...、sessionN中。不同的session有不同的namespace,但是由于目前主流的用戶windows平臺WinXP支持快速用戶切換,所以我們感覺不到這些差異。
在XP中,用Sevice啟動的進程跟我們用當前用戶啟動的進程在編程上似乎沒什么區別,用起來都一樣。 可是到了vista下,情況就不一樣了。vista新的安全機制對不同的session之間的限制做了加強。一些命名內核對象,如Event的使用,為了進行進程通信,在進程1(處在session1中)中,我創建了一個命名的事件對象,然后在進程2(由我的服務啟動,所以運行在session0中)中檢測該Event,發現始終檢查不到,而且錯誤信息是“系統找不到指定的文件。”另外專門寫了個小程序去檢測(直接運行,也是運行在session1中),卻能檢測到。
后來仔細讀了MSDN中關于“ Kernel Object Name Spaces”的資料,才明白:一些命名的內核對象,比如: events, semaphores, mutexes, waitable timers, file-mapping objects, job objects,都只是在自己的namespace里唯一存在,不同的session因為namespace不同,所以會導致上面的現象。詳細的信息可以參考MSDN中的CreateEvent資料中對參數lpName的說明。
按照上面的方法應該可以解決通信問題,下面是我寫的代碼,可以實現通信
HANDLE hThread;
unsigned dwThreadID;
SECURITY_DESCRIPTOR secutityDese;
::InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION);
::SetSecurityDescriptorDacl(&secutityDese,TRUE,NULL,FALSE);
SECURITY_ATTRIBUTES securityAttr;
// set SECURITY_ATTRIBUTES
securityAttr.nLength = sizeof SECURITY_ATTRIBUTES;
//參數為true 的時候,才可以和應用程序通信,false 則不行
securityAttr.bInheritHandle = TRUE;??????????????
securityAttr.lpSecurityDescriptor = &secutityDese;
g_hMutex = CreateMutex(&securityAttr,TRUE,TEXT("Global\\CSAPP"));
hThread = (HANDLE)_beginthreadex( NULL, 0, &Read, NULL, 0, &dwThreadID);
while(m_bIsRunning)
{????
??? WaitForSingleObject(g_Semaphore,INFINITE);
??? ReleaseMutex(g_hMutex);??
}
CloseHandle(hThread);