c# - 桥接非托管 C dll 和托管 C# dll

标签 c# c dll interop wrapper

我有一个第三方 Windows 应用程序,支持 C 插件/驱动程序(请参阅下面的规范)在初始化后成为被动数据接收器。现在我找到了一个 C# 包来进行数据收集和推送。这里唯一缺少的部分是 C dll 和 C# dll 之间的桥梁。

数据流是这样的:当应用程序启动时,它加载并调用 C dll,其中包含几个导出函数,包括 init 函数。在此 init 函数中,我喜欢建立对 C# 的调用来设置一些网络连接并准备传入数据。一旦完成,根据驱动程序规范,C# dll 将收集数据并将其流式传输到接收驱动程序。为了适应这一点,我有两个想法(你可能会想出更多):

1) 用 C++/Cli 包装 C# 并从驱动程序调用公开的类似 C# 的方法。使用 gcroot 声明一个对象,然后使用 gcnew 实例化它,然后调用方法。尝试了一下,我得到了 stackoverflowException 。我是这种混合模式编程的新手,不明白为什么会发生这种情况。

2) 以某种方式包装 C dll(例如使用 C++/Cli 导入 C 函数,然后与 C# 数据流交互)以便从 C# 调用。做这个的最好方式是什么?

我已经阅读了一些内容,似乎 C++/Cli 是一条简单的路线,但我也对其他不那么复杂的选项持开放态度。如果我选择 C++/Cli 或您建议的任何方式,我必须添加/修改哪些项目设置才能使其正常工作?

由于我是解决此类问题的新手,因此任何示例或相关链接都会有所帮助。因此,如果您能以一种或另一种方式演示事情是如何运作的,我将不胜感激。

这里是引用的 C# dll 的一部分(其他方法省略):

公共(public)类客户端 {

    public Client()
    {
        //reset();
    }

    public void test()
    {

        Console.WriteLine("test");
    }

    /// <summary>
    /// Connect connects the Client to the server.
    /// Address string has the form host:port.
    /// </summary>
    public void Connect(string address)
    {
        Disconnect();
        // validate address
        int sep = address.IndexOf(':');
        if (sep < 0)
        {
            throw new ArgumentException("Invalid network address");
        }
        // set host and port
        host = address.Substring(0, sep);
        toPort(ref port, address.Substring(sep + 1));
        // connect
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Connect(host, port);
        rw.Set(socket, CLIENT_DEFAULT_BUFFER_SIZE); 
    }

    /// <summary>
    /// Disconnect disconnects the Client from the server.
    /// </summary>
    public void Disconnect()
    {
        backlog.Clear();
        try
        {
            if (Connected)
            {
                write("close");
            }
        }
        catch (Exception e)
        {

        }
        //reset();
        //rw.Close();
    }

}

这是 C dll 的骨架规范:

//APIs
#ifdef __cplusplus
extern "C"{
#endif

#define DLL __declspec(dllexport)

////////////////////////////////////////////////////////////////////////////
// Initialization: do some prep for receiving data from C#
// params:
//      hWnd            handle
//      Msg             message
//      nWorkMode       work mode
// return:
//       1              true    
//      -1              false
DLL int Init(HWND hWnd, UINT Msg, int nWorkMode);


// Quitting and closing
// Param:
//      hWnd            same handle as Init
//  return:
//       1              true    
//      -1              fals
DLL int Quit(HWND hWnd);

#ifdef __cplusplus
}
#endif

最佳答案

要调用外部 DLL 中的 C 函数,您可以使用您已经提到过的 C++/CLI,或者使用 P/Invoke(平台调用)。

使用 P/Invoke,您可以在 C# 程序集中定义一个static extern 方法,然后使用适当的属性对其进行修饰。之后,您可以像任何其他 C# 方法一样调用该方法,.NET Framework 管道将处理 DLL 的加载并来回编码方法参数。

例如:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);

该方法使用 staticextern 关键字进行声明,并使用 DllImport 属性进行修饰,该属性标识包含 C 函数的 DLL 。这通常是您需要的最低限度。

最难的部分是确定哪些 C 类型映射到哪些 .NET 类型的方法参数和返回类型:这有点像黑魔法!如果您访问pinvoke.net您可以查看 Windows API 是如何翻译的,这可能会有所帮助。在网络上搜索“pinvoke”也应该会找到许多有用的资源。

关于c# - 桥接非托管 C dll 和托管 C# dll,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28504108/

相关文章:

c# - 使用等于。 GetHashCode 不是

c# - 如何在代码隐藏中获取 CSS 类属性值

c# - 如何控制作为绑定(bind)源的TextBox文本何时更新目标值?

c - Robotics Cape Library 无法在 Beaglebone Blue 上工作

c - 链表 : Should the type of pointer "next" and the name of struct be same?

delphi - 如何为静态加载的DLL设置相对路径?

c++ - 无法在 Windows 7 Enterprise x64 中注册 dll

c# - 在哪里放置解决方案的通用接口(interface)以增加解耦?

c++ - 取负值时 "x = -x"和 "x *= -1"之间是否存在功能差异?

c++ - 查找导致依赖性的目标文件