首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 實作橢圓視窗:非矩形視窗、漸變背景、拖曳移動、右鍵選單、全域滑鼠鉤子完整教學

     編輯:關於VC++

標準 Windows 視窗為矩形,透過 CRgn 設定視窗區域 (Window Region) 可建立任意形狀視窗。本文完整實作一個橢圓視窗,包含:橢圓區域建立、漸變背景色彩動畫、標題文字繪製、滑標拖曳移動、右鍵彈出選單 (置頂/自動隱藏)、全域滑鼠鉤子 (DLL) 攔截事件。

一、建立橢圓視窗區域


class CEllipseDlg : public CDialog {
protected:
  CRgn m_rgnWnd;
  // ...
  BOOL OnInitDialog() {
    CDialog::OnInitDialog();
    CRect rc; GetClientRect(rc);
    // 建立橢圓區域,四邊保留邊距
    m_rgnWnd.CreateEllipticRgn(rc.left+32, rc.top+32, rc.Width()-64, rc.Height()-20);
    SetWindowRgn((HRGN)m_rgnWnd, TRUE); // TRUE = 系統接管區域生命週期
    return TRUE;
  }
};

二、滑鼠拖曳移動視窗


void CEllipseDlg::OnMouseMove(UINT nFlags, CPoint point) {
  static CPoint ptLast;
  ClientToScreen(&point);
  if (nFlags & MK_LBUTTON) {
    SetCursor(LoadCursor(NULL, IDC_CROSS));
    CRect rc; GetWindowRect(rc);
    MoveWindow(rc.left + point.x - ptLast.x,
               rc.top  + point.y - ptLast.y,
               rc.Width(), rc.Height());
  }
  ptLast = point;
}

三、漸變背景色彩動畫

於 CWinApp 衍生類別加入 SetBkColor(),以 SetDialogBkColor 設定漸變筆刷,並由 WM_TIMER (20ms) 觸發重繪:


void CEllipseApp::SetBkColor() {
  static BYTE r=80, g=40, b=30;
  static BOOL fr=TRUE, fg=FALSE, fb=TRUE;
  // R: 10~120 震盪
  if (fr) { if (++r > 120) fr=FALSE; } else { if (--r < 10) fr=TRUE; }
  // G: 20~100 震盪 (步進 2)
  if (fg) { g+=2; if (g>100) fg=FALSE; } else { g-=2; if (g<20) fg=TRUE; }
  // B: 20~80 震盪 (步進 3)
  if (fb) { b+=3; if (b>80) fb=FALSE; } else { b-=3; if (b<20) fb=TRUE; }
  SetDialogBkColor(RGB(r,g,b), RGB(r+100,g+120,140));
}

void CEllipseDlg::OnTimer(UINT nIDEvent) {
  theApp.SetBkColor();
  Invalidate(); // 觸發 WM_PAINT
}

四、斜體彩色文字繪製


void CEllipseDlg::OnPaint() {
  CPaintDC dc(this);
  dc.SetBkMode(TRANSPARENT);
  dc.SetTextAlign(TA_BASELINE | TA_CENTER);

  LOGFONT lf;
  SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
  lf.lfHeight = 60;
  lf.lfEscapement = 100; // 10 度傾斜 (0.1度單位)
  lf.lfItalic = TRUE;
  lf.lfQuality = DRAFT_QUALITY;

  CFont font; font.CreateFontIndirect(&lf);
  CFont* pOld = dc.SelectObject(&font);
  CRect rc; GetClientRect(rc);
  dc.TextOut(rc.Width()/2, rc.Height()/2, _T("enjoy!!!"));
  dc.SelectObject(pOld);
}

五、右鍵選單:置頂 / 自動隱藏 (Z-Order 切換)


// 資源選單 IDR_RIGHT_CLICK
// 置頂
void CEllipseDlg::OnTopmost() {
  CRect rc; GetWindowRect(rc);
  SetWindowPos(&wndTopMost, rc.left, rc.top, rc.Width(), rc.Height(), SWP_SHOWWINDOW);
  GetSubMenu()->EnableMenuItem(ID_TOPMOST, MF_GRAYED);
  GetSubMenu()->EnableMenuItem(ID_ZORDER, MF_ENABLED);
}
// 自動隱藏 (送到底再送到頂,達到「不遮擋其他視窗但可見」效果)
void CEllipseDlg::OnZOrder() {
  CRect rc; GetWindowRect(rc);
  SetWindowPos(&wndBottom, rc.left, rc.top, rc.Width(), rc.Height(), SWP_SHOWWINDOW);
  SetWindowPos(&wndTop, rc.left, rc.top, rc.Width(), rc.Height(), SWP_SHOWWINDOW);
  GetSubMenu()->EnableMenuItem(ID_ZORDER, MF_GRAYED);
  GetSubMenu()->EnableMenuItem(ID_TOPMOST, MF_ENABLED);
}

六、全域滑鼠鉤子 (DLL 專案)

1. DLL 介面 (finder.h)


#ifndef __FINDER_H__
#define __FINDER_H__
#ifdef FINDER_EXPORTS
#define FINDER_API __declspec(dllexport)
#else
#define FINDER_API __declspec(dllimport)
#endif
FINDER_API HHOOK StartHook();
FINDER_API void StopHook(HHOOK hHook);
#endif

2. 鉤子程序 (finder.cpp)


#pragma data_seg("SHARED")
HHOOK g_hHook = NULL;
HWND g_hTargetWnd = NULL; // 接收通知的視窗
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,RWS")

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
  if (nCode == HC_ACTION && g_hTargetWnd) {
    // 將滑鼠事件轉發給目標視窗
    PostMessage(g_hTargetWnd, WM_USER_MOUSE, wParam, lParam);
  }
  return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

FINDER_API HHOOK StartHook() {
  g_hHook = SetWindowsHookEx(WH_MOUSE, MouseProc, AfxGetInstanceHandle(), 0);
  return g_hHook;
}
FINDER_API void StopHook(HHOOK hHook) {
  UnhookWindowsHookEx(hHook);
}

3. 主程式載入 DLL 並設定目標視窗


typedef HHOOK (WINAPI *PFN_STARTHOOK)();
typedef void (WINAPI *PFN_STOPHOOK)(HHOOK);

HMODULE hDll = LoadLibrary(_T("finder.dll"));
PFN_STARTHOOK pStart = (PFN_STARTHOOK)GetProcAddress(hDll, "StartHook");
PFN_STOPHOOK pStop = (PFN_STOPHOOK)GetProcAddress(hDll, "StopHook");

// 設定目標視窗 (需在 DLL 共享資料段中定義 HWND g_hTargetWnd)
// 這裡示範透過額外匯出函式 SetTargetWnd(HWND) 實作
typedef void (WINAPI *PFN_SETTARGET)(HWND);
PFN_SETTARGET pSetTarget = (PFN_SETTARGET)GetProcAddress(hDll, "SetTargetWnd");
pSetTarget(m_hWnd);

m_hHook = pStart(); // 啟動
// 選單項啟用/停用切換...

七、編譯與部署注意

  • DLL 需啟用共享資料段 (/section:SHARED,RWS 或 .def SECTIONS SHARED READ WRITE SHARED) 以跨進程共享 g_hHook、g_hTargetWnd。
  • DLL 必須置於系統路徑 (如 System32) 或應用程式目錄。
  • 全域鉤子需 DLL 為 32/64 位元與目標進程一致。
  • Vista+ 需管理員權限安裝全域鉤子,建議改用 SetWindowsHookEx 搭配 WH_MOUSE_LL (低階鉤子,無需 DLL)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved