在 Windows 程式開發中,有時需要在程式中直接呼叫系統的「運行」對話框(即 Win+R 呼叫的對話框)。雖然這看似是一個常見需求,但微軟並未在 MSDN 中公開相關的 API 函數。經過研究發現,這項功能其實藏在 shell32.dll 中一個未導出名稱的函數 RunFileDlg 裡,只能透過其導出序號(Ordinal)來取得位址並調用。
初期嘗試使用 GetProcAddress(hLib, "RunFileDlg") 總是返回 NULL。經過 W32dsm 等工具檢視 shell32.dll 的導出表,確認該 DLL 確實不存在名為 "RunFileDlg" 的導出函數。進一步分析發現,系統保留的內部函數通常不導出名稱,只保留導出序號。對於 shell32.dll,RunFileDlg 的導出序號為 61。
必須透過序號來獲取函數指標:
RunFileDlg = (pRunFileDlg)GetProcAddress(hLib, (char *)61);
其中函數原型定義為:
typedef void (__stdcall *pRunFileDlg)(HWND, HICON, LPCTSTR, LPCTSTR, LPCTSTR, UINT);
////////////////////////////////////////////////
// 呼叫系統「運行」對話框
CString pszDllName = _T("shell32.dll");
HINSTANCE hLib = ::LoadLibrary(pszDllName);
if (hLib == NULL) return;
typedef void (__stdcall *pRunFileDlg)(HWND, HICON, LPCTSTR, LPCTSTR, LPCTSTR, UINT);
pRunFileDlg RunFileDlg = (pRunFileDlg)GetProcAddress(hLib, (char *)61); // 序號 61
if (RunFileDlg != NULL)
{
// 參數準備:標題與提示文字需轉為寬字元
CString strTitle = _T("自訂運行標題");
CString strPrompt = _T("請輸入程式路徑或命令");
LPWSTR wTitle = new WCHAR[100];
LPWSTR wPrompt = new WCHAR[100];
memset(wTitle, 0, sizeof(WCHAR)*100);
memset(wPrompt, 0, sizeof(WCHAR)*100);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strTitle, strTitle.GetLength(), wTitle, 100);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strPrompt, strPrompt.GetLength(), wPrompt, 100);
// 調用對話框
// 參數:父視窗、圖示、目錄、標題、提示、旗標(0x02 等)
RunFileDlg(this->GetSafeHwnd(), NULL, NULL, (LPCTSTR)wTitle, (LPCTSTR)wPrompt, 0x02);
delete[] wTitle;
delete[] wPrompt;
}
::FreeLibrary(hLib);
LoadLibrary 載入 shell32.dll。GetProcAddress(hLib, (char*)61) 以序號 61 取得 RunFileDlg 位址。此技巧同樣適用於其他系統保留的內部函數,掌握「序號調用」是突破未公開 API 限制的關鍵。