![見出し画像](https://assets.st-note.com/production/uploads/images/86997962/rectangle_large_type_2_d589c5538523f2a49be83c5eea1400ab.jpeg?width=1200)
既存のC++ネイティブプロジェクトでC#マネージドコードを使う(C++/C#)
はじめに
こんにちは。Daddy's Officeの市川です。
私が10年以上開発を続けているWindowsPCを監視カメラシステムにする「LiveCapture3」。
先日、今後の拡張性や生産性を考慮して、内部処理を見直しました。
基本的には、C++で構築していた各機能を、C#で置き換える、という作業です。
個人的には今でもC++は好きな言語なのですが、Windowsアプリ開発を行う場合、APIや各種SDKの提供状況を考えると、C#に移行していかないと難しい状況です。
とはいっても、すべてをC#に移行するのは現実的ではありません。
そこで「C++からC#をコールする」方法を調べました。
C++からC#をコールする方法
色々な方法がありますが、C#の生産性の高さを既存のC++ネイティブプロジェクトで利用したい、というのが大きな目的なので、C++/CLIは極力使いたくない。
C#の処理をCOM参照可能にする、というのもコード量が増えるしめんどくさい。
そこで、
「C#処理をDLLで作成し、C++/CLIラッパープロジェクト経由で、C++ネイティブプロジェクトからコールする」
という方法で対応しました。
これであれば、若干スマートさには欠けますが、既存のC++ネイティブプロジェクトを変更せず、生産性の高いC#で処理を記述できます。
構成としてはこんな感じです。
![](https://assets.st-note.com/img/1663315207148-Tio06LR6Oa.png)
実装(AES複号)
今回、AESの複号処理を追加する必要があったのですが、C++でAES複号処理を記述するのは非常に面倒。
そこで、この方法を使用して、AES複号処理をC#で記述し、それをネイティブC++からコールする形にしました。
DLLプロジェクト(C#)
まず、AESの複号を行うC#のクラスを作成します。
プロジェクトをC#のクラスライブラリとして作成し、下記のようなクラスを定義します。
うーん、簡単です!
メソッドはDecodeメソッドのみで、引数に、鍵と初期ベクタ、AESエンコードされたデータを渡すと、out引数にデコードされたデータが格納される感じです。
DLLラッパープロジェクト(C++/CLI)
作成したDLLプロジェクトを参照に追加して、C++/CLIのDLLプロジェクトを作成します。
このプロジェクトの目的は、マネージドとアンマネージドの引数型変換と、参照で追加したAES複号クラスの生成とメソッドコールになります。
AES複号を行うので、扱うデータはメモリ上のバイナリ値になります。
当然、C++上で確保したメモリ領域のポインタをC#クラスに渡すことはできませんので、変換処理が必要になります。
まず、C#クラスに渡す為のByte配列を定義し、そこに、引数で渡されてきたメモリの中身をMarshal::Copyでコピーします。
引数の内容がコピーされたByte配列を引数にC#の複号クラスのメソッドをコールします。
結果はout引数のByte配列に確保されますので、一旦pin_ptrでピン止めしてからmemcpyで内容をコピーします。
C++/CLIは、(個人的には)あまり書きたくないので、極力、型変換処理のみに抑えるようにしています。
メインプロジェクト(ネイティブC++)
あとは、作成したDLLラッパープロジェクト(C++/CLI)でExportされた関数をC、++ネイティブプロジェクトで呼び出すだけです。
下記のようなExport宣言を行います。
この関数をC++ネイティブプロジェクト内でコールすると、AESの複号処理が実行されます。