关于在通过 事件对象 在服务程序和普通桌面应用程序相互之间通信的问题,分类情况进行讨论:
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的说明。
大家可以参考MSDN中的“ Kernel Object Name Spaces”(没事贴点MSDN中的东东,MSDN才是王道啊):
Kernel Object Name Spaces
A Terminal Services server has multiple namespaces for the following named kernel objects: events, semaphores, mutexes, waitable timers, file-mapping objects, and job objects. There is a global namespace used primarily by services such as client/server applications. In addition, each client session has a separate namespace for these objects. Note that this differs from the standard Windows environment in which all processes and services share a single namespace for these objects.
The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the "Global/" prefix to the object name. For example, the following code calls CreateEvent and creates an event object named CSAPP in the global namespace:
CreateEvent( NULL, FALSE, FALSE, "Global//CSAPP" );
Service applications in a Terminal Services environment use the global namespace by default. Processes started under session zero (typically the console session) also use the global namespace by default. The global namespace enables processes on multiple client sessions to communicate with a service application or with the console session. For example, a client/server application might use a mutex object for synchronization. The server component running as a service can create the mutex object in the global namespace. Then the client component running as a process running under a client session can use the "Global/" prefix to open the mutex object.
Another use of the global namespace is for applications that use named objects to detect that there is already an instance of the application running in the system across all sessions. This named object must be created or opened in the global namespace instead of the per-session namespace. Note that the more common case of running the application once per session is supported by default since the named object is created in a per session namespace.
Client processes can also use the "Local/" prefix to explicitly create an object in their session namespace.
The "Local", "Global" and "Session" prefixes are reserved for system use and should not be used as names for kernel objects. These keywords are case sensitive. On Windows 2000 without Terminal Services, these keywords are ignored. On Windows NT 4.0 without Terminal Services, and earlier versions of Windows NT, the functions for creating or opening these objects fail if you specify a name containing the backslash character (/).
Windows XP: Fast user switching is implemented using Terminal Services sessions. The first user to log on uses session zero, the next user to log on uses session one, and so on. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users. For more information, see Fast User Switching.
http://blog.csdn.net/magictong/article/details/3726827