静态非托管 dll C# 包装器和多线程、多域
早上好。
这是我的场景:我有一个第三方非托管 foo.dll,它与自动快速回报设备交互,称之为 FooDevice。我围绕 foo.dll 的方法编写了一个包装器,将其称为 FooWrapper,通过编组和一些锤炼,我终于使它工作了;如您所知,使用 DllImport
时,所有公开的方法都需要标记为 static
和 extern
foo.dll公开了一些方法和回调函数指针;当我尝试在不同线程中同时连接两个设备时,我的包装器在尝试挂钩此回调函数时挂起。 我知道静态的东西是线程共享的,所以我考虑为每个 FooWrapper 实例使用不同的 AppDomain 。 您认为这是开展此类工作的正确方法吗?
这是我的 FooWrapper 的一些内容:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FOO_EventHandlerFunc([In] UInt16 event_id, [In, Out] ref IntPtr data, [In] IntPtr param);
[SuppressUnmanagedCodeSecurity]
internal static class FOO
{
static FOO()
{
//...
}
///
/// FOO_RegisterEventHandler
/// The FOO_RegisterEventHandler function registers an application-defined callback
/// function, which will subsequently be called for all FooDevice generated events.
///
/// long FOO_RegisterEventHandler(FOO_EventHandlerFunc handler, BYTE evmask, LONG param);
///
/// Parameters
/// handler
/// [in] Pointer to an application-defined callback function (see below).
/// evmask
/// [in] Specify which events to enable (see EnableEvents).
/// param
/// [in] An application-defined value to be passed to the callback function
///
/// Return Values
/// If the function succeeds, the return value is zero.
/// If the function fails, the return value is nonzero.
///
/// Remarks
/// The FOO_EventHandlerFunc type defines a pointer to a callback function, which must
/// comply with the following, where FOO_EventHandlerFunc is a placeholder for the
/// application-defined function name.
///
/// void FOO_EventHandlerFunc(WORD event_id, LPVOID data, LONG param);
///
/// Parameters
/// event_id
/// [in] Event index as specified by the FooDevice protocol.
/// data
/// [in] Event data. The type of data depends on event_id.
/// (See the event specifications for FooDevice).
/// param
/// The application-defined value passed during registration.
///
/// Remarks
/// Avoid lengthy callback functions, since it will stall the underlying protocol,
/// thereby interrupting a steady communications flow.
/// FooDevice will only be generating events during operation.
/// That is - between FOO_LogIn and FOO_LogOut.
///
///The handler.
///The evmask.
///The param.
///
[DllImport("foo.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
public static extern UInt32 FOO_RegisterEventHandler([In] [MarshalAs(UnmanagedType.FunctionPtr)] FOO_EventHandlerFunc handler, [In] byte evmask, [In] IntPtr param);
///
/// FOO_LogIn
/// The FOO_LogIn function opens FooDevice for normal operation.
///
/// long FOO_LogIn(LPSTR oper, LPSTR datetime);
///
/// Parameters
/// oper
/// [in] Pointer to a null-terminated string identifying the cashier.
/// The string can have any content, but a maximum of 50 characters will be used.
/// datetime
/// [in] Pointer to a null-terminated string indicating the current date and time.
/// The string must have 'YYYYMMDDhhmmss' format to take effect.
/// Return Values
/// If the function succeeds, the return value is zero.
/// If the function fails, the return value is nonzero.
///
///The oper.
///The datetime.
///
[DllImport("foo.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
public static extern UInt32 FOO_LogIn([In] string oper, [In] string datetime);
//... and so on ...
}
}
您能给我建议一种方法来正确实例化 FooWrapper 多次(在相同或不同的线程或 AppDomain 中)吗?
谢谢你们。 干杯, 南多
Good morning.
This is my scenario: I've got a 3rd party unmanaged foo.dll that interacts with an automatic fast payback device, call it FooDevice. I wrote a wrapper around methods of foo.dll, call it FooWrapper, and with marshaling and a bit of hammering I finally make it work; as you may know, when using DllImport
all methods exposed need to be marked as static
and extern
foo.dll exposes some methods and a callback function pointer; when I try to connect two devices at same time in diffrent threads, my wrapper hangs when tryng to hook this callback function.
I know that static stuff is thread shared, so I thinked about using different AppDomain
for each FooWrapper istances.
Do you think is that the right way to do this kind of work?
Here a bit of my FooWrapper:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FOO_EventHandlerFunc([In] UInt16 event_id, [In, Out] ref IntPtr data, [In] IntPtr param);
[SuppressUnmanagedCodeSecurity]
internal static class FOO
{
static FOO()
{
//...
}
///
/// FOO_RegisterEventHandler
/// The FOO_RegisterEventHandler function registers an application-defined callback
/// function, which will subsequently be called for all FooDevice generated events.
///
/// long FOO_RegisterEventHandler(FOO_EventHandlerFunc handler, BYTE evmask, LONG param);
///
/// Parameters
/// handler
/// [in] Pointer to an application-defined callback function (see below).
/// evmask
/// [in] Specify which events to enable (see EnableEvents).
/// param
/// [in] An application-defined value to be passed to the callback function
///
/// Return Values
/// If the function succeeds, the return value is zero.
/// If the function fails, the return value is nonzero.
///
/// Remarks
/// The FOO_EventHandlerFunc type defines a pointer to a callback function, which must
/// comply with the following, where FOO_EventHandlerFunc is a placeholder for the
/// application-defined function name.
///
/// void FOO_EventHandlerFunc(WORD event_id, LPVOID data, LONG param);
///
/// Parameters
/// event_id
/// [in] Event index as specified by the FooDevice protocol.
/// data
/// [in] Event data. The type of data depends on event_id.
/// (See the event specifications for FooDevice).
/// param
/// The application-defined value passed during registration.
///
/// Remarks
/// Avoid lengthy callback functions, since it will stall the underlying protocol,
/// thereby interrupting a steady communications flow.
/// FooDevice will only be generating events during operation.
/// That is - between FOO_LogIn and FOO_LogOut.
///
///The handler.
///The evmask.
///The param.
///
[DllImport("foo.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
public static extern UInt32 FOO_RegisterEventHandler([In] [MarshalAs(UnmanagedType.FunctionPtr)] FOO_EventHandlerFunc handler, [In] byte evmask, [In] IntPtr param);
///
/// FOO_LogIn
/// The FOO_LogIn function opens FooDevice for normal operation.
///
/// long FOO_LogIn(LPSTR oper, LPSTR datetime);
///
/// Parameters
/// oper
/// [in] Pointer to a null-terminated string identifying the cashier.
/// The string can have any content, but a maximum of 50 characters will be used.
/// datetime
/// [in] Pointer to a null-terminated string indicating the current date and time.
/// The string must have 'YYYYMMDDhhmmss' format to take effect.
/// Return Values
/// If the function succeeds, the return value is zero.
/// If the function fails, the return value is nonzero.
///
///The oper.
///The datetime.
///
[DllImport("foo.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
public static extern UInt32 FOO_LogIn([In] string oper, [In] string datetime);
//... and so on ...
}
}
Can you suggest me a way to istantiate correctly FooWrapper for more than one time (in same or differente thread or AppDomain)?
Thank you guys.
Cheers,
Nando
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我完全理解你的问题。这些是我会尝试的选项,选择适合您特定情况的一个
我会尝试联系 Foo.dll 的供应商并获取线程安全的版本。
如果调用 DLL 上的方法不影响性能(它们花费很少的时间),我将通过锁定、登录、设置状态、执行操作并在每次调用时注销来使包装器线程安全。这是一个干净的解决方案,稍后可以用线程安全的 foo.dll 甚至新的基于 C# 的实现来替换。它也很容易测试和维护。
第三个虽然混乱但简单的选项 - 是将 P/Invoke 类包装器包装到可执行文件中,并为每个线程启动一个进程,并使用远程处理与类包装器的实际实例进行通信。您可以使用 ThreadId 来确定为哪个线程启动了哪个进程,并以这种方式单独调用。
希望这些选项之一有帮助!
I understand your problem completely. These are the options I would try, choose one that suits your particular situation
I would try to contact the vendor of Foo.dll and get a version that is thread safe.
If calling methods on the DLL does not affect performance (they take very little time), I would make the wrapper thread-safe by locking, logging in, setting up state, performing the operation and logging out on each call. This is a clean solution that can be replaced later with a thread-safe foo.dll or even new C# based implementation. It is also easy to test and maintain.
The third, messy but easy option - is to wrap the P/Invoke class wrapper into an executable and launch one process per thread and use remoting to talk to the actual instance of the class wrapper. You could use ThreadId to determine which process was launched for which thread and separate calls that way.
Hope one of these options help!