我有一个第三方 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);
该方法使用 static
和 extern
关键字进行声明,并使用 DllImport
属性进行修饰,该属性标识包含 C 函数的 DLL 。这通常是您需要的最低限度。
最难的部分是确定哪些 C 类型映射到哪些 .NET 类型的方法参数和返回类型:这有点像黑魔法!如果您访问pinvoke.net您可以查看 Windows API 是如何翻译的,这可能会有所帮助。在网络上搜索“pinvoke”也应该会找到许多有用的资源。
关于c# - 桥接非托管 C dll 和托管 C# dll,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28504108/