我有一个C lib,想从C#应用程序调用这个库中的函数。我尝试通过添加C lib文件作为链接器输入并将源文件添加为附加依赖项来在C lib上创建C ++ / CLI包装器。
有没有更好的方法来实现这一目标,因为我不确定如何将C输出添加到c#应用程序。
我的C代码 -
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
我的CPP包装 -
long MyClass::ConnectSessionWrapper(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
答案 0 :(得分:77)
示例将是 Linux :
1)使用以下内容创建C
个文件libtest.c
:
#include <stdio.h>
void print(const char *message)
{
printf("%s\\n", message);
}
这是printf的一个简单的伪包装器。但代表您要调用的库中的任何C
函数。如果您有C++
函数,请不要忘记使用extern C
来避免名称错误。
2)创建C#
文件
using System;
using System.Runtime.InteropServices;
public class Tester
{
[DllImport("libtest.so", EntryPoint="print")]
static extern void print(string message);
public static void Main(string[] args)
{
print("Hello World C# => C++");
}
}
3)除非你的库libtest.so在像“/ usr / lib”这样的标准库路径中,你可能会看到一个System.DllNotFoundException,为了解决这个问题,你可以将你的libtest.so移动到/ usr / lib,或者更好的是,只需将CWD添加到库路径:export LD_LIBRARY_PATH=pwd
学分
修改强>
对于 Windows ,它没有太大的不同。
以here为例,您只需在*.cpp
文件中附上extern "C"
方法
像
extern "C"
{
//Note: must use __declspec(dllexport) to make (export) methods as 'public'
__declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)
{
printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);
}
}//End 'extern "C"' to prevent name mangling
然后,编译,并在您的C#文件中执行
[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]
public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);
然后只使用它:
using System;
using System.Runtime.InteropServices;
public class Tester
{
[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]
public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);
public static void Main(string[] args)
{
ushort var1 = 2;
char var2 = '';
DoSomethingInC(var1, var2);
}
}
答案 1 :(得分:23)
更新 - 2019年2月22日:由于这个答案已经得到了不少赞成,我决定用更好的方法来调用C方法来更新它。以前我曾建议使用unsafe
代码,但安全且正确的方法是使用MarshalAs
属性将.NET string
转换为char*
。此外,在VS2017中不再有Win32项目,您可能必须创建一个Visual C ++ DLL或空项目并进行修改。谢谢!
您可以使用P / Invoke直接从C#调用C函数 这是创建包含C dll的C#lbrary的简短方法。
将Win32项目添加到解决方案中,将应用程序类型设置为:DLL(我称之为“CLibrary”)
现在我们需要配置CLibrary项目,右键单击它并转到属性,然后选择配置:“所有配置”
<强> CLibrary.h 强>
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
<强> CLibrary.c 强>
#include "CLibrary.h"
unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return 42;
}
让Wrapper项目依赖于CLibrary是一个好主意,所以首先构建CLibrary,你可以通过右键单击Wrapper项目,转到“Project Dependencies”并检查“CLibrary”来实现。 现在为实际的包装代码:
<强> ConnectSessionWrapper.cs 强>
using System.Runtime.InteropServices;
namespace Wrapper
{
public class ConnectSessionWrapper
{
[DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern uint ConnectSession(uint handle,
[MarshalAs(UnmanagedType.LPStr)] string publicKey,
char publicKeyLen);
public uint GetConnectSession(uint handle,
string publicKey,
char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
}
}
现在只需致电GetConnectSession
,它就会返回42
。
结果:
答案 2 :(得分:3)
好的,打开VS 2010,转到文件 - &gt;新 - &gt;项目 - &gt; Visual C ++ - &gt; Win32 - &gt; Win32项目并给它一个名字(在我的情况下是HelloWorldDll),然后在 Application Type 下面的窗口中选择'DLL'并在下面添加选项选择'空项目'。
现在转到你的解决方案资源管理器标签,通常是VS窗口的右侧,右键单击源文件 - &gt;添加项目 - &gt; C ++文件(.cpp)并给它一个名字(在我的例子中是HelloWorld)
然后在新类中粘贴此代码:
#include <stdio.h>
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
}
现在构建项目,导航到您的项目 DEBUG 文件夹后,您应该找到: HelloWorldDll.dll 。
现在,让我们创建我们的C#应用程序,它将访问dll,转到文件 - &gt;新 - &gt;项目 - &gt; Visual C# - &gt;控制台应用程序并为其命名(CallDllCSharp),现在将此代码复制并粘贴到您的主要文件中:
using System;
using System.Runtime.InteropServices;
...
static void Main(string[] args)
{
Console.WriteLine("This is C# program");
DisplayHelloFromDLL();
Console.ReadKey();
}
并构建程序,现在我们已经构建了两个应用程序,可以使用它们,将* .dll和 .exe(bin / debug / .exe)放在同一目录中,执行应用程序输出应该是
这是C#程序
你好DLL!
希望能解决你的一些问题。
<强>参考强>:
答案 3 :(得分:0)
注意:下面的代码用于DLL中的多种方法。
[DllImport("MyLibc.so")] public static extern bool MyLib_GetName();
[DllImport("MyLibc.so")] public static extern bool MyLib_SetName(string name);
[DllImport("MyLibc.so")] public static extern bool MyLib_DisplayName(string name);
public static void Main(string[] args)
{
string name = MyLib_GetName();
MyLib_SetName(name);
MyLib_DisplayName(name);
}
答案 4 :(得分:0)
到目前为止,P/Invoke method已被广泛重复地描述。 我在这里缺少的是C ++ / CLI方法具有很大的优势:调用安全性。 与P / Invoke相反,在C函数中的调用就像是向天空盲目射击(如果允许此比较),因此在调用C函数时没有人会检查函数参数。 在这种情况下,使用C ++ / CLI通常意味着,要在要使用的函数原型中包含头文件。如果您使用错误的参数/太多/很少的参数调用C函数,编译器会告诉您。
我认为您通常不能说哪种方法更好,说实话,我不喜欢其中任何一个。