web-dev-qa-db-ja.com

C#.NetからC関数を呼び出すことは可能ですか

Cライブラリがあり、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);
    }
54
Chinjoo

例は、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)「/ usr/lib」などの標準ライブラリパスにlibtest.soライブラリがない場合、System.DllNotFoundExceptionが表示される可能性があります。これを修正するには、libtest.soを/ usr/libに移動するか、さらに良いのは、CWDをライブラリパスに追加するだけです:export LD_LIBRARY_PATH=pwd

ここ からのクレジット

[〜#〜] edit [〜#〜]

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);
            }
    }
80
Gonzalo.-

PDATE-2019年2月22日:この答えはかなりの数の賛成票を得ているので、Cメソッドを呼び出すより良い方法でそれを更新することにしました。以前はunsafeコードを使用することを提案していましたが、安全で正しい方法は、MarshalAs属性を使用して.NET stringchar*に変換することです。また、VS2017にはWin32プロジェクトはもうありません。おそらく、Visual C++ dllまたは空のプロジェクトを作成して変更する必要があります。ありがとうございました!

P/Invokeを使用して、C#からC関数を直接呼び出すことができます。
C dllをラップするC#ライブラリを作成する方法を簡単に説明します。

  1. 新しいC#ライブラリプロジェクトを作成します(「ラッパー」と呼びます)
  2. ソリューションにWin32プロジェクトを追加し、アプリケーションタイプをDLL(「CLibrary」と呼びます)

    • 他のすべてのcpp/hファイルは必要ないので削除できます。
    • CLibrary.cppファイルの名前をCLibrary.cに変更します
    • CLibrary.hヘッダーファイルを追加する
  3. ここで、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#プロジェクトディレクトリにDLLを取得します。
  • C#Wrapperプロジェクトを右クリックし、既存のアイテムを追加し、CLibrary.dllを追加します
  • CLibrary.dllをクリックし、プロパティペインに移動して、「出力ディレクトリにコピー」を「常にコピー」に設定します。

WrapperプロジェクトをCLibraryに依存させることをお勧めします。CLibraryを最初にビルドするには、Wrapperプロジェクトを右クリックし、[プロジェクトの依存関係]に移動して[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が返されます。

結果:
Testing wrapper library in a console application

26
Shahin Dohan

さて、VS 2010を開き、Goto File-> New-> Project-> Visual C++-> Win32-> Win32 Projectに名前を付けて(私の場合はHelloWorldDll)、次にウィンドウでアプリケーションの種類を選択'DLL'および追加オプションを選択'Empty Project'を選択します。

ソリューションエクスプローラー通常はVSウィンドウの右側のタブに移動し、右クリックソースファイル->アイテムの追加-> C++ファイル(.cpp)に名前を付けます(私の場合はHelloWorld)

次に、新しいクラスに次のコードを貼り付けます。

#include <stdio.h>

extern "C"
{
  __declspec(dllexport) void DisplayHelloFromDLL()
  {
    printf ("Hello from DLL !\n");
  }
}

ここでBuildプロジェクトに移動した後、プロジェクトに移動します[〜#〜] debug [〜#〜]フォルダーとそこにあるはずです:HelloWorldDll.dll

次に、dllにアクセスするC#アプリを作成します。Gotファイル->新規->プロジェクト-> Visual C#->コンソールアプリケーションに名前(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
David Kroukamp