C#からネイティブC++を使うには

2009.01.17 土 16:36:55

時代はすでにJavaやC#をはじめとしたJIT方式の高度に管理されたプログラミングが主流となっています。しかし、やはり速度が必要な部分にC++を使いたいという要望は当然。

ということでC#からC++のアンマネージドなライブラリを呼び出す方法が知りたかったので調べました。

方法1:C#から直接DLLを呼び出す
方法2:C++/CLIによる薄いラップライブラリを作成

ここでは方法2を使います。
自作のネイティブライブラリをスタティックライブラリとしてビルドし、それをC++/CLIでラップしてマネージなDLLとして出力、あとはC#側で参照に追加すれば普通に使えます。
既存の(アンマネージな)DLLをラップするときも同じです。

とにかくかゆいところに手が届くC++/CLIに感動しました。

1.C++でネイティブなライブラリ(スタティック/DLL)を作成する

これについては特段の説明は不要でしょう。
Visual Studioでプロジェクトを生成するときにVisual C++からWin32のWin32 プロジェクトを選択します。ウィザードで「スタティックライブラリ」か「DLL」を選択します。

これで必要な関数をC++スタイルで(クラスのスタティックメンバとして)追加します。

これをビルドすると、DLLでもスタティックライブラリでも、*.libファイルが生成されます。これを後にラッパに使用します。

注意?: DLLとして作成した場合、ただしくエクスポートしないと*.libファイルすら生成されません。
クラス定義のほうで
__declspec(dllexport) type __stdcall func(...)
とちゃんと記述しましょう。ハマると長時間迷います(迷った!)。

注意?: ちゃんとヘッダ(*.h)とソース(*.cpp)に分けましょう。あとで使います。

例:

//func.h

class TestClass

{

public:

    static void func();

};



//func.cpp

#include "stdafx.h"

#include "func.h"

void TestClass::func()

{

     //noop

}


ソースを書いたらひとまずビルドしましょう。

なおサードパーティ製DLLで*.libが付属している場合は直接2にいけます。
付属してない場合は、一番上にある方法1を使うことになります。

2.C++/CLIによるラッパライブラリを作成

ここで新たなプロジェクトを生成します。
Visual C++のCLRのクラスライブラリを選択します。これは.NETの他言語から直接アクセスできるマネージDLLを生成します。

まず、ヘッダを追加します。
先ほどのfunc.hの内容をそっくりそのままコピーして持ってきます。
なおファイルごとCtrl+D&Dでコピーするとビルドできなくなります(ファイル実体が増えないからか)。中身をコピーしてください。

つぎに、リンクするライブラリを設定します。
ソリューションエクスプローラからラッパプロジェクトを選んで右クリック、プロジェクトのプロパティを開き、構成プロパティ→リンカ→入力のページにある追加の依存ファイルの項目に、1で作成したライブラリのlibファイルのパスを入力します。

必須ではないですが、特にstaticライブラリの場合はプロジェクト依存関係を正しく設定した方がいいです。ラッパはネイティブなライブラリに依存します。

// Wrapper.h

#pragma once

using namespace System;

namespace Wrapper

{

    public ref class Wrap

    {

    public:

        static void func();

    };

}



//Wrapper.cpp

#include "stdafx.h"

#include "Wrapper.h"

#include "func.h"

using namespace Wrapper;

using namespace System;

void Wrap::func()

{

    TestClass::func();

}

これでビルドすれば、DLLが生成されます。

3.C#でのプログラムを生成

あとはこれを使うC#のプログラムを作成するのみです。
ラッパーは.NETなクラスなので、C#プロジェクトの参照に追加するだけで、IntelliSenceまでちゃんと使えるようになります。

Wrapper.Wrap.func(buf, buf.Length);


こんなかんじ。

パフォーマンスについて

(1)ネイティブなDLLをラップしたマネージDLL経由でコール
(2)ネイティブなスタティックライブラリをラップしたマネージDLL経由でコール
(3)C#に直接書いたメソッドをコール

これらのコール速度を比較したところ、(3)はダントツではやく、(2)が(1)より若干速いながらも(3)よりは数十倍劣る感じとなりました。

もちろん絶対時間がきわめて小さく、μ秒以下のオーダーなので、何らかの処理を行うとなると状況が変わってきます。

関数ないで数千〜数万要素ほどの配列をバブルソートするプログラムを追加したところ、コールのオーバーヘッドは全くみられず、(1)、(2)が全く同等、(3)がほぼ半分の速度でした。簡単なプログラムでしたがネイティブのパワーが発揮されたと言えます。

(1)と(2)では、コールのオーバーヘッドには有意差がありましたが、実行処理速度は同等で速いです。

(1)ではDLLがネイティブ、ラッパでそれぞれ別々に存在し、つねに同じディレクトリに入れ…など煩雑となります。?はネイティブコードがラッパDLLに埋め込まれているため、DLLは一つです。

このことから、自作のネイティブライブラリをC++/CLIでラップするなら、ネイティブ側はスタティックライブラリとして作成したほうが良いと言えるように思います。
もちろん状況次第ですが。

引数を渡したい

やはりネイティブコードに処理を委託する以上、なんらかのデータを渡せなければ意味はないです。

このあたりもすでに解決したので次の記事くらいで。

  1. 2009年01月17日(土) 16時36分55秒|
  2. カテゴリ: C#|
  3. コメント:0

コメント


コメントの投稿

管理者にだけ表示を許可する


画像の文字を半角数字で下記ボックスに記入ください。
文字が読みにくい場合はブラウザの更新をすると新しい文字列が表示されます。

プロフィール

xptn
Author:xptn
例外人生まっしぐらの高専卒大学生。平成生まれの21歳。
twitter

最近のエントリー

カテゴリー

最近のコメント

月別アーカイブ

リンク

RSSリンク

ブラックジャックによろしく
DTIブログ
ブログでアフィリエイト
DTIブログポータルへ
このブログを通報
Report Abuse

人気ブログランキング
利用規約