关于在通过 事件对象 在服务程序和普通桌面应用程序相互之间通信的问题,分类情况进行讨论:
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的时候,将使用默认访问控制。普通桌面应用程序自然没有权限访问了,解决方法如下,在服务程序创建事件对象时,指定确定的安全描述符。
- // set SECURITY_DESCRIPTOR
- SECURITY_DESCRIPTOR secutityDese;
- ::InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION);
- ::SetSecurityDescriptorDacl(&secutityDese,TRUE,NULL,FALSE);
- SECURITY_ATTRIBUTES securityAttr;
- // set SECURITY_ATTRIBUTES
- 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);