Windows 無單一「預設瀏覽器」註冊表鍵值。可靠做法:查詢 .htm 檔案關聯:
HKEY_CLASSES_ROOT\.htm → 預設值 htmlfileHKEY_CLASSES_ROOT\htmlfile\shell\open\command → 預設值含執行檔路徑範例值:
// IE
"\"C:\PROGRA~1\INTERN~1\iexplore.exe\" -nohome"
// Netscape
"\"C:\PROGRA~1\NETSCAPE\netscape.exe\""
解析字串取得路徑後,配合 ShellExecute 啟動即可。
WNDCLASS wc = {0};
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 或自訂 .cur 資源
RegisterClass(&wc);
MFC 預設為主視窗註冊箭頭游標。
訊息流向:滑鼠移入子視窗 → 子視窗 WM_SETCURSOR → 若回傳 FALSE → 父視窗 WM_SETCURSOR → 若仍 FALSE → 使用類別全域游標。
按鈕類別覆寫範例:
BOOL CMyButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg) {
::SetCursor(m_hMyCursor); // m_hMyCursor 為載入的 HCURSOR
return TRUE; // 停止向上傳播
}
對話框統一處理範例(不想新增按鈕類別時):
BOOL CMyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg) {
if (pWnd->GetDlgCtrlID() == IDC_MY_BUTTON) {
::SetCursor(LoadCursor(NULL, IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, msg);
}
關鍵點:必須回傳 TRUE 表示已處理,否則系統會繼續預設流程。
靜態庫連結進 EXE 時,資源 ID 共享同一命名空間,極易衝突。四種解決策略:
// libres.h
#ifndef LIBBASEID
#define LIBBASEID 2000
#endif
#define IDR_FOO (LIBBASEID + 1)
#define IDR_BAR (LIBBASEID + 2)
// 主程式 .rc
#define LIBBASEID 3000 // 衝突時改這裡
#include "libres.h"
#include "libres.rc"
缺點:需重新編譯庫(若庫直接使用 IDR_FOO)。
// libres.h
#ifdef RC_INVOKED
#ifndef LibBaseId
#define LibBaseId 2000
#endif
#else
extern UINT LibBaseId;
#endif
#define IDR_FOO (LibBaseId + 1)
#define IDR_BAR (LibBaseId + 2)
// 庫主模組 .cpp
UINT LibBaseId = 2000;
// 主程式啟動碼
LibBaseId = 3000; // 衝突時修改
RC_INVOKED 確保資源編譯器只看到宏定義,C++ 編譯器看到 extern 變數。
#define IDR_FOO "MyApp_IDR_FOO"
#define IDR_BAR "MyApp_IDR_BAR"
適用於對話框等低頻資源;高頻資源(圖示、位圖)仍建議整數 ID。
// 庫內部
BOOL SomeLibFn(UINT nDlgID) { DialogBox(hInst, MAKEINTRESOURCE(nDlgID), ...); }
// 主程式呼叫
SomeLibFn(IDR_FOO); // 主程式定義 IDR_FOO
庫不直接引用資源 ID,主程式完全掌控編號。
OnSetCursor 最符合 OO 原則