如何从Win32中检测在.NET中创建的事件对象

时间:2010-12-12 15:45:42

标签: .net winapi events

我正在使用这样的互操作调用在.NET中创建一个命名的事件对象:

[DllImport("kernel32.dll")]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset,
    bool bInitialState, [MarshalAs(UnmanagedType.LPWStr)] string lpName);

const string EVENT_NAME = "Global\\unique_id_string";
const uint SYNCHRONIZE = 0x00100000;
const uint EVENT_MODIFY_STATE = 0x0002;
hEvent = CreateEvent(IntPtr.Zero, true, false, EVENT_NAME);

然后我尝试从像这样的Win32程序中打开此事件

WCHAR evntName[MAX_PATH] = {0};
wcscpy(evntName, L"Global\\unique_id_string");
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, evntName);

但返回的句柄始终为0.

当我从这样的另一个.NET应用程序中尝试相同的时候,

[DllImport("kernel32.dll")]
static extern IntPtr OpenEvent(UInt32 dwDesiredAccess, bool bInheritable,
    [MarshalAs(UnmanagedType.LPWStr)] string lpName);

const string EVENT_NAME = "Global\\unique_id_string";
const uint SYNCHRONIZE = 0x00100000;
const uint EVENT_MODIFY_STATE = 0x0002;

IntPtr hEvent = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, false, EVENT_NAME);

它完美无缺,并返回事件的正确句柄。

为什么它不能使用本机C ++应用程序?有什么我想念的吗?

2 个答案:

答案 0 :(得分:1)

Win32 API调用有两个版本 - ANSI和Unicode。根据{{​​3}}的文档,您必须将其指定为CharSet属性,否则默认为ANSI版本。即使您将字符串封送为LPWStr,您实际上也在调用ANSI版本,并且它很可能只看到名称G的第一个字符。但是您的Win32应用程序正在使用完整的Unicode名称(如您所愿)但未能找到这样的命名事件。

尝试显式导入函数的Unicode版本:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]

如果您指定CharSet,则您也无需自行指定编组。

答案 1 :(得分:0)

虽然ANSI / Unicode可能是问题,但ANSI API的工作方式是将字符串映射到Unicode,然后调用Unicode代码路径。所以这应该不是问题。

如果找不到文件错误,则名称不匹配。尝试更改本机应用程序以使用CreateEvent(如果名称存在,则获得句柄,GetLastError()返回ERROR_ALREADY_EXISTS - 因此CreateEvent的模式并始终检查上一个错误以避免计时对象创建的依赖关系或竞争条件。

如果没有名称冲突,请使用handleProcess Explorer(两个SysInternals)查看事件对象,看看它的名称是什么。


更新

发生了什么(TL:DR:版本):

告诉P / Invoke它是ANSI API(默认情况下,如果你没有指定Unicode)并且说字符串是Unicode会产生错误的结果。修复其中任何一个都可以解决问题。

富勒版:

最初的P / Inovoke声明:

[DllImport("kernel32.dll")]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset,
        bool bInitialState, [MarshalAs(UnmanagedType.LPWStr)] string lpName);

具有默认(ANSI)API方法(因为DllImport的{​​{1}}属性默认为CharSet 但是 string CharSet.Ansi:传递 wide (即Unicode)字符串。

如果在运行时查看对象的名称,请看:

\Sessions\1\BaseNamedObjects\G

API刚刚从名称中看到了UnmanagedType.LPWStr,并添加了每会话名称前缀。

将声明修改为:     [的DllImport( “KERNEL32.DLL”)]     static extern IntPtr CreateEvent(IntPtr lpEventAttributes,bool bManualReset,             bool bInitialState,[MarshalAs(UnmanagedType.LPStr)] string lpName);

将ANSI字符串传递给ANSI API,或

G

将Unicode字符串传递给Unicode API都可以正常工作,并创建了一个对象名称:

\BaseNamedObjects\unique_id_string

[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [MarshalAs(UnmanagedType.LPWStr)] string lpName); Global的内核对象树中的别名。)

要点:

P / Invoke声明需要100%正确...即使一个字符也很重要。