可以从C#Net调用C函数吗
我有一个C库,想从C#应用程序调用这个库中的函数。我尝试在C库上创建一个C++/CLI包装器,方法是将C库文件添加为链接器输入,并将源文件添加为其他依赖项 有没有更好的方法来实现这一点,因为我不知道如何将C输出添加到C#应用程序中 我的C代码-可以从C#Net调用C函数吗,c,c#-4.0,interop,C,C# 4.0,Interop,我有一个C库,想从C#应用程序调用这个库中的函数。我尝试在C库上创建一个C++/CLI包装器,方法是将C库文件添加为链接器输入,并将源文件添加为其他依赖项 有没有更好的方法来实现这一点,因为我不知道如何将C输出添加到C#应用程序中 我的C代码- __declspec(dllexport) unsigned long ConnectSession(unsigned long handle, unsigned char * publicKey,
__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);
}
例如,对于Linux:
1) 使用以下内容创建一个C
文件,libtest.C
:
#include <stdio.h>
void print(const char *message)
{
printf("%s\\n", message);
}
3) 除非在标准库路径(如“/usr/lib”)中有libtest.so库,否则您可能会看到System.DllNotFoundException,要解决此问题,您可以将libtest.so移动到/usr/lib,或者更好,只需将CWD添加到库路径:export LD\u library\u path=pwd
来自
编辑
对于Windows来说,这没什么不同。
举一个例子,您只需在*.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#文件中执行以下操作:
然后使用它:
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);
}
}
<好的,打开VS 2010,Goto文件> NeX-> Project -VisualC++ + > Win32–> Win32项目,并给它命名(HeloRoLDLDL在我的例子中),然后在应用程序类型下面的窗口中选择“DLL”,在附加选项下选择“空项目”。 现在转到您的解决方案资源管理器选项卡,通常是VS窗口的右侧,右击源文件->添加项-> C++文件(.CPP),并给它命名(HeloWord在我的例子中) 然后在新类中粘贴此代码:
#include <stdio.h>
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
}
然后构建程序,现在我们已经构建了两个应用程序,让我们使用它们,将*.dll和.exe(bin/debug/.exe)放在同一个目录中,并执行应用程序输出
这是C#程序
你好,来自DLL
希望这能解决你的一些问题
参考资料:
unsafe
code,但安全正确的方法是使用MarshalAs
属性将.NET字符串
转换为char*
。此外,在VS2017中不再有Win32项目,您可能需要创建VisualC++或DLL项目并修改它。谢谢大家!
您可以使用p/Invoke从C#直接调用C函数。下面是一个关于创建围绕C dll的C#lbrary的简短操作
- 您可以删除所有其他cpp/h文件,因为我们不需要它们
- 将CLibrary.cpp文件重命名为CLibrary.c
- 添加CLibrary.h头文件
- 在配置属性>C/C++>预编译头中,将预编译头设置为:“不使用预编译头”
- 在同一个C/C++分支中,转到高级,将编译更改为:“编译为C代码(/TC)”
- 现在在链接器分支中,转到常规,并将输出文件更改为:“$(SolutionDir)Wrapper\$(ProjectName).dll”,这将把构建的C dll复制到C#项目根目录
__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;
}
- 右键单击CLibrary项目,构建它,这样我们就可以在C#project目录中获得DLL
- 右键单击C#包装器项目,添加现有项,添加CLibrary.dll
- 单击CLibrary.dll,转到属性窗格,将“复制到输出目录”设置为“始终复制”
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
结果:注意:下面的代码用于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);
}
到目前为止,已经对该问题进行了广泛而反复的描述。
这里我缺少的是,C++/CLI方法有一个很大的优势:调用安全性。
与P/Invoke不同,在P/Invoke中,调用C函数就像瞎了眼睛(如果允许进行这种比较),调用C函数时没有人会检查函数参数。
在这种情况下,使用C++/CLI意味着通常情况下,您会包含一个headerfile,其中包含您想要使用的函数原型。如果您使用错误/到多/到少的参数调用C函数,编译器会告诉您
我不认为你能概括地说哪种方法更好,老实说,我不喜欢这两种方法。是的,这是可能的,如果你用谷歌搜索它,会有很多例子,但是从有限的信息中不知道你的问题是什么如果你做X时会受伤,停止做X. Rename的C++包装方法,将其排除为问题的根源。并发布您的代码:-)我有一个由供应商提供的c库,它具有类似ConnectToMachine(unsignedchar*psig,charapt)的函数。我想从我的c#类中调用这个函数。看看Q:可以从c#.Net调用c函数吗?是的,很明显!问:C++/CLI包装器是一种好方法吗?我个人认为C++和CLI是一种憎恶。但是人们在这样的场景中使用它。我更愿意直接使用Interop(即PInvoke)——所有的C#,一直到本机的非托管C/C++。有很多教程,并不难。当然没有比从VB6中调用C/C++更难的了;)好例子!这是一个很好的例子
#include "CLibrary.h"
unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return 42;
}
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);
}
}
}
[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);
}