關(guān)于在通過 事件對象 在服務(wù)程序和普通桌面應(yīng)用程序相互之間通信的問題,分類情況進(jìn)行討論:
1、普通桌面應(yīng)用程序中創(chuàng)建事件,服務(wù)程序中打開事件
XP的情況
普通桌面應(yīng)用程序中創(chuàng)建:

  1. m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT( "{67BDE5D7-C2FC-49f5-9096-C255AB791B75}" ));

服務(wù)程序中打開并置其為有信號:

  1. HANDLE hEvent = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT( "{67BDE5D7-C2FC-49f5-9096-C255AB791B75}" ));
  2. DWORD dwErr = ::GetLastError();
  3. ::SetEvent(m_hEvent);

vista下情況
vista下有點(diǎn)問題是,如果像上面那樣寫的話,服務(wù)程序在打開該事件對象時(shí)報(bào)錯(cuò)“系統(tǒng)找不到指定的文件。”,原因是XP下服務(wù)程序和應(yīng)用程序創(chuàng)建的內(nèi)核對象的命名空間默認(rèn)是全局的,而vista下則不是,服務(wù)創(chuàng)建的內(nèi)核對象默認(rèn)在session0下,而用戶創(chuàng)建的內(nèi)核對象默認(rèn)在各自的session下(session1,session2……),解決此問題的方法很簡單,就是在創(chuàng)建命名時(shí)間對象時(shí)指定名字是全局的,也就是將CreateEvent和OpenEvent的最后一個(gè)參數(shù)設(shè)置為TEXT("Global\\{67BDE5D7-C2FC-49f5-9096-C255AB791B75}")。

2、服務(wù)程序中創(chuàng)建事件,普通桌面應(yīng)用程序中打開事件
下面就不分系統(tǒng)說明,只說說根本的問題。

服務(wù)程序中創(chuàng)建:

  1. m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, TEXT( "{67BDE5D7-C2FC-49f5-9096-C255AB791B75}" ));

普通桌面應(yīng)用程序中打開:

  1. HANDLE hEvent = ::OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT( "{67BDE5D7-C2FC-49f5-9096-C255AB791B75}" ));
  2. ::SetEvent(hEvent);

上面的代碼不能正常工作,在普通桌面應(yīng)用程序中打開事件對象時(shí),報(bào)錯(cuò)“拒絕訪問。”,并且獲得的事件句柄是NULL,原因是這樣的,在服務(wù)程序中創(chuàng)建的內(nèi)核對象,默認(rèn)情況下桌面程序無法打開這個(gè)對象,每個(gè)內(nèi)核對象都是有訪問控制的,而服務(wù)中創(chuàng)建的內(nèi)核對象權(quán)限比較高,當(dāng)LPSECURITY_ATTRIBUTES這個(gè)參數(shù)傳NULL的時(shí)候,將使用默認(rèn)訪問控制。普通桌面應(yīng)用程序自然沒有權(quán)限訪問了,解決方法如下,在服務(wù)程序創(chuàng)建事件對象時(shí),指定確定的安全描述符。

  1. // set SECURITY_DESCRIPTOR
  2. SECURITY_DESCRIPTOR secutityDese;
  3. ::InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION);
  4. ::SetSecurityDescriptorDacl(&secutityDese,TRUE,NULL,FALSE);
  5. SECURITY_ATTRIBUTES securityAttr;
  6. // set SECURITY_ATTRIBUTES
  7. securityAttr.nLength = sizeof SECURITY_ATTRIBUTES;
  8. securityAttr.bInheritHandle = FALSE;
  9. securityAttr.lpSecurityDescriptor = &secutityDese;
  10. m_hEvent = ::CreateEvent(&securityAttr, FALSE, FALSE, TEXT( "{67BDE5D7-C2FC-49f5-9096-C255AB791B75}" ));

這樣普通桌面應(yīng)用程序再去打開該事件對象就沒有問題了。(注:vista下事件對象的名字仍然要指定全局空間)

另外再談?wù)刉indows編程中的session,最近遇到一些很郁悶的問題。一直在折騰Vista下的服務(wù)程序啟動(dòng)進(jìn)程的問題,有了點(diǎn)小小的體會(huì),記下來,希望能幫到跟我遇到一樣問題的朋友。Windows xp、Vista中,服務(wù)程序都是運(yùn)行在session0中,而后面的第1、2、...、N個(gè)用戶則分別運(yùn)行在session1、session2、...、sessionN中。不同的session有不同的namespace,但是由于目前主流的用戶windows平臺WinXP支持快速用戶切換,所以我們感覺不到這些差異。

在XP中,用Sevice啟動(dòng)的進(jìn)程跟我們用當(dāng)前用戶啟動(dòng)的進(jìn)程在編程上似乎沒什么區(qū)別,用起來都一樣。 可是到了vista下,情況就不一樣了。vista新的安全機(jī)制對不同的session之間的限制做了加強(qiáng)。一些命名內(nèi)核對象,如Event的使用,為了進(jìn)行進(jìn)程通信,在進(jìn)程1(處在session1中)中,我創(chuàng)建了一個(gè)命名的事件對象,然后在進(jìn)程2(由我的服務(wù)啟動(dòng),所以運(yùn)行在session0中)中檢測該Event,發(fā)現(xiàn)始終檢查不到,而且錯(cuò)誤信息是“系統(tǒng)找不到指定的文件。”另外專門寫了個(gè)小程序去檢測(直接運(yùn)行,也是運(yùn)行在session1中),卻能檢測到。

后來仔細(xì)讀了MSDN中關(guān)于“ Kernel Object Name Spaces”的資料,才明白:一些命名的內(nèi)核對象,比如: events, semaphores, mutexes, waitable timers, file-mapping objects, job objects,都只是在自己的namespace里唯一存在,不同的session因?yàn)閚amespace不同,所以會(huì)導(dǎo)致上面的現(xiàn)象。詳細(xì)的信息可以參考MSDN中的CreateEvent資料中對參數(shù)lpName的說明。

按照上面的方法應(yīng)該可以解決通信問題,下面是我寫的代碼,可以實(shí)現(xiàn)通信

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;

//參數(shù)為true 的時(shí)候,才可以和應(yīng)用程序通信,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);