本工具結合 全域鍵盤鉤子 DLL (共享記憶體段) 與 GDI 螢幕截取,使用者自訂熱鍵 (如 Ctrl+F9) 即可在任何應用程式中將全螢幕內容存為 BMP 檔。完整原始碼包含 DLL 端鉤子管理、主程式熱鍵註冊、訊息回應、螢幕 DC 複製、BMP 檔案寫入。
#pragma data_seg("SHARED_DATA")
HHOOK g_hHook = NULL;
UINT g_nHookCount = 0;
UCHAR g_HotKeyVK[MAX_HOTKEYS] = {0};
UCHAR g_HotKeyMod[MAX_HOTKEYS] = {0}; // MOD_CONTROL=4, MOD_ALT=2, MOD_SHIFT=1
HWND g_hTargetWnd[MAX_HOTKEYS] = {0};
int g_nKeyCount = 0;
UCHAR g_CurrentMod = 0; // 即時修飾鍵狀態
#pragma data_seg()
#pragma comment(linker, "/section:SHARED_DATA,RWS")
所有變數必須初始化,否則連結器會放入一般資料段導致共享失效。
BOOL __stdcall AddHotkey(HWND hWnd, UCHAR vk, UCHAR mod) {
for (int i=0; i
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode != HC_ACTION) return CallNextHookEx(g_hHook, nCode, wParam, lParam);
BOOL bKeyUp = (lParam & 0xC0000000) == 0xC0000000; // bit 30-31 = 11 表示釋放
if (bKeyUp) {
switch (wParam) {
case VK_CONTROL: g_CurrentMod &= ~4; break;
case VK_MENU: g_CurrentMod &= ~2; break;
case VK_SHIFT: g_CurrentMod &= ~1; break;
}
} else { // Key Down
switch (wParam) {
case VK_CONTROL: g_CurrentMod |= 4; break;
case VK_MENU: g_CurrentMod |= 2; break;
case VK_SHIFT: g_CurrentMod |= 1; break;
}
}
// 比對已註冊熱鍵
for (int i=0; i
typedef BOOL (__stdcall *PFN_ADD)(HWND,UCHAR,UCHAR);
typedef BOOL (__stdcall *PFN_DEL)(HWND,UCHAR,UCHAR);
HMODULE hHookDll = LoadLibrary(_T("HotkeyHook.dll"));
PFN_ADD pAdd = (PFN_ADD)GetProcAddress(hHookDll, "AddHotkey");
PFN_DEL pDel = (PFN_DEL)GetProcAddress(hHookDll, "DeleteHotkey");
// UI 讓使用者選擇修飾鍵 (Checkbox) + 功能鍵 (ComboBox)
// 按下「設定」呼叫 pAdd(m_hWnd, vk, mod);
LRESULT CCapDlg::WindowProc(UINT msg, WPARAM wp, LPARAM lp) {
if (msg == WM_HOTKEY && lp == WM_KEYDOWN) {
SaveBmp();
return 0;
}
return CDialog::WindowProc(msg, wp, lp);
}
void CCapDlg::SaveBmp() {
CDC dc; dc.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
CBitmap bmp; bmp.CreateCompatibleBitmap(&dc, w, h);
CDC memDC; memDC.CreateCompatibleDC(&dc);
CBitmap* pOld = memDC.SelectObject(&bmp);
memDC.BitBlt(0,0,w,h, &dc,0,0, SRCCOPY);
memDC.SelectObject(pOld);
BITMAP bm; bmp.GetBitmap(&bm);
DWORD dwSize = bm.bmWidthBytes * bm.bmHeight;
LPSTR pBits = (LPSTR)GlobalAllocPtr(GPTR, dwSize);
BITMAPINFOHEADER bih = {0};
bih.biSize = sizeof(bih);
bih.biWidth = bm.bmWidth;
bih.biHeight = bm.bmHeight;
bih.biPlanes = 1;
bih.biBitCount = bm.bmBitsPixel;
bih.biCompression = BI_RGB;
bih.biSizeImage = dwSize;
GetDIBits(dc.m_hDC, bmp, 0, h, pBits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
// 寫入 BMP 檔案
static int nCount = 0;
CString path; path.Format(_T("%spict%04d.bmp"), m_strSavePath, nCount++);
CFile file; file.Open(path, CFile::modeCreate|CFile::modeWrite);
BITMAPFILEHEADER bfh = {0};
bfh.bfType = 0x4D42; // 'BM'
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + dwSize;
file.WriteHuge(&bfh, sizeof(bfh));
file.WriteHuge(&bih, sizeof(bih));
file.WriteHuge(pBits, dwSize);
file.Close();
GlobalFreePtr(pBits);
UpdateStatus(nCount);
}
HotkeyHook.dll → 複製至 C:\Windows\System32 (或應用程式同目錄)。ScreenCap.exe。pict0000.bmp、pict0001.bmp...。WH_KEYBOARD_LL (低階鉤子),無需 DLL、不受 Session 0 隔離影響。SM_CXSCREEN/CYSCREEN 僅回傳主螢幕尺寸,需改用 EnumDisplayMonitors 計算虛擬螢幕範圍。