桥接非托管C dll并管理C#dll

时间:2015-02-13 16:25:12

标签: c# c dll interop wrapper

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

数据流如下:当应用程序启动时,它会加载并调用包含多个导出函数(包括init函数)的C dll。在这个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 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

1 个答案:

答案 0 :(得分:0)

要在外部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”也应该提供一些有用的资源。