Open棟梁Project - マイクロソフト系技術情報 Wiki

* 目次 [#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]
&#8727;.libファイルを使用する。~

リンカが*.libファイルを使ってインポートされている~
関数/変数の参照を解決しながら*.objモジュールを結合し、~
&#8727;.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/

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS