📄 hook.cpp
字号:
#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 + -