Open棟梁Project - マイクロソフト系技術情報 Wiki
以下、2つのメソッドをエクスポートしている。
// エクスポートとインポートの切り替え #ifdef MYLIBAPI // VC_DLL_EXPORTS でもいいかも // DLLのPJではソース・ファイル(MYLIBAPI.cpp)を使用して関数をエクスポート。 #else // EXEの(DLLを利用する)PJではヘッダ・ファイル(MYLIBAPI.h)を使用して関数をインポート。 // extern "C" は、他の言語から呼ぶ場合に必要。 #define MYLIBAPI extern "C" __declspec(dllimport) #endif // 以下、エクスポート関数のプロトタイプ宣言 // 通常、__stdcallを適用する(__stdcall = WINAPI)。 MYLIBAPI void __stdcall Test_MYLIBAPI(LPCWSTR lpText, LPCWSTR lpCaption); // この方式だと、C4603、C4273の警告が出るが、問題はない。 // ・C4603:'<identifier>': マクロが定義されていないか、 // プリコンパイル済みヘッダーが使用している定義とは異なります。 // ・C4273:dll リンクが一貫していません。
// このソースコードの関数と変数をエクスポート #define MYLIBAPI extern "C" __declspec(dllexport) #include <stdafx.h> #include <windows.h> #include <stdio.h> // エクスポート関数の宣言 #include "MYLIBAPI.h" // 以下、エクスポート関数の実装 // 通常、__stdcallを適用する(__stdcall = WINAPI)。 void __stdcall Test_MYLIBAPI(LPCWSTR lpText, LPCWSTR lpCaption) { // MessageBoxを呼び出すだけ。 MessageBox(NULL, lpText, lpCaption, MB_OK); }
;MS系以外のツールから呼ぶときは、 ;DefファイルのEXPORTSを使用してExportする。 LIBRARY "VC_DLL" EXPORTS Test_MYLIBAPI TestArrayMethod
簡単な方式では明示的ロードでVC、DOTNETから使用するので、
ヘッダ・ファイル、モジュール定義ファイルなどのモジュールが不要。
// 簡単な方式(明示的ロードが必要) // Simple.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。 #include "stdafx.h" #include <stdio.h> // 以下は、 // The try, catch, and throw Statements // http://msdn.microsoft.com/en-us/library/6dekhbbc(VS.80).aspx // から引用 class CTest { public: CTest() {}; ~CTest() {}; const char *ShowReason() const { return "Exception in CTest class."; } }; extern "C"{ typedef struct _A{ int m1; char m2_in[12]; // 11文字まで (MarshalAs(UnmanagedType.ByValTStr, SizeConst=12) char m3_out[48]; // 47文字まで (MarshalAs(UnmanagedType.ByValTStr, SizeConst=48) } A; // 明示的ロードでVC、DOTNETから使用するのでヘッダーファイルは不要 // なお、__cdeclでもDOTNETから正しく呼び出せることを確認している。 // 通常、__stdcallを適用する(__stdcall = WINAPI)。 __declspec(dllexport) int __stdcall TestArrayMethod(A a, A *pa, int len, A aa[]); int __stdcall TestArrayMethod(A a, A *pa, int len, A aa[]){ // 配列の長さが0未満はありえないので // 例外を発行する(C++の例外を発行してみる) if(len < 0){ throw CTest(); } // extern "c" で警告が表示される(__cdeclでも__stdcallでも)。 // 値渡しの構造体 a char *p_inputmessage = a.m2_in; int counter = a.m1; // 参照渡しの構造体 *pa(構造体 a からコピー) pa->m1 = ++counter; // 先に ++ してから代入 sprintf_s(pa->m3_out, 47, "%s by pa->", p_inputmessage); // 参照渡しの構造体配列 aa[](構造体 a からコピー) int i; for (i = 0; i < len; i++){ aa[i].m1 = ++counter; // 先に ++ してから代入 sprintf_s(aa[i].m3_out, 47, "%s by aa[%d].", p_inputmessage, i); } return counter; } }
∗.libファイルを使用する。
リンカが*.libファイルを使ってインポートされている
関数/変数の参照を解決しながら*.objモジュールを結合し、
∗.exeファイル(必要なDLLとインポートされているシンボルの
リストであるインポートテーブルを格納している)を生成する。
明示的なリンクでは、*.libファイルを使用せず
(従って、*.exeファイルはインポートテーブルを持たない)、
Win32APIのLoadLibrary?を使用してDLLをロードする。
// VC_Client1.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <windows.h> #include "../../VC_DLL/MYLIBAPI.h" int _tmain(int argc, _TCHAR* argv[]) { // 通常のLibを使用したDLL関数の呼び出し方法。 Test_MYLIBAPI(L"text", L"caption"); return 0; }
.NETからのDLL呼び出し(P/Invoke)では、 明示的なリンクを使用している。
/// <summary>VC_DLL.TestArrayMethod</summary> [DllImport("VC_DLL.dll")] private extern static int TestArrayMethod(A a, ref A pa, int len, [In, Out] A[] aa); // 第4引数(構造体配列)を、結果受け取り用で宣言する場合は // "ref" では不可、"[Out]" 属性を設定する必要がある /// <summary>DLL呼び出し</summary> private void Button1_Click(object sender, RoutedEventArgs e) { try { A a1 = new A(); A a2 = new A(); A[] a3 = new A[3]; a1.m1 = 8; a1.m2_in = "{入力値側}"; // 正常系: 第3引数(配列サイズ)を正しく指定 int ret1 = TestArrayMethod(a1, ref a2, a3.Length, a3); } catch (SEHException ex) { //MessageBox MessageBox.Show( "Err.Number:" + ex.ErrorCode + "\r\n" + "Err.Description:" + ex.Message, "例外", MessageBoxButton.OK); } }