Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可以从C#Net调用C函数吗_C_C# 4.0_Interop - Fatal编程技术网

可以从C#Net调用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,

我有一个C库,想从C#应用程序调用这个库中的函数。我尝试在C库上创建一个C++/CLI包装器,方法是将C库文件添加为链接器输入,并将源文件添加为其他依赖项

有没有更好的方法来实现这一点,因为我不知道如何将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);
    }
例如,对于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

希望这能解决你的一些问题

参考资料


更新-2019年2月22日:由于这个答案已经获得了很多赞成票,我决定用一种更好的方法来调用C方法来更新它。以前我建议使用
unsafe
code,但安全正确的方法是使用
MarshalAs
属性将.NET
字符串
转换为
char*
。此外,在VS2017中不再有Win32项目,您可能需要创建VisualC++或DLL项目并修改它。谢谢大家!

您可以使用p/Invoke从C#直接调用C函数。
下面是一个关于创建围绕C dll的C#lbrary的简短操作

  • 创建一个新的C#库项目(我称之为“包装器”)
  • 将Win32项目添加到解决方案中,将应用程序类型设置为:DLL(我称之为“CLibrary”)

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

    • 在配置属性>C/C++>预编译头中,将预编译头设置为:“不使用预编译头”
    • 在同一个C/C++分支中,转到高级,将编译更改为:“编译为C代码(/TC)”
    • 现在在链接器分支中,转到常规,并将输出文件更改为:“$(SolutionDir)Wrapper\$(ProjectName).dll”,这将把构建的C dll复制到C#项目根目录
  • 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;
    }
    
    • 右键单击CLibrary项目,构建它,这样我们就可以在C#project目录中获得DLL
    • 右键单击C#包装器项目,添加现有项,添加CLibrary.dll
    • 单击CLibrary.dll,转到属性窗格,将“复制到输出目录”设置为“始终复制”
    让包装器项目依赖于CLibrary是一个好主意,因此首先构建CLibrary,您可以右键单击包装器项目,转到“项目依赖项”并选中“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

    结果:

    注意:下面的代码用于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);
    }