概要

DLLファイからライブラリファイルを作成するプログラムです。

対象のDLLはDLL名のエディットボックスに直接入力するか参照ボタンを押してファイルを選択します。
読み込みをクリックすると対象DLLが読み込まれ関数名の一覧がリストビューに表示されます。
関数名のチェックボックスにチェックを入れるとライブライフィルの作成対象になります。
関数名をダブルクリックすると以下のダイアログボックスが表示されます。

32bitのLIBファイルを作成するためには各関数に引数のバイト数が必要となりますのでこのダイアログボックスで入力します。
作成するLIBファイルをどのVisual C++のコンパイラを使用して作成するか下のリストビューのチェックボックスで選択します。
作成ボタンをクリックするとVisual C++を使ってコンパイルします。コンパイル結果は、各タブをクリックすると閲覧できます。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsアプリケーション

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

dll2list.cpp

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。
chk_vc_cmd関数によりスタートメニューからVisual C++のコマンドプロンプトを検索し一覧をVC_CMDクラス配列に保管します。
DialogBox APIによりダイアログボックスを表示します。

DlgProc

ダイアログボックスプロシージャーです。 必要な時にWindowsから呼び出されます。 第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。 コントロールダイアログボックスに張り付けられているエディットボックス・アップダウンコントロールを操作するには、コントロールのハンドルが必要です。 ダイアログボックスのコントロール(子ウィンドウ)のハンドルを取得するには、 GetDlgItem APIを呼び出し、ダイアログボックスのハンドルとコントロールのID番号よりハンドルを取得します。 自分で処理しないメッセージは、ダイアログボックスプロシージャー終了時にFALSEを渡せば、Windowsが標準的な処理を行ってくれます。
case WM_INITDIALOG:
リストビューの拡張スタイルを設定します。
リストビューのヘッダーを設定します。
タブコントロールのタブを設定します。
Visual C++のコマンドプロンプトの一覧をタブコントロールの最初のページのリストビューに表示します。
SendMessageによりWM_SIZEメッセージを作成し、タブコントロールの最初のページの大きさを調整します。
case WM_SIZE:
ダイアログボックスの大きさが変更されたときに呼び出されます。 ダイアログボックスの大きさは、ダイアログボックスのスタイルで変更できないようにしていますが、前項のとおり、子ウィンドウの大きさを調整するために、WM_SIZEメッセージの処理を行います。 リソース側でうまく大きさを調整しても環境が変わるとウィンドウ枠の大きさが変わったりするのでプログラムで処理するのが常道です。 通常は、メインダイアログのクライアント領域一杯にタブコントロールは配置されますが、今回は小さくしています。 通常であれば、タブコントロールのクライアント領域の大きさを取得し、TabCtrl_AdjustRectマクロにより子ウィンドウの大きさを算出し、::hPage配列からウィンドウハンドルを取り出しMoveWindow APIにより大きさを変更します。 今回は、タブコントロールのメインウィンドウ上でのクライアント領域の大きさを求めるためにGetClientRect APIを使用しますが、このAPIは左上座標が0,0で右下座標がウィンドウサイズとなります。 今回のケースでは、タブコントロールの左上のクライアント座標が0,0ではないので、メインダイアログの左上座標(0,0)とタブコントロール上での左上座標(0,0)をスクリーン座標に変換し、その差分をメインダイアログボックス上でのタブコントロールのクライアント領域の左上座標としています。 あとは、通常通りTabCtrl_AdjustRectマクロにより子ウィンドウの大きさを算出し差分を加算しています。
case WM_NOTIFY:
case NM_DBLCLK:
リストビューのアイテムをダブルクリックしたときに発生します。
アイテム番号をDialogBoxParam APIの引数としてダイアログボックスを表示します。
case TCN_SELCHANGE:
タブを左クリックした場合に発生します。 TabCtrl_GetCurSelマクロによりクリックされたタブ番号を取得します。
::hPage配列の各要素に対してShowWindow APIにより表示、非表示を設定します。
case WM_COMMAND:
メニューが選択されたとき発生するメニューを処理します。
ウィンドウプロシージャーの第3引数であるWPARAMの下位16bitにID番号が格納されています。
LOWORDマクロによりWPARAMの下位16bitを取り出し、switchステートメントによりIDごとに処理を振り分けます。
case IDC_DLLREF_BUTTON:
DLL名エディットボックスの横の参照ボタンをクリックすると呼び出されます。
GetFileName関数を呼び出しDLLファイル名を取得します。
case IDC_LIBREF_BUTTON:
LIB保存フォルダエディットボックスの横の参照ボタンをクリックすると呼び出されます。
GetDir関数を呼び出しSHBrowseForFolder APIを用いてフォルダー名を選択するコモンダイアログボックスを表示しフォルダー名を取得します。
case IDC_DLLREAD_BUTTON:
読み込みボタンをクリックすると呼び出されます。
dllFuncListClear関数によりVC_CMDクラス配列をクリアします。
ListView_DeleteAllItemsマクロにより関数名のリストビューをクリアします。
GetWindowText APIによりDLL名エディットボックスからDLL名を取得します。
dllFuncListMake関数により指定したDLL名のDLLをロードしてエクスポート関数の一覧を配列funcNameに保存します。
関数名が取得できた場合は、set_lvs関数により関数名の一覧をリストビューに設定します。
resize()を用いて関数の呼び出し形式を示す配列callTypeと関数の引数のバイト数を示す配列funcArgcの大きさをエクスポート関数の数と同じにします。
リサイズした配列を初期化します。
case IDC_TAB_DEL:
TabAllDelete関数により最初のタブのみ残して残りのタブを全部削除します。
TabSelect関数により最初のタブを表示します。
case IDOK:
作成ボタンをクリックすると呼び出されます。
各プッシュボタンをEnableButton関数により無効状態にします。
ListView_GetCheckStateマクロで選択状態を配列callTypeに設定します。
GetWindowText APIによりDLL名エディットボックスからDLL名を取得します。
GetFullPathName APIによりDLL名からフルパス名を取得します。
拡張子を除外しfnameに保存します。
GetWindowText APIによりLIB保存先を取得しoutBaseDirに保存します。
outBaseDirの末尾に\がある場合は削除します。
outBaseDirとfnameを合成して拡張子を除いたソースファイル名を作成します。
export_src関数によりDEFファイルとCPPファイルを作成します。
TabAllDelete関数により最初のタブのみ残して残りのタブを全部削除します。
Visual C++リストビューを行ごとにListView_GetCheckStateマクロによりチェック状態をチェックしチェックされている場合は、以下の処理を実行します。
LIBファイルはLIB保存フォルダーにチェックされているVisual C++バージョンと対象ビット数で示すフォルダーに保存するのでフォルダー名をoutdir変数に作成します。 たとえば以下の様なフォルダーになります。
LIB保存先 D:\
Visual C++ 2008 x86
DLL名 mlang.dll
上記の条件の場合 D:\2008\x86 が保存先となる。
作成したフォルダー名をCreateDirectory APIにより作成します。
LIBファイルをビルドするためのバッチファイルの名前を作成し、変数batNameに保存します。バッチファイル名は、上記の条件の場合 D:\2008\mlang86.bat となります。
ビルドするためのコマンド文字列を作成しcmd変数に保存します。
コマンド文字列はビルドビット数により以下の様になります。
64bitの場合 lib /machine:x64 /def:defファイル名
32bitの場合 cl /LD C++ソースファイル名 /link /NODEFAULTLIB /noentry /def:defファイル名
ビルドの詳細はDLLファイルからLIBファイルを作成する(_stdcall宣言)を参照してください。
makeBat関数によりビルド用のバッチファイルを作成します。
ビルド結果を表示するページのタブ名はVisual C++コマンドプロンプトのショートカット名ですが、複数のタブを表示するとタブが大きくなるので、コマンドプロンプトという共通名の削除やVisual Studioや.lnkを省略した文字列をmakeShortName関数により作成しタブ名にします。
TabCtrl_InsertItemマクロにより新しいタブを作成します。
新しいタブに対応するビルド結果を表示するエディットボックスをCreateWindowEx APIにより作成します。
SetWindowText APIによりVisual C++ コマンドプロンプトのショートカット名を作成したエディットボックスに表示します。
ShoWindow APIにより直前のタブのページを非表示にします。
TabCtrl_SetCurSel APIにより作成したタブを有効にします。
push_backにより新しく作成したエディットボックスを配列hPageに追加します。
新しくしたエディットボックスの大きさをタブコントロールに合わせるためにダイアログのクライアントサイズをGetClientRect APIにより取得し、SendMessage APIによりダイアログボックスにWM_SIZEメッセージを送信します。
SetWindowTextにより何個目をビルドしているかラベルに表示します。
ShoWindow APIにより現在のタブを表示します。
create_cmd_process関数によりcmd.exeを新しいプロセスとして実行しバッチファイルを実行しその結果をエディットボックスに表示します。
リストビューの各行すべてについて上記の処理が終了したら、一番左のタブをTabSelect関数により有効状態にします。
SetWindowTextによりビルド時間をラベルに表示します。
各プッシュボタンをEnableButton関数により有効状態にしてメッセージ処理を終了します。

makeBat

LIBファイル作成するためのバッチファイルを作成します。
ビルドのためにコンパイラ等を使用しますが、パスが通っていないので任意のフォルダーでは実行できません。
パスの初期化のためにスタートメニューのVisual C++ コマンドプロンプトのショートカット先のバッチファイルを作成したバッチファイルの中のcall文でpath変数を初期化しバッチファイル内でコンパイラ等が使用できるようにします。
pushdコマンドでカレントドライブ・カレントフォルダーをLIBファイルを作成するフォルダーに移動します。
次の行にmakeBat関数呼び出し時に指定されたコマンドを書き込みます。
popdコマンドを実行しカレントドライブ・カレントフォルダーを元に戻します。
作成されるバッチファイルの中身の例を以下に示す。
rem 以下のコマンドは Visual Studio 2008 コマンド プロンプト.lnk を呼び出します
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"   x86
pushd "D:\2008\x86"
cl /LD D:\mlang.cpp /link /NODEFAULTLIB /noentry /def:D:\mlang.def
popd
echo 終了しました

dllFuncListClear

DLLのエクスポート関数名の一覧を保存している配列funcNameは各要素の_tcsdupで確保した動的メモリ上に関数名を保存しているのでfreeで動的メモリを解放します。
配列funcNameと呼び出し形式を保存している配列callTypeと引数のサイズを保存している配列funcArgcのサイズを0にします。

dllFuncListMake

DLLを読み込みエクスポート関数の一覧を配列funcNameに作成します。
詳細は、DLLファイルのエクスポート関数の一覧を表示のdllFuncList関数を参照してください。

set_lvs

ListView_InsertItemマクロによりリストビューの各行の0列目にエクスポート関数名を表示します。

editDlgProc

リストビュー上でダブルクリックしたときに呼び出されるダイアログボックスプロシージャーです。 必要な時にWindowsから呼び出されます。 第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。 コントロールダイアログボックスに張り付けられているエディットボックス等を操作するには、コントロールのハンドルが必要です。 ダイアログボックスのコントロール(子ウィンドウ)のハンドルを取得するには、 GetDlgItem APIを呼び出し、ダイアログボックスのハンドルとコントロールのID番号よりハンドルを取得します。 自分で処理しないメッセージは、ダイアログボックスプロシージャー終了時にFALSEを渡せば、Windowsが標準的な処理を行ってくれます。
case WM_INITDIALOG:
ダイアログボックスの初期化時に発生するメッセージです。
第5引数lParamにDialogBoxParam APIからLPARAMに選択されているリストビューのアイテム番号が渡されるので静的変数nItemに保存しておきます。
ListView_GetItemTextマクロにより親ダイアログボックスのリストビューの0列目のアイテムを取得し、SetWindowText APIによりラベルに表示します。
親ダイアログボックスのリストビューのチェックボックスの状態をListView_GetCheckStateマクロで取得し、本ダイアログボックスのチェックボックスのチャックの状態ををSendMessage APIによりBM_SETCHECKメッセージを発生さえせ親ダイアログボックスとのチェックボックスと同じ状態に設定します。
SetWindowText APIにより配列funcArgcの値を_stprintf_s関数で文字列に変換しラベルに設定します。
case WM_COMMAND:
ダイアログボックスの子ウィンドウ(ダイアログボックスに張り付けられているエディットボックスやプッシュボタン等)から発生したメッセージが届きます。 ダイアログボックスプロシージャーの第3引数であるWPARAMの下位16bitがコントロールのID番号、その上位16bitには通知内容が格納されています。 LOWORDマクロによりWPARAMの下位16bitを取り出し、switchステートメントによりコントロールごとに処理を振り分けます。
case IDOK:
OKボタンをクリックすると呼び出されます。
GetWindowText APIによりエディットボックスより引数のバイト数を示す文字列を取得します。
ListView_SetItemマクロにより親ダイアログボックスのリストビューの1列目にバイト数を設定します。
取得した文字列を_ttoi関数により整数に変換し配列funcArgcに設定します。
SendMessage APIによりチェックボックスにBM_GETCHECKメッセージを送りチェックボックスの状態を取得します。
取得した状態をListView_SetCheckStateマクロにより親ダイアログボックスのリストビューのチェックボックスに反映させます。
合わせて配列callTypeにも反映させます。
EndDialog APIを呼び出し、ダイアログボックスを終了させます。
case IDCANCEL:
EndDialog APIを呼び出し、ダイアログボックスを終了させます。

page0DlgProc

タブコントロールの一番左のタブに連動するダイアログボックスのプロシージャーです。 必要な時にWindowsから呼び出されます。 第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。 コントロールダイアログボックスに張り付けられているエディットボックス等を操作するには、コントロールのハンドルが必要です。 ダイアログボックスのコントロール(子ウィンドウ)のハンドルを取得するには、 GetDlgItem APIを呼び出し、ダイアログボックスのハンドルとコントロールのID番号よりハンドルを取得します。 自分で処理しないメッセージは、ダイアログボックスプロシージャー終了時にFALSEを渡せば、Windowsが標準的な処理を行ってくれます。
case WM_INITDIALOG:
ダイアログボックスの初期化時に発生するメッセージです。
第5引数lParamにDialogBoxParam APIからLPARAMに選択されているリストビューのアイテム番号が渡されるので静的変数nItemに保存しておきます。
case WM_COMMAND:
ダイアログボックスの子ウィンドウ(ダイアログボックスに張り付けられているエディットボックスやプッシュボタン等)から発生したメッセージが届きます。 ダイアログボックスプロシージャーの第3引数であるWPARAMの下位16bitがコントロールのID番号、その上位16bitには通知内容が格納されています。 LOWORDマクロによりWPARAMの下位16bitを取り出し、switchステートメントによりコントロールごとに処理を振り分けます。
case IDOK:
OKボタンをクリックすると呼び出されます。
EndDialog APIを呼び出し、ダイアログボックスを終了させます。
case IDCANCEL:
EndDialog APIを呼び出し、ダイアログボックスを終了させます。

export_src

LIBファイルの作成に必要なdefファイルとcppファイルをカレントフォルダー上に作成します。
作成するファイルの内容は、DLLファイルからLIBファイルを作成する(_stdcall宣言)を参照してください。
mlang.dllでConvertINetUnicodeToMultiByteとConvertINetMultiByteToUnicode関数を選択した場合以下の内容のファイルが作成されます。
mlang.cpp
extern "C" void _stdcall ConvertINetUnicodeToMultiByte(void ){
}

extern "C" void _stdcall ConvertINetMultiByteToUnicode(void ){
}
mlang.def
; d:¥mlang.def 32bit
; d:¥mlang.def 64bit
LIBRARY "d:¥mlang.dll"
EXPORTS
        ConvertINetUnicodeToMultiByte
        ConvertINetMultiByteToUnicode

GetDir

SHBrowseForFolder APIを呼び出しフォルダー名を取得します。
詳細は SHBrowseForFolderによりフォルダー名を取得するプログラム(32/64bit)を参照してください。

GetFileName

GetOpenFileName APIを呼び出しファイルを選択するコモンダイアログを実行します。
GetOpenFileName APIの詳細は、GetOpenFileNameによりファイル名を取得するを参照してください。

create_cmd_process

パイプを作成し、標準出力・標準エラー出力をパイプで接続し、CreateProcess APIで起動したcmd.exeを使って指定コマンドを実行します。
結果はパイプ経由で取り込み、指定したウィンドウに出力します。
create_cmd_process関数の詳細は、パイプにより標準出力をエディットボックスに取り込むのcreate_cmd_process関数を参照してください。

TabSelect

指定したタブを表示状態にし選択状態にします。

TabAllDelete

左から2個目以降のタブを削除します。
タブ数はTabCtrl_GetItemCountマクロで取得します。
2個目以降の各タブに対して以下の処理を実行します。

TabCtrl_DeleteItemマクロで左から2個目のタブを削除します。 勘違いしやすいのが削除するとタブインデックス番号が1個ずつずれますので、タブインデックス番号は1でなければなりません。
DestroyWindow APIでタブと連動するエディットボックスを削除します。

TabCtrl_SetCurSelマクロにより選択タブを一番左にします。
ShowWindow APIによりタブと連動するダイアログボックスの表示を有効にします。
InvalidateRect APIによりタブコントロールの親ウィンドウ(ダイアログボックス)の再描画を要求します。
タブに関連するウィンドウを管理しているhPage配列の最初の1個を残してすべての要素を削除します。
hPageはvectorで実装されているので、削除する始点と終点のイテレータが必要となります。
最初の要素の次の要素はbeginで最初の要素を取得し+1すれば取得できます。
最後の要素はendを使用します。
取得した2つの要素をeraseに渡します。

makeShortName

文字列を検索し一致した場合その部分の文字列を指定文字列で置き換えます。
検索する文字列は、find配列、置き換え後の文字列はchg配列で指定で指定しています。
置き換え例は以下の通りです。
Visual Studio 2008 x64 Win64 コマンド プロンプト.lnkVS2008 x64 Win64
Visual Studio 2008コマンド プロンプト.lnkVS2008
Visual Studio コマンド プロンプト (2010).lnk → VS (2010)
VS2013 x64 Cross Tools コマンド プロンプト.lnk → VS2013 x64 Cross Tools 
VS2013 x86 Native Tools コマンド プロンプト.lnk → VS2013 x86 Native Tools 

EnableButton

参照ボタン、読込ボタン、作成ボタン、キャンセルボタン、ビルド結果削除ボタンをEnableWindow APIにより有効状態および無効状態を切り替えします。

get_vc_cmd.cpp get_vc_cmd.h

スタートメニューからVisual C++ のコマンドプロンプトを検索し配列にショートカット名やリンク先等を取得し配列に保存します。
詳細はスタートメニューからVisual C++のコマンドプロンプトを抽出し指定したコマンドを実行するを参照してください。

ソースファイルと実行ファイルのダウンロード

ダウンロード dll2libs.zip(152kByte)

ZIPファイルに含まれるファイル
dll2libs.cpp
dll2libs.exe    32bitの実行ファイル
dll2libs64.exe  64bitの実行ファイル
get_vc_cmd.h
get_vc_cmd.cpp
resource.h
resource.rc