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);
}
}