[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]] * 目次 [#aa6384d6] #contents *概要 [#m5af277b] -https://github.com/OpenTouryoProject/SampleProgram --VC++ DLL:「\InteropWithUnmanage\VC\VC_DLL」 --VC++ 呼出元:「\InteropWithUnmanage\VC\VC_Client\VC_Client1」 --C# 呼出元:「\InteropWithUnmanage\DNET\DNET_Client」 *手順 [#k159e01b] **プロジェクトの作成 [#l75830cc] -新規作成→Win32プロジェクトを選択 -ウィザードで、DLLを選択。 -プロジェクトが作成される。 --dllmain.cppはDLLのエントリポイント。 --(プロジェクト名).cppにエクスポート関数を定義する。 **エクスポート関数の実装 [#t9300a13] 以下、2つのメソッドをエクスポートしている。 -Test_MYLIBAPI -TestArrayMethod ***ヘッダ・ファイル、ソース・ファイル、モジュール定義ファイルを作成して関数をエクスポートする方式 [#cc6a9e66] -ヘッダ・ファイル(MYLIBAPI.h)~ プロジェクトに、ヘッダ・ファイルを追加する。 // エクスポートとインポートの切り替え #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 リンクが一貫していません。 -ソース・ファイル(MYLIBAPI.cpp)~ プロジェクトに、ソース・ファイルを追加する。 // このソースコードの関数と変数をエクスポート #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); } -モジュール定義ファイル(Exports.def)~ プロジェクトに、モジュール定義ファイルを追加する。 ;MS系以外のツールから呼ぶときは、 ;DefファイルのEXPORTSを使用してExportする。 LIBRARY "VC_DLL" EXPORTS Test_MYLIBAPI TestArrayMethod ***明示的ロードが必要だが、関数のエクスポートが簡単な方式 [#p97c5784] 簡単な方式では明示的ロードでVC、DOTNETから使用するので、~ ヘッダ・ファイル、モジュール定義ファイルなどのモジュールが不要。 -ソース・ファイル(Simple.cpp)~ プロジェクトに、ソース・ファイルを追加する。 // 簡単な方式(明示的ロードが必要) // 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; } } *DLLの呼び出し方法 [#i3b400f4] -参考:Advanced Windows --第 4 部 DLL ---第 19 章 DLL の基礎 ---第 20 章 DLL の高度なテクニック **暗黙のうちにリンク [#y6d6bb39] ∗.libファイルを使用する。~ リンカが*.libファイルを使ってインポートされている~ 関数/変数の参照を解決しながら*.objモジュールを結合し、~ ∗.exeファイル(必要なDLLとインポートされているシンボルの~ リストであるインポートテーブルを格納している)を生成する。~ **明示的にリンク [#w266ec25] 明示的なリンクでは、*.libファイルを使用せず~ (従って、*.exeファイルはインポートテーブルを持たない)、~ Win32APIのLoadLibraryを使用してDLLをロードする。 *DLL呼び出し側コード例 [#z837f076] **VC++ [#ufc73858] -VC++からのDLL呼び出しでは、∗.libファイルを使用した暗黙のリンクを使用している。 -∗.libファイルを参照する場合は、以下の手順に従う。 --リンカ入力としての .lib ファイル~ http://msdn.microsoft.com/ja-jp/library/ba1z7822.aspx +++プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。 +++[リンカ] フォルダをクリックします。 +++[入力] プロパティ ページをクリックします。 +++[追加の依存ファイル] プロパティを変更します。 // 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; } **C# [#a947e0a4] .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); } } *補足 [#oa4f829d] -MYLIBAPI --上記コード中で定義されるマクロです。 --当該(DLLの)プロジェクトではエクスポートを実行 --(DLLの)利用側プロジェクトではインポートを実行 -extern "C"~ 関数がC言語形式 になっていることを示す。 -__stdcall = WINAPI --Web-DB プログラミング徹底解説 ---Hello, world の解説 ~ WINAPI とは何か? __stdcall の説明~ http://keicode.com/winprimer/wp07.php ---関数を呼び出すということ~ http://keicode.com/winprimer/wp07b.php -__declspec(dllexport) --DLL からのエクスポート~ http://msdn.microsoft.com/ja-jp/library/z4zxe9k8.aspx ---__declspec(dllexport) を使った DLL からのエクスポート~ http://msdn.microsoft.com/ja-jp/library/a90k134d.aspx -__declspec(dllimport) --アプリケーションへのインポート~ http://msdn.microsoft.com/ja-jp/library/kh1zw7z7.aspx ---__declspec(dllimport) を使った関数呼び出しのインポート~ http://msdn.microsoft.com/ja-jp/library/zw3za17w.aspx -モジュール定義ファイル、LIBRARY、EXPORTS~ --DEF ファイルを使った DLL からのエクスポート~ http://msdn.microsoft.com/ja-jp/library/d91k01sh.aspx *参考 [#r819416c] **DLLの作成 [#z0f83354] -Codian(DLL・フック)~ http://www.kab-studio.biz/Programing/Codian/#DLL_Hook_SClass --DLLを使おう!!~ http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/01.html --DLLの実行中リンク~ http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/02.html --DLLを作ろう!(関数編)~ http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/03.html --「自作」DLLを使おう!!~ http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/04.html --DLLを作ろう!(クラス編)~ http://www.kab-studio.biz/Programing/Codian/DLL_Hook_SClass/05.html -Win32API(C言語)編 トップページ --Win32API(C言語)編 第57章 ダイナミックリンクライラリ(DLL)~ http://www.geocities.jp/ky_webid/win32c/057.html --Win32API(C言語)編 第58章 LoadLibrary()によるDLLロード~ http://www.geocities.jp/ky_webid/win32c/058.html --Win32API(C言語)編 第59章 .DEFファイルによるDLLエクスポート~ http://www.geocities.jp/ky_webid/win32c/059.html -ダイナミック リンク ライブラリ(DLL)の基礎知識~ http://exlight.net/devel/windows/dll/windll.html -VC++DLL作成補足(Hishidama's VC++Memo DLL)~ http://www.ne.jp/asahi/hishidama/home/tech/vcpp/dllusage.html -C++で作成したDLLを他の言語で利用できるようにする » 二流プログラマの三流な日常~ http://blogs.konuma.org/blog/2007/04/c_b43e/ **P/Invoke [#t9cf3ae5] -P-Invoke - Wikipedia~ http://ja.wikipedia.org/wiki/P/Invoke -pinvoke.net the interop wiki!~ http://www.pinvoke.net/