开发者

How to detect event object created in .NET from Win32

I am creating a named event object in .NET using interop calls like this:

[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);

And then I try to open this event from a Win32 program like this

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

But the handle returned is always 0.

When I try the same from another .NET application like this,

[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);

it works perfectly and returns the correct handle to the event.

Why is it not working with the native C++ application? Is there some开发者_开发百科thing I am missing?


Win32 API calls come in two versions -- ANSI and Unicode. According to the docs for DllImport, you must specify this as a CharSet attribute, otherwise it defaults to the ANSI version. Even though you are marshaling the string as LPWStr, you are actually invoking the ANSI version and it most likely sees only the first character of the name G. But your Win32 application is using the full Unicode name (as you intended) but fails to find such a named event.

Try explicitly importing the Unicode version of the function:

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

If you specify the CharSet, you also don't need to specify the marshaling yourself.


While ANSI/Unicode might be the problem, the ANSI APIs work by mapping strings to Unicode and then calling the Unicode code path. So that shouldn't be a problem.

With an error of File Not Found there is a name mismatch. Try changing the native app to use CreateEvent as well (if the name exists you get a handle, and GetLastError() returns ERROR_ALREADY_EXISTS—thus the pattern of CreateEvent and always checking last error to avoid timing dependencies or race conditions around object creation.

If you don't get a name collision, use handle or Process Explorer (both SysInternals) to look at the event object to see what its name really is.


UPDATE

What happened (TL:DR: version):

Telling P/Invoke that it is an ANSI API (the default, if you don't specif Unicode) and saying the string is Unicode gives wrong results. Fixing either of these would resolve the problem.

Fuller version:

The original P/Inovoke declaration:

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

has the default (ANSI) API method (because DllImport's CharSet property defaults to CharSet.Ansi) but the string has UnmanagedType.LPWStr: pass a wide (i.e. Unicode) string.

If you look at the name of the object when this is running you see:

\Sessions\1\BaseNamedObjects\G

where the API has just seen the G from the name, and add the per-session name prefix.

Modifying the declaration to: [DllImport("kernel32.dll")] static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [MarshalAs(UnmanagedType.LPStr)] string lpName);

passing an ANSI string to an ANSI API, or

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

passing a Unicode string to a Unicode API both work, with an object name created:

\BaseNamedObjects\unique_id_string

(Global is an alias in the Kernel object tree for BaseNamedObjects.)

Summary:

P/Invoke declarations need to be 100% correct... even one character is significant.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜