是否可以从C#.Net调用C函数

Chi*_*joo 55 c interop c#-4.0

我有一个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);
Run Code Online (Sandbox Code Playgroud)

我的CPP包装 -

long MyClass::ConnectSessionWrapper(unsigned long handle,
                                unsigned char * publicKey,
                                unsigned char   publicKeyLen)
    {
        return ConnectSession(handle, publicKey, publicKeyLen);
    }
Run Code Online (Sandbox Code Playgroud)

Gon*_*o.- 81

对于Linux,示例将是:

1)创建一个包含以下内容的C文件libtest.c:

#include <stdio.h>

void print(const char *message)
{
  printf("%s\\n", message);
}
Run Code Online (Sandbox Code Playgroud)

这是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++");
        }
}
Run Code Online (Sandbox Code Playgroud)

3)除非您在"/ usr/lib"之类的标准库路径中拥有库libtest.so,否则您可能会看到System.DllNotFoundException,为了解决此问题,您可以将libtest.so移动到/ usr/lib,或者更好的是,只需将CWD添加到库路径: export LD_LIBRARY_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
Run Code Online (Sandbox Code Playgroud)

然后,编译,并在你的C#文件中做

[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);
Run Code Online (Sandbox Code Playgroud)

然后使用它:

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);
            }
    }
Run Code Online (Sandbox Code Playgroud)

  • 好例子!这是使用Interop的一个很好的例子.它显然是Linux(不是Windows),但原理是一样的.这是*我确实推荐给OP的方法. (6认同)

Sha*_*han 28

更新 - 2019年2月22日:由于这个答案已经得到了不少赞成,我决定用更好的方法来调用C方法来更新它.以前我曾建议使用unsafe代码,但安全和正确的方法是使用MarshalAs属性将.NET string转换为char*.此外,在VS2017中不再有Win32项目,您可能必须创建一个Visual C++ DLL或空项目并进行修改.谢谢!

您可以使用P/Invoke直接从C#调用C函数.
这是创建包含C dll的C#lbrary的简短操作方法.

  1. 创建一个新的C#Library项目(我称之为"Wrapper")
  2. 将Win32项目添加到解决方案中,将应用程序类型设置为:DLL(我将其称为"CLibrary")

    • 您可以删除所有其他cpp/h文件,因为我们不需要它们
    • 将CLibrary.cpp文件重命名为CLibrary.c
    • 添加CLibrary.h头文件
  3. 现在我们需要配置CLibrary项目,右键单击它并转到属性,然后选择Configuration:"All Configurations"

    • 在"配置属性">"C/C++">"预编译头"中,将"预编译头"设置为:"不使用预编译头"
    • 在同一个C/C++分支中,转到Advanced,将Compile as更改为:"Compile as C code(/ TC)"
    • 现在在Linker分支中,转到General,并将Output File更改为:"$(SolutionDir)Wrapper\$(ProjectName).dll",这会将构建的C DLL复制到C#项目根目录.

CLibrary.h

__declspec(dllexport) unsigned long ConnectSession(unsigned long   handle,
                                                   unsigned char * publicKey,
                                                   unsigned char   publicKeyLen);
Run Code Online (Sandbox Code Playgroud)

CLibrary.c

#include "CLibrary.h"

unsigned long ConnectSession(unsigned long   handle,
                             unsigned char * publicKey,
                             unsigned char   publicKeyLen)
{
    return 42;
}
Run Code Online (Sandbox Code Playgroud)
  • 右键单击CLibrary项目,构建它,以便我们在C#项目目录中获取DLL
  • 右键单击C#Wrapper项目,添加现有项目,添加CLibrary.dll
  • 单击CLibrary.dll,转到属性窗格,将"复制到输出目录"设置为"始终复制"

让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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在只需打电话GetConnectSession,它应该返回42.

结果:
在控制台应用程序中测试包装器库