C/C++で実装したDLLをC#から呼び出す(2)
エラーについて

どもです。
前回のエントリにて、C#で実装されたプログラムからC/C++で実装された「任意の」DLLをロードする方法について書きました。
その中で、アプリケーションとDLLのプラットフォームを一致指せておく必要があると書いています。
しかし揃えなかった場合には、どのような現象/エラーが発生するかまでは書いていません。

そこで今回は、(プラットフォームを一致させなかった場合に限らず)任意のDLLをロードする際に発生する、または発生させてしまう可能性があるエラー/例外と、その対処法について書いていきます。

…平たく言うと、前回のエントリの投稿に当たり、「自分がハマった内容」について書く、ということです。

1. プラットフォームの不一致

1.1. 原因と現象

まず、「プラットフォームの不一致」です。
この場合、「GetDelegateForFunctionPointer()」関数にて「ArgumentNullException」が発生します。
コードレベルの原因を見てみると、プラットフォームが一致していない場合、「GetDelegateForFunctionPoter()」を実行する前の「LoadLibrary」または「GetProcAddress」関数の戻り値が「0」となっています。
そもそも、「LoadLibrary」の段階でDLLの読み込みに失敗している、ということになります。



1.2. 対策

この例外は、「LoadLibrary」または「GetProcAddress」関数の戻り値のチェック処理を実装しておくことで対応が可能かと思います。

更に言うと、上記2つの関数でエラーが発生した場合には、GetLastError関数でより詳細なエラー情報が取得可能(のよう)です。
そのため、この関数と併せてエラー処理を実装しておくよ良いかもしれません。

GetLastError関数の仕様は、Microsoftの公式ページで確認できます。

2. 呼び出し規約の不一致

2.1. 原因と現象

次のエラーは、「呼び出し規約」の指定の不一致です。
VisualStudio2019の「C/C++ DLL ライブラリの開発」プロジェクトでは、生成されるDLLの呼び出し規約が「__cdecl」となっています。
これに対して、関数を「__stdcall」付きで宣言/実装したりしていた場合には、エラーとなったります。

なお、「エラー」と言っても、これで発生するエラーは、VisualStudioで実行している場合にのみ発生します。
しかも、無視ができます。
実際にステップ実行でエラーが発生した場合でも、無視して次のステップ(コード)を実行できたりしました。

2.2. 対策

もう、C/C++のDLLの実装に注意する以外に、対策はないかと思います。
C/C++のプロジェクトの設定を、必ず確認するようにしましょう。

ちなみに、設定場所はココ!



別の対策として、関数の宣言の際に「__stdcall」とか「__cdecl」を付与しない、という方法もあります。
呼び出し規約はコンパイラの設定と処理に任せ、関数そのものの宣言では何もしない、というのも有効な対策と考えています。

3. exportされていない

3.1. 原因と現象

次のエラーは、export宣言されていない関数へのアクセスです。

C/C++のDLLが外部に公開する関数については、以下のように関数を宣言します。

__declspec(dllexport) int Add(int value1, int value2)

この「__declspec(dllexport)」宣言がされていない関数にアクセスしようとした場合、「GetDelegateForFunctionPointer()」関数にて「ArgumentNullException」が発生します。
コードレベルの原因を見てみると、「__declspec(dllexport)」宣言がされていない関数にアクセスした場合、「GetDelegateForFunctionPoter()」を実行する前の「GetProcAddress」関数の戻り値が「0」となっています。



これ以降は、ココで書いた内容と同じです。

3.2. 対策

原因/現象が同じである以上、対策も同じです。

4. まとめ

今回は、前回のエントリで紹介した、C#からC/C++のライブラリを呼び出す/実行する中で、私がハマった内容をまとめました。
基本的にはハマらないような内容、基本的なことが原因で発生する現象ばかりです。
しかし、実際にこの現象が発生すると、その原因委は中々気が付けない内容ではないかと思います。

この記事が、誰かの助けになれば幸いです。

ではっ!