C で作った DLL を C# で使う

August 8, 2012

C で DLL を作る

C で書かれた次の資産 arith.c を DLL 化し,C# のプログラムから使いたいとする。

// arith.c
int add(int a, int b) { return a + b; }
double div(double a, double b) { return a / b; }

これを DLL 化するには,次のようにソースコードに手を加える。

// arith.c
__declspec(dllexport) int __stdcall add(int a, int b)
{ return a + b; }
__declspec(dllexport) double __stdcall div(double a, double b)
{ return a / b; }

__declspec(dllexport) を指定すると,その関数はエクスポート (外部に公開) される。
__stdcall は呼び出し規約と呼ばれるもので,他に __cdecl,__fastcall などがある。

このソースコードを DLL としてコンパイルするには,次のようにオプションを指定してコンパイルする (上から VC++, BCC, GCC)。

> cl /LD arith.c
> bcc32 -WD arith.c
> gcc -shared -o arith.dll arith.c -Wl,--add-stdcall-alias

C++ プログラムを DLL 化する場合,関数の名前が変更されないようにするには extern "C" を指定する必要がある。

// arith.cpp
extern "C" {
    ...
}

C# で DLL を利用する

DLL を取り込む側である C# のコードは次のように書く。

// TestDll.cs
using System;
using System.Runtime.InteropServices;
class TestDll
{
    [DllImport("arith.dll")]
    static extern int add(int a, int b);

    [DllImport("arith.dll")]
    static extern double div(double a, double b);

    static void Main()
    {
        Console.WriteLine("5 + 2 = {0}", add(5, 2));
        Console.WriteLine("5 / 2 = {0}", div(5, 2));
    }
}

System.Runtime.InteropServices.DllImport 属性は,指定したアンマネージド DLL から関数が読み込まれることを示す。

これをコンパイルし,同じディレクトリに arith.dll を置けば,プログラムを実行できる。

> csc TestDll.cs
> TestDll
5 + 2 = 7
5 / 2 = 2.5

System.BadImageFormatException が発生する場合は,DLL と実行可能ファイルのターゲット CPU を一致させればよい。

> csc /platform:x86 TestDll.cs

文字列を扱う

文字列を受け渡しする次の mystrcpy() 関数を DLL 化する。

// strutil.c
#include <string.h>

__declspec(dllexport) void __stdcall
    mystrcpy(char *dest, const char *src)
{
    strcpy(dest, src);
}

これを C# から呼び出すためには,次のようにする。

// TestDll.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
class TestDll
{
    [DllImport("strutil.dll")]
    static extern void mystrcpy(StringBuilder dest, string src);

    static void Main()
    {
       StringBuilder dest = new StringBuilder(1024);
       string src = "This is some sample text.";
       mystrcpy(dest, src);
       Console.WriteLine(dest.ToString());
    }
}
> csc TestDll.cs
> TestDll
This is some sample text.

char * は System.Text.StringBuilder,const char * は string に対応付けられる。
StringBuilder を使用する際は,new StringBuilder(1024) のように,十分な領域を確保する必要がある*1

C の型C# の型
const char *string
char *System.Text.StringBuilder

関数宣言の頭に unsafe を指定すれば,ポインタをポインタのまま利用することも可能。

その他の型の対応については,次のサイトが参考になる。

.NET TIPS Win32 API や DLL 関数を呼び出すには? - @IT
http://www.atmarkit.co.jp/fdotnet/dotnettips/024w32api/w32api.html

(*1) ご指摘をいただき,new StringBuilder() を new StringBuilder(1024) に訂正,その旨補足 (2012-08-20)。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

post date*