2

早上好。

这是我的场景:我有一个与自动快速回报设备交互的第 3 方非托管 foo.dll,称为 FooDevice。我围绕 foo.dll 的方法编写了一个包装器,称之为 FooWrapper,通过编组和一些锤击,我终于使它工作了;您可能知道,在使用DllImport所有公开的方法时都需要标记为static,而extern foo.dll 公开了一些方法和一个回调函数指针;当我尝试在不同的线程中同时连接两个设备时,我的包装器在尝试挂钩此回调函数时挂起。我知道静态的东西是线程共享的,所以我考虑AppDomain为每个 FooWrapper 实例使用不同的东西。你认为这是做这种工作的正确方法吗?

这是我的 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 中)吗?

感谢你们。干杯,南多

4

1 回答 1

3

我完全理解你的问题。这些是我会尝试的选项,选择适合您特定情况的选项

  1. 我会尝试联系 Foo.dll 的供应商并获得一个线程安全的版本。

  2. 如果在 DLL 上调用方法不会影响性能(它们花费的时间很少),我会通过锁定、登录、设置状态、执行操作和每次调用注销来使包装器线程安全。这是一个干净的解决方案,以后可以用线程安全的 foo.dll 甚至新的基于 C# 的实现来替换。它也易于测试和维护。

  3. 第三种,混乱但简单的选择 - 是将 P/Invoke 类包装器包装到一个可执行文件中,并为每个线程启动一个进程,并使用远程处理与类包装器的实际实例对话。您可以使用 ThreadId 来确定为哪个线程启动了哪个进程并以这种方式单独调用。

希望这些选项之一有所帮助!

于 2012-02-13T19:43:53.047 回答