概要

DLLファイルのエクスポートされている関数の一覧を表示し、保存を選択するとテキストファイルに関数の一覧が保存されます。

32bitの実行ファイルでは32bitのDLL、64bitの実行ファイルでは64bitのDLLを扱うことができます。
例えば64bitの実行ファイルで32bitのDLLの一覧を得ることはできません。

メニューの操作

DLLファイルを開く(O)

ファイル(F)・DLLファイルを開く(O)を選択するとDLL名を入力できるダイアログボックスが表示されます。
直接DLL名を入力するか、参照ボタンを押してDLLファイルを選択することができます。
OK(O)をクリックすると関数の一覧が表示されます。

一覧をファイルへ保存(S)

ファイル(F)・一覧をファイルへ保存(S)を選択すると ファイル名の選択後、関数の一覧が保存されます。
例えばmlang.dllの一覧を保存すると以下の内容のファイルが作成されます。
IsConvertINetStringAvailable
ConvertINetString
ConvertINetUnicodeToMultiByte
ConvertINetMultiByteToUnicode
ConvertINetReset
DllCanUnloadNow
DllGetClassObject
GetGlobalFontLinkObject
LcidToRfc1766A
LcidToRfc1766W
Rfc1766ToLcidA
Rfc1766ToLcidW

テスト環境

コンパイラ

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関数が呼び出されます。 ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。 Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。 メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。 メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。 詳細は以下を参照してください。
メッセージループについて
本プログラムは、メニューを使用していますので、ウィンドウクラスを定義使用する際に使用するWNDCLASS構造体のメンバーlpszMenuNameにリソースファイルのメニュー名を設定します。

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

RegisterClass APIにより登録することによりWindowsから呼び出されます。
第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。
case WM_CREATE:
ウィンドウの初期化時に呼び出されます。
CreateWindowEx APIでこのウィンドウの子ウィンドウであるリストビューを作成します。
リストビューの位置、大きさは0で作成し、後でウィンドウサイズを修正します。
リストビューのハンドルは後で他の関数で使用するのでグローバル変数に保存しておきます。
ListView_InsertColumnによりヘッダー(列の見出し)を作成します。
set_Lvs関数によりリストビューのアイテムを追加します。
この段階ではDLLが読みだされていないので、ファイルメニューの一覧をファイルへ保存の表示を無効化する必要があります。
まずGetMenu APIによりメニューの中からファイルメニューのハンドルを取得します。
GetSubMenu APIにより先ほどのハンドルからファイルへ保存のメニューのハンドルを取得します。
取得したメニューハンドルに対してEnableMenuItem APIによりメニューを無効化します。
case WM_COMMAND:
メニューが選択されたとき発生するメニューを処理します。
ウィンドウプロシージャーの第3引数であるWPARAMの下位16bitがメニューのID番号が格納されています。
LOWORDマクロによりWPARAMの下位16bitを取り出し、switchステートメントによりメニューIDごとに処理を振り分けます。
メニューはリソースファイルとヘッダーファイルで定義しています。
case IDM_OPEN:
ファイルメニューのDLLファイルを開くを選択した時の処理を記述しています。
DLL名の入力をするためのダイアログボックスを表示するためにDialogBoxParam APIを呼び出します。第5引数lParamにはファイル名のバッファを指定しています。
DLL名が取得できた場合、エクスポート関数名を保存しているfuncName配列に要素があれば各要素のメモリを解放します。その後配列そのものをクリアします。
次にdllFuncList関数によりエクスポート関数名の一覧を取得します。
コンパイルされた実行ファイルのビット数(32bit/64bit)に応じてタイトルボックスにDLL名とビット数をSetWindowText APIにより設定します。
ファイルメニューの一覧をファイルへ保存の表示を有効にします。
case IDM_SAVE:
ファイルメニューの一覧をファイルへ保存を選択した時の処理を記述しています。
保存ファイル名を取得するコモンダイアログボックスをGetSaveName関数により表示させ、ファイル名を取得します。
ファイルを開き、funcName配列に保存されているファイル名をファイルへ書き込みます。
fclose関数によりファイルを閉じます。
case IDM_EXIT:
ファイルメニューの終了を選択した時の処理を記述しています。
SendMessageを使用して自身のウィンドウにWM_DESTROYメッセージを発生させウィンドウを閉じます。
case WM_SIZE:
ウィンドウのサイズが変更されたときに呼び出されます。
MoveWindows APIを呼び出しリストビューのサイズを変更します。
case WM_DESTROY:
ウィンドウが閉じるときに呼び出されます。
PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。

dllFuncList

DLLファイルをロードし、エクスポートディレクトリより関数の一覧を作成します。
LoadLibrary APIによりDLLファイルを動的にロードします。
ImageDirectoryEntryToData APIによりエクスポートディレクトリを取得します。
シンボル名と序数のそれぞれのテーブルを取得します。
エクスポートテーブルの要素番号と序数テーブルの内容を総当たりで比較し、一致したものがエクスポートされた関数名です。
ロードしたモジュールのハンドルはアドレスそのものなので、これにシンボル名のテーブルを要素番号で取得するANSIで格納された関数名を得ることができます。
UNICODEでコンパイルするときはMultiByteToWideChar APIにより1バイト文字をWCHAR(TCHAR)に変換します。
TCHARで文字列を_tcsdup関数により動的に確保されたメモリに文字列をコピーし、funcName配列の要素に追加します。

set_lvs

ListView_InsertItemマクロにより0列目のアイテムを設定ます。
ListView_SetItemマクロにより1列目にエクスポート関数名を設定します。

DlgProc1

ダイアログボックスプロシージャーです。 必要な時にWindowsから呼び出されます。 第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。 コントロールダイアログボックスに張り付けられているエディットボックス・アップダウンコントロールを操作するには、コントロールのハンドルが必要です。 ダイアログボックスのコントロール(子ウィンドウ)のハンドルを取得するには、 GetDlgItem APIを呼び出し、ダイアログボックスのハンドルとコントロールのID番号よりハンドルを取得します。 自分で処理しないメッセージは、ダイアログボックスプロシージャー終了時にFALSEを渡せば、Windowsが標準的な処理を行ってくれます。
case WM_INITDIALOG:
ダイアログボックスの初期化時に発生するメッセージです。
第5引数lParamにDialogBoxParam APIからバッファのアドレスが渡されますので、スタティック変数bufに保存します。
case WM_COMMAND:
ダイアログボックスの子ウィンドウ(ダイアログボックスに張り付けられているエディットボックスやプッシュボタン等)から発生したメッセージが届きます。 ダイアログボックスプロシージャーの第3引数であるWPARAMの下位16bitがコントロールのID番号、その上位16bitには通知内容が格納されています。 LOWORDマクロによりWPARAMの下位16bitを取り出し、switchステートメントによりコントロールごとに処理を振り分けます。
case IDC_REF_BUTTON:
参照ボタンをクリックしたときに呼び出されます。
読み取りファイル名を入力するコモンダイアログをGetFileName関数経由で呼び出します。
ファイル名が正常に入力されている場合は、エディットボックスに取得したファイル名を設定します。
case IDOK:
OKボタンをクリックすると呼び出されます。
エディットボックスの内容を読み取り変数bufに保存します。
EndDialog APIを呼び出し、ダイアログボックスを終了させます。
case IDCANCEL:
EndDialog APIを呼び出し、ダイアログボックスを終了させます。

GetFileName

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

GetSaveName

GetSaveFileName APIを呼び出し保存するファイル名を選択するコモンダイアログを表示します。
使い方は、GetOpenFileName APIとほぼ同じです。

プログラムソース

dll2list.cpp

//	DLLファイルのエクスポートされている関数の一覧を表示する
//	Visual C++ 2008/2013 32/64bit

#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#include <imagehlp.h>
#include "resource.h"
#include <vector>
#pragma comment(lib,"imagehlp.lib")

typedef TCHAR* TCHARP;

std::vector<TCHARP> funcName;	//	エクスポート関数の一覧
TCHAR* szClassName=TEXT("dll2list");
HINSTANCE hInst;
HWND hList;	//	リストビューのハンドル
TCHAR def_dir[MAX_PATH];	//	コモンダイアログのディフォルトディレクトリ

//	ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

//	ウィンドウプロシージャー
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

//	DLLでエクスポートされている関数の一覧を作成
bool dllFuncList(TCHAR* dllName,std::vector<TCHARP>& func,HWND hWnd=0);

//	リストビューの表示内容を設定
void set_lvs(HWND hList);

// ファイル名を指定するコモンダイアログを表示
BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir = 0);

// ファイル名を指定するコモンダイアログを表示
BOOL GetSaveName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir = 0);

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,TCHAR* lpszCmdLine, int nCmdShow){
    HWND hWnd;
	MSG lpMsg;
	WNDCLASS wc;
	hInst=hInstance;
	if (!hPreInst) {
		wc.style=CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc=WndProc;
		wc.cbClsExtra=0;
		wc.cbWndExtra=0;
		wc.hInstance=hInstance;
		wc.hIcon=NULL;
		wc.hCursor=LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground = (HBRUSH__ *)GetStockObject(LTGRAY_BRUSH);
		wc.lpszMenuName = _TEXT("IDM_MENU");
		wc.lpszClassName=szClassName;
		if (!RegisterClass(&wc))
			return FALSE;
	}
	hWnd = CreateWindow(szClassName,
		TEXT("Dll2List"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		450,
		450,
		NULL,
		NULL,
		hInstance,
		NULL);
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	while (GetMessage(&lpMsg, NULL, 0, 0)) {
		TranslateMessage(&lpMsg);
		DispatchMessage(&lpMsg);
	}
	return int(lpMsg.wParam);
}

//	ウィンドウプロシージャー

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
	TCHAR buf[MAX_PATH];
	TCHAR title[MAX_PATH];
	LV_COLUMN lvcol;
	static HWND hEdit=0;
	static HWND hButton = 0;
	static HMENU hMenu = 0;
	static HMENU hSubMenu = 0;
	switch (msg){
	case WM_CREATE:{
		hList = CreateWindowEx(0,
			WC_LISTVIEW, TEXT(""),
			WS_CHILD | WS_VISIBLE | LVS_REPORT,
			0, 0, 0, 0,
			hWnd,
			(HMENU)ID_LISTVIEW,
			hInst,
			NULL);

		lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
		lvcol.fmt = LVCFMT_LEFT;
		lvcol.cx = 32;
		lvcol.pszText = TEXT("No.");
		lvcol.iSubItem = 0;
		ListView_InsertColumn(hList, 0, &lvcol);

		lvcol.fmt = LVCFMT_LEFT;
		lvcol.cx = 256;
		lvcol.pszText = TEXT("関数名");
		lvcol.iSubItem = 1;
		ListView_InsertColumn(hList, 1, &lvcol);

		hMenu=GetMenu(hWnd);
		hSubMenu= GetSubMenu(hMenu, 0);
		EnableMenuItem(hSubMenu, IDM_SAVE, MFS_DISABLED);
		def_dir[0]=_T('\0');
		break;
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDM_OPEN:
			if (DialogBoxParam(::hInst, TEXT("OPEN_DLG"), hWnd, (DLGPROC)DlgProc1, (LPARAM)buf) == TRUE){
				for (unsigned n = 0; n < ::funcName.size(); n++){
					free(::funcName[n]);	//	_tcsdupで確保したメモリを解放
				}
				::funcName.clear();
				if (dllFuncList(buf, ::funcName, hWnd) == false)
					break;
#ifdef _WIN64
				_stprintf_s(title, MAX_PATH, _TEXT("%s 64Bit"), buf);
#else
				_stprintf_s(title, MAX_PATH, _TEXT("%s 32Bit"), buf);
#endif
				SetWindowText(hWnd, title);
				SendMessage(hList, LVM_DELETEALLITEMS, 0, 0);
				set_lvs(hList);
				EnableMenuItem(hSubMenu, IDM_SAVE, MFS_ENABLED);
			}
			break;
		case IDM_SAVE:
			if (GetSaveName(hWnd, buf, MAX_PATH, def_dir)){
				FILE* fp;
				if (_tfopen_s(&fp, buf, _TEXT("w"))){
					MessageBox(hWnd, buf, _TEXT("以下のファイルをオープンすることができませんでした"), MB_OK);
					break;
				}
				for (unsigned n = 0; n<::funcName.size(); n++){
					_ftprintf(fp,_TEXT("%s\n"),::funcName[n]);
				}
				fclose(fp);
			}
			break;
		case IDM_EXIT:
			SendMessage(hWnd, WM_DESTROY, 0, 0);
			break;
		}
		break;
	case WM_SIZE:
		MoveWindow(hList, 0, 0, LOWORD(lParam), HIWORD(lParam) , TRUE);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break; 

	default:
		return(DefWindowProc(hWnd, msg, wParam, lParam));
	}
	return (0L);
}

//	DLLでエクスポートされている関数の一覧を作成

bool dllFuncList(TCHAR* dllName,std::vector<TCHARP>& func,HWND hWnd){
	unsigned i, j;
	ULONG  nSize;
	DWORD* funcTable;
	WORD*  numTable;
	IMAGE_EXPORT_DIRECTORY* pImage;

	HMODULE hMod = LoadLibrary(dllName);

	if (hMod == NULL) {
		MessageBox(hWnd,_TEXT("DLLをロードすることができませんでした"),_TEXT("エラー") , MB_OK);
		return false;
	}
	//エクスポートディレクトリの取得
	pImage = (IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(
		(PVOID)hMod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &nSize);
	if (pImage == NULL) {
		MessageBox(hWnd,_TEXT("エクスポートディレクトリを取得することができませんでした"),_TEXT("エラー") , MB_OK);
		FreeLibrary(hMod);
		return false;
	}

	int count = 0;
	//	シンボル名
	funcTable = (DWORD*)(pImage->AddressOfNames + (BYTE*)hMod);
	//	序数テーブル
	numTable = (WORD*)(pImage->AddressOfNameOrdinals + (BYTE*)hMod);
	for (i = 0; i < pImage->NumberOfFunctions; i++)	{	//	エクスポートテーブルを検索	
		for (j = 0; j < pImage->NumberOfNames; j++) {	//	シンボルテーブルを検索
			if (numTable[j] != i)	//序数ではなくインデックスであるため無視
				continue;
			char* ansi = (char*)(funcTable[j] + (BYTE*)hMod);
			TCHAR* func_name;
#ifdef	_UNICODE
			WCHAR utf[128];
			MultiByteToWideChar(932, 0, ansi, -1, utf, sizeof(utf) / sizeof(TCHAR));
			func_name = utf;
#else
			func_name = ansi;
#endif
			TCHAR* p = _tcsdup(func_name);
			func.push_back(p);
			++count;
			break;
		}
	}
	FreeLibrary(hMod);
	return true;
}

//	リストビューの表示内容を設定

void set_lvs(HWND hList){
	LV_ITEM item;
	TCHAR buf[16];
	for(unsigned i=0; i< ::funcName.size();i++){

		_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%d"), i + 1);
		item.mask = LVIF_TEXT;

		item.pszText = buf;
		item.iItem = i;
		item.iSubItem = 0;
		ListView_InsertItem(hList, &item);

		item.mask = LVIF_TEXT;
		item.pszText = ::funcName[i]; 
		item.iItem = i;
		item.iSubItem = 1;
		ListView_SetItem(hList, &item);

		item.mask = LVIF_TEXT;
		item.pszText = _TEXT("");
		item.iItem = i;
		item.iSubItem = 2;
		ListView_SetItem(hList, &item);
	}
}

//	ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	static TCHAR* buf = 0;
	switch (msg) {
	case WM_INITDIALOG:
		buf = (TCHAR*)lParam;
		return TRUE;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_REF_BUTTON:
			if (GetFileName(hDlg, buf, MAX_PATH, def_dir)){
				SetWindowText(GetDlgItem(hDlg, IDC_DLL_EDITBOX), buf);
			}
			break;
		case IDOK:
			GetDlgItemText(hDlg, IDC_DLL_EDITBOX, (TCHAR*)buf, MAX_PATH);
			EndDialog(hDlg, TRUE);
			return TRUE;
		case IDCANCEL:
			EndDialog(hDlg, FALSE);
			return FALSE;
		default:
			return FALSE;
		}
	default:
		return FALSE;
	}
	return TRUE;
}

// ファイル名を指定するコモンダイアログを表示

BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir){
	OPENFILENAME o;
	fname[0] = _T('\0');
	ZeroMemory(&o, sizeof(o));
	o.lStructSize = sizeof(o);		//	構造体サイズ
	o.hwndOwner = hWnd;				//	親ウィンドウのハンドル
	o.lpstrInitialDir = initDir;	//	初期フォルダー
	o.lpstrFile = fname;			//	取得したファイル名を保存するバッファ
	o.nMaxFile = sz;				//	取得したファイル名を保存するバッファサイズ
	o.lpstrFilter = _TEXT("DLLファイル(*.DLL)\0*.DLL\0");
	o.lpstrDefExt = _TEXT("DLL");
	o.lpstrTitle = _TEXT("DLLファイルを指定");
	o.nFilterIndex = 1;
	return GetOpenFileName(&o);
}

// ファイル名を指定するコモンダイアログを表示

BOOL GetSaveName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir){
	OPENFILENAME o;
	fname[0] = _T('\0');
	ZeroMemory(&o, sizeof(o));
	o.lStructSize = sizeof(o);		//	構造体サイズ
	o.hwndOwner = hWnd;				//	親ウィンドウのハンドル
	o.lpstrInitialDir = initDir;	//	初期フォルダー
	o.lpstrFile = fname;			//	取得したファイル名を保存するバッファ
	o.nMaxFile = sz;				//	取得したファイル名を保存するバッファサイズ
	o.lpstrFilter = _TEXT("TXTファイル(*.TXT)\0*.TXT\0") _TEXT("全てのファイル(*.*)\0*.*\0");
	o.lpstrDefExt = _TEXT("TXT");
	o.lpstrTitle = _TEXT("出力ファイルを指定");
	o.nFilterIndex = 1;
	return GetSaveFileName(&o);
}

resource.h

#define ID_LISTVIEW 100

#define IDC_REF_BUTTON 200
#define IDC_DLL_EDITBOX	210

#define IDM_OPEN 300
#define IDM_SAVE 310
#define IDM_EXIT 320

resource.rc


#include <windows.h>
#include "resource.h"


IDM_MENU MENU DISCARDABLE
{
	POPUP "ファイル(&F)"
	{
		MENUITEM "DLLを開く(&O)", IDM_OPEN
		MENUITEM "一覧をファイルへ保存く(&S)", IDM_SAVE
		MENUITEM "終了(&X)", IDM_EXIT
	}

}

OPEN_DLG DIALOG DISCARDABLE 0, 0, 309,7+14+14+14+7
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "DLLファイル名入力"
FONT 9,"MS PGothic"
{
	CONTROL "user32.dll", IDC_DLL_EDITBOX, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT, 7, 7, 256, 12
	CONTROL "参照(&R)", IDC_REF_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 270, 7, 32, 14

	CONTROL "OK(&O)", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,97, 35, 54, 14
	CONTROL "キャンセル(&C)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 158, 35, 54, 14
}

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

ダウンロード dll2list.zip(123kByte)

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