山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他DLLファイルからLIBファイルを作成する(_stdcall宣言)
概要
DLLロードの仕方には、静的リンク(暗黙的)と動的リンクがあります。
静的リンクの場合は、LIBファイルが必要となりますが、例えば文字コードの変換を行うMLANG.DLL等は、ヘッダーファイルは存在しますが、なぜかMLANG.LIBは存在しません。
LIBファイルがない場合は、DLLファイルからDEFファイルを作成し、DEFファイルをもとにLIB.EXEによりLIBファイルを作成することができます。
ただし、extern "C" _stdcallで定義されている関数の場合、 DEFファイルのみでLIB.EXEを使用して作成すると32bitの場合コンパイラの名前修飾規則とLIB.EXEの名前検索方法の指定が一致しないためリンクエラーか実行時にエラーが発生します。(Windows 64bitでは__stdcallは無視されるのでLIB.EXEのみで正常なLIBファイルを作成可能)
静的リンクの場合は、LIBファイルが必要となりますが、例えば文字コードの変換を行うMLANG.DLL等は、ヘッダーファイルは存在しますが、なぜかMLANG.LIBは存在しません。
LIBファイルがない場合は、DLLファイルからDEFファイルを作成し、DEFファイルをもとにLIB.EXEによりLIBファイルを作成することができます。
ただし、extern "C" _stdcallで定義されている関数の場合、 DEFファイルのみでLIB.EXEを使用して作成すると32bitの場合コンパイラの名前修飾規則とLIB.EXEの名前検索方法の指定が一致しないためリンクエラーか実行時にエラーが発生します。(Windows 64bitでは__stdcallは無視されるのでLIB.EXEのみで正常なLIBファイルを作成可能)
32bitで正常に動作する場合の各関数名及びエクスポート名
呼び出し側のconvstr2.cppのプロトタイプ宣言
extern "C" HRESULT __stdcall ConvertINetMultiByteToUnicode(LPDWORD , DWORD , LPCSTR , LPINT , LPWSTR , LPINT );
呼び出し側のmlang.objファイルの呼び出し名
_ConvertINetMultiByteToUnicode@24__stdcallを宣言することにより先頭に_末尾に@24がコンパイラにより付加される。
呼び出されるmalng.dll内のエクスポート名
ConvertINetMultiByteToUnicode
リンク時に指定するmlang.libファイルのエクスポート名
_ConvertINetMultiByteToUnicode@24libファイル内で先頭の_と末尾の@24を無視する方法が指定されているのでリンク及び実行時にエラーが発生しない。 ここでは、Windows APIをDEFファイルとソースファイルを作成し、LIBファイルを作る方法を記載し 実際にmlang.dllからmlang.libを作成しConvertINetMultiByteToUnicode APIを呼び出す例を次項以降で示します。64bitでのLIBファイルの作成
64bitでのコンパイルでは__stdcallが無視されるため、extern "C"により名前の修飾が行われません。 64bitのmlang.dllからmlang.libを作成する場合は、以降で説明する面倒な方法を用いずLIB.EXEを用いて以下のようにVisual C++のコマンドプロンプトで以下の様に入力すれば作成できます。lib /def:mlang.def /machine:x64
呼び出し側のconvstr2.cppのプロトタイプ宣言
extern "C" HRESULT __stdcall ConvertINetMultiByteToUnicode(LPDWORD , DWORD , LPCSTR , LPINT , LPWSTR , LPINT );
呼び出し側のmlang.objファイルの呼び出し名
ConvertINetMultiByteToUnicode__stdcallが無視されるので、関数名と同一。
呼び出されるmalng.dll内のエクスポート名
ConvertINetMultiByteToUnicode
リンク時に指定するmlang.libファイルのエクスポート名
ConvertINetMultiByteToUnicode
注意点
なお、LIBファイルを作成及び呼び出し側のVisual C++は同じバージョンでしかもビット数は同じでなければなりません。テスト環境
コンパイラ
Visual C++ 2008 Standard 32/64bitVisual C++ 2013 Express 32/64bit
プロジェクトの作成
Win32プロジェクト Windowsコンソロールアプリケーション実行環境
Windows 8.1 Enterprise 64bitWindows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit
DLLファイルからLIBファイルを作成する方法
LIBファイルを作成するには、最初にDEFファイルを作成する必要があります。
DEFファイルは、DLLのうち公開する関数名の一覧を記述したファイルです。
dumpbin.exeはVisual C++をインストールしたフォルダーの中に存在します。単純にこのファイルをコピーしてもだめで、実行に各種のDLLが必要です。
PATHにdumpbin.exeへのパスを記述するか、スタートメニューのVisual C++のコマンドプロンプトを実行する必要があります。
ここではVisual C++のコマンドプロンプトを使用します。
DEFファイルの作成もとのDLLがD:¥mlang.dll、作成するDEFファイルの名前がD:¥mlang.defの場合の DEFファイルの作成方法は以下の通りです。
スタートメニューからVisual C++のコマンドプロンプトを実行する。
コマンドプロンプトでカレントドライブをD:に移動する。
カレントフォルダーがD:¥でなければCDコマンドによりD:¥にする。
dumpbin /exports D:¥mlang.dllを実行して、関数名が表示されるか確認する。
関数の一覧は、例えば以下のように出力されます。
d:¥mlang.txtを開くとDLL内の全部の関数名が一覧されているのでLIBファイルに含めたいファイル名のみを取り出し、以下のように編集してd:¥mlang.defファイルとして保存する。
ここではConvertINetMultiByteToUnicodeだけ抽出して以下のように記述します。
以上でdefファイルの作成ができます。
コマンドプロンプトでの入力例を以下に示します。
以下にビルド結果を示します。
Visual C++ 2013でのConvertINetMultiByteToUnicodeにかかわる定義を以下に示します。
winnt.h
extern "C"を使用した場合、関数名の修飾はないはずですが、_stdcallを宣言したことにより末尾に関数名の修飾が発生しています。
末尾の@24は引数で24バイト使用しているという意味です。
_stdcallはWindowsのAPIで使用されており、何らかの手段で末尾の@24有りでDLL内の末尾@24なしのConvertINetMultiByteToUnicode関数が呼び出せるのでうまく処理されていることがわかります。
ちなみにDEFファイルでConvertINetMultiByteToUnicode@24=ConvertINetMultiByteToUnicodeのように記述すると、呼び出し名がConvertINetMultiByteToUnicode@24がConvertINetMultiByteToUnicodeに変換されるので、 リンクは正常にできますが、実行時に関数を見つけることができない旨のエラーが発生します。
これはLIBファイル側の関数名のマッチング方法が指定されており、指定方法によっては先頭や末尾の文字列を無視することができるからです。
関数名のマッチングルールは以下の3つがあります。
オブジェクトファイルで使う名前と等しい。 IMPORT_OBJECT_NAME
オブジェクトファイルで使う名前から先頭を取ったもの。IMPORT_OBJECT_NAME_NO_PREFIX
オブジェクトファイルで使う名前から先頭を取り、 さらに @ 以降の末尾も無視したもの。IMPORT_OBJECT_NAME_UNDECORATE
したがって、DLL内のAPIを呼び出すLIBファイルを作成するためにはIMPORT_OBJECT_NAME_UNDECORATEでなければなりません。
APIを正常に呼び出せるLIBファイルを作成するには、コンパイラを使ってLIBファイルを作成します。
以下のようにダミーの関数を作成します。引数は24バイトになるように適当に作成すればよいです。 mlang.cpp
OBJファイルの呼び出し名とLIBファイルのエクスポート名が一致していることがわかります。
DLLのエクスポート名が、ConvertINetMultiByteToUnicodeですので、_ConvertINetMultiByteToUnicode@24の赤の部分が無視されてうまく呼び出せることがわかります。
DEFファイルは、DLLのうち公開する関数名の一覧を記述したファイルです。
DLLファイルからDEFファイルを作成する標準的な方法
LIBファイルのエクスポートしている関数名の一覧を取得するためにdumpbin.exeを使用します。dumpbin.exeはVisual C++をインストールしたフォルダーの中に存在します。単純にこのファイルをコピーしてもだめで、実行に各種のDLLが必要です。
PATHにdumpbin.exeへのパスを記述するか、スタートメニューのVisual C++のコマンドプロンプトを実行する必要があります。
ここではVisual C++のコマンドプロンプトを使用します。
DEFファイルの作成もとのDLLがD:¥mlang.dll、作成するDEFファイルの名前がD:¥mlang.defの場合の DEFファイルの作成方法は以下の通りです。
スタートメニューからVisual C++のコマンドプロンプトを実行する。
コマンドプロンプトでカレントドライブをD:に移動する。
カレントフォルダーがD:¥でなければCDコマンドによりD:¥にする。
dumpbin /exports D:¥mlang.dllを実行して、関数名が表示されるか確認する。
関数の一覧は、例えば以下のように出力されます。
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file mlang.dll
File Type: DLL
Section contains the following exports for MLANG.dll
00000000 characteristics
4A5BC5CE time date stamp Tue Jul 14 08:39:58 2009
0.00 version
110 ordinal base
14 number of functions
12 number of names
ordinal hint RVA name
113 0 000131A2 ConvertINetMultiByteToUnicode
114 1 0000FAA2 ConvertINetReset
111 2 00013176 ConvertINetString
112 3 0000414B ConvertINetUnicodeToMultiByte
115 4 00001D86 DllCanUnloadNow
116 5 00002071 DllGetClassObject
117 6 0001AD5C GetGlobalFontLinkObject
110 7 0000A613 IsConvertINetStringAvailable
120 8 0001AE39 LcidToRfc1766A
121 9 000038EF LcidToRfc1766W
122 A 0001ADCF Rfc1766ToLcidA
123 B 00003EC4 Rfc1766ToLcidW
Summary
5000 .data
2000 .reloc
8000 .rsrc
1E000 .text
正常に表示されれば、出力をリダイレクトしてd:¥mlang.txtファイルに保存する。d:¥mlang.txtを開くとDLL内の全部の関数名が一覧されているのでLIBファイルに含めたいファイル名のみを取り出し、以下のように編集してd:¥mlang.defファイルとして保存する。
ここではConvertINetMultiByteToUnicodeだけ抽出して以下のように記述します。
LIBRARY mlang EXPORTS ConvertINetMultiByteToUnicode大量に関数名を使用したい場合、矩形で選択できるテキストエディタを使用するか、Excelで区切り文字を指定して読み込む方法があります。
以上でdefファイルの作成ができます。
コマンドプロンプトでの入力例を以下に示します。
C:¥Program Files (x86)¥Microsoft Visual Studio 12.0¥VC>d:
D:¥>dumpbin /exports mlang.dll
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file mlang.dll
File Type: DLL
Section contains the following exports for MLANG.dll
00000000 characteristics
4A5BC5CE time date stamp Tue Jul 14 08:39:58 2009
0.00 version
110 ordinal base
14 number of functions
12 number of names
ordinal hint RVA name
113 0 000131A2 ConvertINetMultiByteToUnicode
114 1 0000FAA2 ConvertINetReset
111 2 00013176 ConvertINetString
112 3 0000414B ConvertINetUnicodeToMultiByte
115 4 00001D86 DllCanUnloadNow
116 5 00002071 DllGetClassObject
117 6 0001AD5C GetGlobalFontLinkObject
110 7 0000A613 IsConvertINetStringAvailable
120 8 0001AE39 LcidToRfc1766A
121 9 000038EF LcidToRfc1766W
122 A 0001ADCF Rfc1766ToLcidA
123 B 00003EC4 Rfc1766ToLcidW
Summary
5000 .data
2000 .reloc
8000 .rsrc
1E000 .text
D:¥>dumpbin /exports mlang.dll > mlang.txt
LIB.EXEでdefファイルのみでLIBファイルを作成した場合
defファイルのみから以下のようにコマンド入力するとLIBファイルが作成できます。lib /def:mlang.defmalng.hの定義を使用した場合の呼び出し名はビルド結果より_ConvertINetMultiByteToUnicode@24となっている。
以下にビルド結果を示します。
convstr.obj : error LNK2001: 外部シンボル "_ConvertINetMultiByteToUnicode@24" は未解決です。
1>D:¥convstr¥Release¥convstr.exe : fatal error LNK1120: 1 件の未解決の外部参照
LIBファイルのエクスポート名を確認すると以下のようになっている。
D:¥>dumpbin /exports
ang.lib
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file mlang.lib
File Type: LIBRARY
Exports
ordinal name
_ConvertINetMultiByteToUnicode
_ConvertINetReset
_ConvertINetString
_ConvertINetUnicodeToMultiByte
_DllCanUnloadNow
_DllGetClassObject
_GetGlobalFontLinkObject
_IsConvertINetStringAvailable
_LcidToRfc1766A
_LcidToRfc1766W
_Rfc1766ToLcidA
_Rfc1766ToLcidW
Summary
BD .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
A .idata$6
以上の通り、OBJファイルの関数名_ConvertINetMultiByteToUnicode@24とLIBファイルの関数名_ConvertINetMultiByteToUnicodeとは末尾@24の有無により一致しないため失敗です。Visual C++ 2013でのConvertINetMultiByteToUnicodeにかかわる定義を以下に示します。
winnt.h
#define EXTERN_C extern "C"
typedef _Return_type_success_(return >= 0) long HRESULT;
#define STDAPICALLTYPE __stdcall
#define STDAPI EXTERN_C HRESULT STDAPICALLTYPEmlang.h
STDAPI ConvertINetMultiByteToUnicode(_Inout_opt_ LPDWORD lpdwMode, _In_ DWORD dwEncoding, _In_opt_ LPCSTR lpSrcStr, _Inout_opt_ LPINT lpnMultiCharCount, _Out_writes_opt_(*lpnWideCharCount) LPWSTR lpDstStr, _Inout_opt_ LPINT lpnWideCharCount); extern "C"宣伝されているため関数名の修飾なし、__stdcallなので呼び出された側で引数で使用したスタックエリアを解放することを示します。extern "C"を使用した場合、関数名の修飾はないはずですが、_stdcallを宣言したことにより末尾に関数名の修飾が発生しています。
末尾の@24は引数で24バイト使用しているという意味です。
_stdcallはWindowsのAPIで使用されており、何らかの手段で末尾の@24有りでDLL内の末尾@24なしのConvertINetMultiByteToUnicode関数が呼び出せるのでうまく処理されていることがわかります。
ちなみにDEFファイルでConvertINetMultiByteToUnicode@24=ConvertINetMultiByteToUnicodeのように記述すると、呼び出し名がConvertINetMultiByteToUnicode@24がConvertINetMultiByteToUnicodeに変換されるので、 リンクは正常にできますが、実行時に関数を見つけることができない旨のエラーが発生します。
これはLIBファイル側の関数名のマッチング方法が指定されており、指定方法によっては先頭や末尾の文字列を無視することができるからです。
関数名のマッチングルールは以下の3つがあります。
オブジェクトファイルで使う名前と等しい。 IMPORT_OBJECT_NAME
オブジェクトファイルで使う名前から先頭を取ったもの。IMPORT_OBJECT_NAME_NO_PREFIX
オブジェクトファイルで使う名前から先頭を取り、 さらに @ 以降の末尾も無視したもの。IMPORT_OBJECT_NAME_UNDECORATE
したがって、DLL内のAPIを呼び出すLIBファイルを作成するためにはIMPORT_OBJECT_NAME_UNDECORATEでなければなりません。
APIを正常に呼び出せるLIBファイルを作成するには、コンパイラを使ってLIBファイルを作成します。
以下のようにダミーの関数を作成します。引数は24バイトになるように適当に作成すればよいです。 mlang.cpp
extern "C" {
void __stdcall ConvertINetMultiByteToUnicode(int,int,int,int,int,int){
}
}
以下のようにコマンドラインでビルドすると希望のLIBファイルmlang.libが作成されます。
cl /LD mlang.cpp /link /NODEFAULTLIB /noentry /def:mlang.defこのLIBファイルを指定すると以下のコードの様に、ConvertINetMultiByteToUnicode APIを使うことができます。
// EUC文字列をUNICODEに変換するサンプル
// 静的リンクでConvertINetMultiByteToUnicode APIを呼び出す
// Visual C++ 2008/2013 Unicode
#include <windows.h>
#include <stdio.h>
#include <mlang.h>
#include <tchar.h>
#include <locale.h>
#pragma comment(lib,"mlang.lib")
void _tmain(int argc,TCHAR** argv){
// 雀の往来 9byte
unsigned char euc[] = { 0xbf, 0xfd, 0xa4, 0xce, 0xb1, 0xfd, 0xcd, 0xe8, 0 };
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
WCHAR utf16[1024];
DWORD mode;
int len = sizeof(utf16) / sizeof(WCHAR);
// EUCをUNICODEに変換
ConvertINetMultiByteToUnicode(&mode, 51932, (char*)euc, 0, utf16, &len);
_putts(utf16);
}
作成されたLIBファイルのエクスポート名を確認すると以下の通りです。OBJファイルの呼び出し名とLIBファイルのエクスポート名が一致していることがわかります。
DLLのエクスポート名が、ConvertINetMultiByteToUnicodeですので、_ConvertINetMultiByteToUnicode@24の赤の部分が無視されてうまく呼び出せることがわかります。
D:¥>dumpbin /exports mlang.lib
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file mlang.lib
File Type: LIBRARY
Exports
ordinal name
_ConvertINetMultiByteToUnicode@24
Summary
BD .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
A .idata$6
プログラムソースの概要
とライブラリファイルをリンクして作成されたconv2str.exe ConvertINetMultiByteToUnicode APIをサポートするライブラリファイルmlang.libを作成するためのmlang.defとmlang.cpp、 呼び出しテストを行うconvstr2.cppで構成されています。
戻り値および引数の型は適当ですが引数の合計バイト数はAPIと一致させる必要があります。
convstr2では、ConvertINetMultiByteToUnicode関数しか使用していませんがついでですので、ConvertINetUnicodeToMultiByteもライブラリ化します。
_tsetlocale関数によりUNICODEでの標準エラー出力を正常に行えるように設定します。
EUC文字列には、他の文字コードで解釈すると必ず解読不可能な文字列になるため使われる雀の往来を使用しています。
ConvertINetMultiByteToUnicode APIによりEUC文字列をUNICODEに変換します。
_putts関数によりUNICODE文字列を標準出力に出力します。
mlang.def
ライブラリファイルのエクスポート名を指定しています。mlang.cpp
ライブラリファイルを作成するためのダミーのソースファイルです。戻り値および引数の型は適当ですが引数の合計バイト数はAPIと一致させる必要があります。
convstr2では、ConvertINetMultiByteToUnicode関数しか使用していませんがついでですので、ConvertINetUnicodeToMultiByteもライブラリ化します。
convstr2.cpp
このソースはUNICODE専用です。_tsetlocale関数によりUNICODEでの標準エラー出力を正常に行えるように設定します。
EUC文字列には、他の文字コードで解釈すると必ず解読不可能な文字列になるため使われる雀の往来を使用しています。
ConvertINetMultiByteToUnicode APIによりEUC文字列をUNICODEに変換します。
_putts関数によりUNICODE文字列を標準出力に出力します。
プログラムソース
mlang.def
LIBRARY "mlang.dll"
EXPORTS
ConvertINetMultiByteToUnicode
ConvertINetUnicodeToMultiByte
mlang.cpp
extern "C" {
void __stdcall ConvertINetMultiByteToUnicode(int,int,int,int,int,int){
}
void __stdcall ConvertINetUnicodeToMultiByte(int,int,int,int,int,int){
}
}
convstr2.cpp
// EUC文字列をUNICODEに変換するサンプル
// 静的リンクでConvertINetMultiByteToUnicode APIを呼び出す
// Visual C++ 2008/2013 Unicode
#include <windows.h>
#include <stdio.h>
#include <mlang.h>
#include <tchar.h>
#include <locale.h>
#ifdef _WIN64
#pragma comment(lib,"mlang64.lib")
#else
#pragma comment(lib,"mlang.lib")
#endif
void _tmain(int argc,TCHAR** argv){
// 雀の往来 9byte
unsigned char euc[] = { 0xbf, 0xfd, 0xa4, 0xce, 0xb1, 0xfd, 0xcd, 0xe8, 0 };
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
WCHAR utf16[1024];
DWORD mode;
int len = sizeof(utf16) / sizeof(WCHAR);
// EUCをUNICODEに変換
ConvertINetMultiByteToUnicode(&mode, 51932, (char*)euc, 0, utf16, &len);
utf16[len] = 0;
_putts(utf16);
}
ソースファイルのダウンロード
ダウンロード mlang.zip(103kByte)
ZIPファイルにはVisual C++ 2013用の以下のファイルが含まれています。
ZIPファイルにはVisual C++ 2013用の以下のファイルが含まれています。
vc2013
├─convstr2
│ convstr2.cpp C++ソースファイル
│ convstr2.exe convstr2.cppとmlang.libから成された32bit実行ファイル
│ convstr2_64.exe convstr2.cppとmlang64.libから作成された64bit実行ファイル
│
└─mlang
mlang.cpp
mlang.def
mlang.lib malng.cppとmlang.defから作成された32bitライブラリファイル
mlang64.lib mlang.defから作成された64bitライブラリファイル
Copyright (C) 2012 山本ワールド All Rights Reserved.