Windows允许创建(命名的)事件对象。
Event (Windows中的同步原语)可以是类型为auto-reset的(在这种情况下,你可以说它是一种信号量),也可以是类型为manual-reset的,在这种情况下,它保持设置,直到有人重置它。
现在,从文档CreateEvent, OpenEvent, SetEvent等,似乎没有办法确定,一旦事件已经创建,是否自动复位或手动复位。
我在这种情况下,一个进程创建了一个命名的事件,第二个进程将不得不对这个事件进行操作(它获得了名称,然后将打开事件并最终发出信号)。由于事件应该始终是手动重置事件,因此我希望在第二个过程中添加一个检查,以确保它是手动重置事件。有什么办法检查这个吗?
(是的,它在我的情况下更有好处,因为如果任何代码将创建一个自动重置事件,然后将其传递给此进程,无论如何都会是一个bug。)但是bug是会发生的,如果我能检测到它们就更好了。
没有文档规定的方法来做到这一点,但如果您冒险进入文档规定的领域,实际上并不难。(对于您的目的,这应该是好的,因为它不会真正影响您的程序的功能。)
你需要做的第一件事是弄清楚给你的句柄是否是一个事件。为此使用NtQueryObject。该函数的文档在这里:http://msdn.microsoft.com/en-us/library/bb432383(v=vs.85).aspx。它附带了通常的原生api条款,可能会在没有通知的情况下消失或更改。部分的例子:
#include <winternl.h>
typedef NTSTATUS (NTAPI * PFN_NtQueryObject)(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength );
HMODULE ntdll = GetModuleHandle( L"ntdll.dll" );
auto NtQueryObject = (PFN_NtQueryObject)GetProcAddress( ntdll, "NtQueryObject" );
NTSTATUS result = NtQueryObject(
eventHandle,
ObjectTypeInformation,
buffer,
length,
&length );
这将给你一个PUBLIC_OBJECT_TYPE_INFORMATION结构。如果对象实际上是一个事件,则TypeName字段将为"Event"。
接下来,调用NtQueryEvent来获取事件的类型。所有这些都是完全没有文档记录的。
typedef enum _EVENT_INFORMATION_CLASS {
EventBasicInformation
} EVENT_INFORMATION_CLASS, *PEVENT_INFORMATION_CLASS;
typedef enum _EVENT_TYPE {
NotificationEvent,
SynchronizationEvent
} EVENT_TYPE, *PEVENT_TYPE;
typedef struct _EVENT_BASIC_INFORMATION {
EVENT_TYPE EventType;
LONG EventState;
} EVENT_BASIC_INFORMATION, *PEVENT_BASIC_INFORMATION;
typedef NTSTATUS (NTAPI * PFN_NtQueryEvent)(
HANDLE EventHandle,
EVENT_INFORMATION_CLASS EventInformationClass,
PVOID EventInformation,
ULONG EventInformationLength,
PULONG ReturnLength );
auto NtQueryEvent = (PFN_NtQueryEvent)GetProcAddress( ntdll, "NtQueryEvent" );
EVENT_BASIC_INFORMATION info;
ULONG length = sizeof( info );
NTSTATUS result = NtQueryEvent(
eventHandle,
EventBasicInformation,
&info,
length,
&length );
现在,只需检查info中的EventType字段,就完成了。"NotificationEvent"为手动复位,"SynchronizationEvent"为自动复位。
如果你想知道我是如何想出第二部分的,我没有。信息来自这里:http://undocumented.ntinternals.net/。请负责任地使用!
在初始WaitForSingleObject
返回后立即调用WaitForSingleObject( handle, 0 )
。如果返回值是WAIT_TIMEOUT
,那么你知道这是一个自动复位事件,如果它是WAIT_OBJECT_0
,将返回,这是一个手动复位事件。
这确实依赖于在两个调用之间设置的句柄,因此存在潜在的竞争条件,它不会检测到自动重置事件,但它应该在大多数情况下工作。因为它是一个很好的拥有,希望这就足够了?