⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hook.cpp

📁 C++Builder深度历险配套源码 C++Builder深度历险配套源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include <windows.h>

#include <vcl.h>

#include "dihook.h"

const int MAX_ESSAY_LINE  = 100;                      // 最大短文行数
const int MAX_ESSAY_CHAR  = 100;                      // 最长短文字串
const char* HOOK_EVENT_NAME = "SetWindowsHookEx_Event";

// 全域资料区域, 由所有 DLL instance 共享

typedef struct {
    HHOOK HHOOK; // Hook handle
    HWND MainWnd; // 程式主视窗 handle

    HWND Wnd; // 背景视窗 handle
    WNDPROC OrigWndProc; // 背景视窗原本的视窗函式

    HWND ParentWnd; // 背景视窗的父视窗 handle
    WNDPROC OrigParentWndProc; // 背景视窗的父视窗原本的视窗函式

    // 桌面显示
    bool ShowDesktopWallPaper; // 是否显示桌布

    COLORREF SeperatorColor; // 分隔线颜色
    TRect WorkAreaRect;

    // 短文资讯
    char Essay[MAX_ESSAY_LINE][MAX_ESSAY_CHAR]; // 短文
    int Essay_LineCount; // 行数

    TLogFont Essay_Font; // 字型
    COLORREF Essay_Color; // 颜色
} TGlobalData, *PGlobalData;

  // 动态, 不断变化的画面控制
typedef struct {
  TPoint Position; // 显示位置

  Graphics::TBitmap* DesktopBits; // 桌面背景影像
  Graphics::TBitmap* BackBits; // double-buffering 贴图使用

  AnsiString DisplayString; // 时间字串

  TRect LastRect; // 上次显示区域
  TRect CurRect; // 此次显示区域
} TDynDisplay;

const int WM_MYHOOK            = WM_APP;     // hook 安装及解除後的通知讯息
const int WM_SUBCLASSED        = WM_USER;          // subclass 视窗函式後的通知讯息
const int WM_BEFORE_UNSUBCLASS = WM_USER + 1;      // unsubclass 视窗函式前的通知讯息

const char* GLOBAL_DATA_MAPNAME = "XDESK_HOOK_GLOBAL_DATA"; // 共享 file mapping 名称

const int  TIMER_ID             = 500;                      // 计时器编号

HINSTANCE g_hinstDLL;
PGlobalData g_Data; // 指向共享 TGlobalData 结构的指标
HANDLE MapHandle;     // file mapping handle

TDynDisplay DynDisplay; // 动态画面控制资讯

class TConfigButton : public TButton { // 设定按钮类别
private:
  void __fastcall WMPaint(TWMPaint& Message);
  void __fastcall WMLButtonUp(TWMLButtonUp& Message);
protected:
  virtual void __fastcall CreateParams(Controls::TCreateParams &Params);
public:
/* TWinControl.CreateParented */ inline __fastcall TConfigButton(HWND ParentWindow) :
      TButton(ParentWindow) { }

BEGIN_MESSAGE_MAP
    VCL_MESSAGE_HANDLER(WM_PAINT, TWMPaint, WMPaint);
    VCL_MESSAGE_HANDLER(WM_LBUTTONUP, TWMLButtonUp, WMLButtonUp);
END_MESSAGE_MAP(TButton);
};

TConfigButton* ConfigButton; // 设定按钮

void __fastcall TConfigButton::WMLButtonUp(TWMLButtonUp& Message)
{
  TButton::Dispatch(&Message);

  if (g_Data->MainWnd != 0) { // 显示设定视窗
    if (IsWindowVisible(g_Data->MainWnd))
      SetForegroundWindow(g_Data->MainWnd);
    else
      ShowWindow(g_Data->MainWnd, SW_SHOW);
  }
}

void __fastcall TConfigButton::WMPaint(TWMPaint& Message)
{
  TButton::Dispatch(&Message);

  TRect R;

  // 防止按钮本身盖在桌面图示上方, 若按钮重绘时, 也重绘其上的桌面图示
  R = BoundsRect;
  InvalidateRect(g_Data->Wnd, &R, false);
}

void __fastcall TConfigButton::CreateParams(Controls::TCreateParams &Params)
{
  TButton::CreateParams(Params);

  Params.Style = Params.Style & ~WS_CLIPSIBLINGS & ~WS_BORDER & ~WS_DLGFRAME; // 不需要管兄弟视窗, 照画不误
}

#define MAX(a, b) ((a) > (b) ? (a) : (b))

  // 绘制短文
void DrawEssay(HDC DC)
{
  HFONT OldFont;
  HPEN OldPen;

  int Y, X, MaxLen, LineHeight;
  TTextMetric TextMetric;

  TSize Size;

  SetBkMode(DC, TRANSPARENT);
  SetTextColor(DC, g_Data->Essay_Color);

  OldFont = SelectObject(DC, CreateFontIndirect(&g_Data->Essay_Font));
  OldPen = SelectObject(DC, CreatePen(PS_SOLID, 1, g_Data->SeperatorColor));

  MaxLen = 0;
  for (int i = 0; i < g_Data->Essay_LineCount; i++) {
    GetTextExtentPoint32(DC, g_Data->Essay[i], strlen(g_Data->Essay[i]), &Size);
    if (Size.cx > MaxLen) MaxLen = Size.cx;

    X = MAX((Screen->Width - MaxLen) / 2, 0);
    GetTextMetrics(DC, &TextMetric);
    LineHeight = TextMetric.tmHeight + TextMetric.tmExternalLeading;

    Y = TextMetric.tmHeight;
    for (i = 0; i < g_Data->Essay_LineCount; i++) {
      if (strlen(g_Data->Essay[i]) == 1 && g_Data->Essay[i][0] == '-') {
        MoveToEx(DC, X, Y + LineHeight / 2, NULL);
        LineTo(DC, X + MaxLen, Y + LineHeight / 2);
      } else
        TextOut(DC, X, Y, g_Data->Essay[i], strlen(g_Data->Essay[i]));

      Y += LineHeight;
    }

    DeleteObject(SelectObject(DC, OldFont));
    DeleteObject(SelectObject(DC, OldPen));
  }
}

LRESULT _stdcall WndProc(HWND Window, UINT AMessage, Longint WPARAM , Longint LPARAM)
{
  HDC DC;
  TRect R;

  TSize DisplaySize;

  switch (AMessage) {

    case WM_SETTINGCHANGE: // 系统设定改变
    case WM_SYSCOLORCHANGE: // 系统颜色改变(主要针对桌面颜色)

        SystemParametersInfo(SPI_GETWORKAREA, 0, &g_Data->WorkAreaRect, NULL);

        // 将桌面绘制到 DesktopBits
        PaintDesktop(DynDisplay.DesktopBits->Canvas->Handle);

        // 取得时间字串的显示尺寸
        DisplaySize = DynDisplay.BackBits->Canvas->TextExtent(TimeToStr(Now()));

        // 计算时间字串的显示位置
        DynDisplay.Position = Point(g_Data->WorkAreaRect.Right - DisplaySize.cx - 20 - g_Data->WorkAreaRect.Left, g_Data->WorkAreaRect.Bottom - DisplaySize.cy - 20 - g_Data->WorkAreaRect.Top);
        // 计算设定按钮的位置
        ConfigButton->Left = g_Data->WorkAreaRect.Right - ConfigButton->Width - 20 - g_Data->WorkAreaRect.Left;
        ConfigButton->Top = DynDisplay.Position.y - ConfigButton->Height - 10;

        ShowWindow(ConfigButton->Handle, SW_SHOW);
        break;

    case WM_TIMER:
      DynDisplay.DisplayString = TimeToStr(Now()); // 取得目前时间字串

      // 取得时间字串的显示尺寸
      DisplaySize = DynDisplay.BackBits->Canvas->TextExtent(DynDisplay.DisplayString);

        // 取得前次及此次显示区域的交集
      DynDisplay.CurRect = Rect(0, 0, DisplaySize.cx, DisplaySize.cy);
      OffsetRect(&DynDisplay.CurRect, DynDisplay.Position.x, DynDisplay.Position.y);
      UnionRect(&DynDisplay.CurRect, &DynDisplay.CurRect, &DynDisplay.LastRect);
      InvalidateRect(Window, &DynDisplay.CurRect, false);

      DynDisplay.LastRect = DynDisplay.CurRect;

      return 0;

    case WM_SUBCLASSED:
//      DebugStr("WM_SUBCLASSED");
      SetTimer(Window, TIMER_ID, 1000, NULL);

      ConfigButton = new TConfigButton(g_Data->ParentWnd);
      ConfigButton->Font->Name = "新细明体";
      ConfigButton->Font->Size = 11;
      ConfigButton->Caption = "设定";

      // 建立储存桌面影像的 bitmap
      DynDisplay.DesktopBits = new Graphics::TBitmap;
      DynDisplay.DesktopBits->Width = Screen->Width;
      DynDisplay.DesktopBits->Height = Screen->Height;

      // 建立用来做 double-buffering 贴图的 bitmap
      DynDisplay.BackBits = new Graphics::TBitmap;
      // 设定好时间显示的字形及颜色
      DynDisplay.BackBits->Canvas->Font->Height = 48;
      DynDisplay.BackBits->Canvas->Font->Color = clRed;
      DynDisplay.BackBits->Canvas->Font->Name = "超研泽中隶";

      // 强迫取得桌面影像, 放置按钮
      SendMessage(Window, WM_SETTINGCHANGE, 0, 0);
      break;

    case WM_BEFORE_UNSUBCLASS:
//      DebugStr("WM_BEFORE_UNSUBCLASS");
      KillTimer(Window, TIMER_ID);

      delete ConfigButton;
      ConfigButton = NULL;

      delete DynDisplay.DesktopBits;
      delete DynDisplay.BackBits;
      break;

    case WM_ERASEBKGND: // 清除背景
      DC = (HDC)WPARAM; // 使用讯息内附的 DC

      if (g_Data->ShowDesktopWallPaper)
        // 若要看到桌布, 就呼叫原来的视窗函式
        CallWindowProc((FARPROC)g_Data->OrigWndProc, Window, AMessage, WPARAM, LPARAM);
      else {
        // 若不要看到桌布, 就涂满桌面颜色
        R = Rect(0, 0, Screen->Width, Screen->Height);
        HANDLE BackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_DESKTOP));
        FillRect(DC, &R, BackgroundBrush);
        DeleteObject(BackgroundBrush);
      }

      // 将短文画在背景上
      if (g_Data->Essay_LineCount > 0)
        DrawEssay(DC);

      // 重绘设定按钮
      if (ConfigButton) {
        ConfigButton->Invalidate();
        ConfigButton->Update();
      }

      // 背景清除完成
      return 1;

    default:

    if (AMessage >= WM_MOUSEFIRST && AMessage <= WM_MOUSELAST) { // 拦截所有的滑鼠相关讯息
      // 取得讯息发生的滑鼠指标位置
      TLVHitTestInfo HitTestInfo;
      
      HitTestInfo.pt = Point(LOWORD(LPARAM), HIWORD(LPARAM));

      if (ConfigButton && SendMessage(Window, LVM_HITTEST, 0, Integer(&HitTestInfo)) == -1) {
        // 若讯息发生在设定按钮上 ...
        R = ConfigButton->BoundsRect;

        if (PtInRect(&R, HitTestInfo.pt)) {
          // 换算成相对於 ConfigButton client area 的座标值
          ClientToScreen(Window, &HitTestInfo.pt);
          ScreenToClient(ConfigButton->Handle, &HitTestInfo.pt);

          // 将讯息转交给 ConfigButton
          // 在此不用 PostMessage 而用 SendMessage 的原因是, 希望在按钮处理完
          // 讯息後立即重绘按钮区域的桌面
          LPARAM = MAKELONG(HitTestInfo.pt.x, HitTestInfo.pt.y);
          SendMessage(ConfigButton->Handle, AMessage, WPARAM, LPARAM);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -