如何从Win32检测在.NET中创建的事件对象
我正在.NET中使用以下互操作调用创建命名事件对象:如何从Win32检测在.NET中创建的事件对象,.net,winapi,events,.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_
[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++应用程序?我缺少什么吗?Win32 API调用有两个版本——ANSI和Unicode。根据的文档,必须将其指定为
CharSet
属性,否则默认为ANSI版本。即使您将字符串封送为LPWStr
,但实际上您正在调用ANSI版本,它很可能只看到名称G
的第一个字符。但是您的Win32应用程序正在使用完整的Unicode名称(如您所希望的),但找不到这样一个命名事件
尝试显式导入函数的Unicode版本:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
如果指定
字符集
,也不需要自己指定封送。虽然ANSI/Unicode可能是问题所在,但ANSI API通过将字符串映射到Unicode,然后调用Unicode代码路径来工作。所以这不应该是个问题
“未找到文件”错误导致名称不匹配。尝试将本机应用程序也更改为使用(如果名称存在,您将获得一个句柄,GetLastError()
返回已经存在的错误-CreateEvent
的模式,并始终检查最后一个错误,以避免对象创建的时间依赖性或争用条件
如果没有名称冲突,请使用或(两个系统内部)查看事件对象,以了解其真正名称
更新 发生了什么(TL:DR:version): 告诉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
的CharSet
属性默认为CharSet.ANSI
)但该字符串具有UnmanagedType.LPWStr
:传递宽(即Unicode)字符串
如果在运行时查看对象的名称,您会看到:
\Sessions\1\BaseNamedObjects\G
将Unicode字符串传递给Unicode API都可以工作,并创建一个对象名:
\BaseNamedObjects\unique_id_string
\BaseNamedObjects\unique\u id\u字符串
(Global
是BaseNamedObjects
的内核对象树中的别名)
总结:
p/Invoke声明必须100%正确…即使只有一个字符是有效的。在本机应用程序中,
GetLastError()
返回什么?(0,或者更确切地说是NULL,表示失败,但您需要找出失败的原因。)另外,您是否让创建事件的应用程序继续运行?如果没有对内核对象的引用,它将被销毁。很抱歉,我忘了提到这一点。GetLastError()返回2,这是错误文件未找到的系统错误代码。这让我完全困惑。是的,事件创建应用程序正在后台运行。它在系统托盘中运行。未找到文件对于命名对象意味着“没有具有该名称的对象”。即使它们可能在内部映射到Unicode,但输入字符串被视为ANSI字符串,它最终只会看到Unicode字符串的第一个字符。不知何故,它只有在显式指定Unicode时才起作用。此解决方案与Charset方法相结合,对我来说效果非常好。@casablanca,我不理解:从正确的ANSI代码页到Unicode执行正确的转码。因此,ANSI文本输出函数会写入多个字符。我怀疑更多的情况是P/Invoke和编组混淆。由于Unicode字符串编码为UTF-16,L“hello”
在内存中看起来像h\0e\0l\0l\0o\0\0\0
。ANSI“hello”
就是hello\0
。ANSI函数将在第一个空字符处停止,因此Unicode字符串看起来像“h”
。您可以通过将Unicode字符串传递给ANSI函数来测试这一点,例如MessageBoxA(NULL,(LPCSTR)L“hello”,NULL,0)
将只显示h
@casablanca:我正在更新这里的根本原因(P/invoke不一致)。调用ANSI版本的CreateEvent
也可以。
\BaseNamedObjects\unique_id_string