📄 voyeur.dpr
字号:
program Voyeur;
{$R Voyeur.res}
uses Windows, Messages, VoyHelp in 'VoyHelp.pas';
const
IDI_VOYEUR = 102; // 图标资源ID
IDC_EYES = 103; // 光标资源ID
IDC_WNDINFO = 1000; // 编辑框ID
// WM_CREATE 消息处理函数
function Voyeur_OnCreate(hWnd: HWND; lpCreateStruct: PCreateStruct): Boolean;
var
hWndEdit: LongWord; // HWND
uTabStop: UINT;
begin
// 建立编辑控件
hWndEdit := CreateWindowEx(0, 'EDIT',
'Click and drag the right mouse button to select a window.',
WS_CHILD or WS_HSCROLL or WS_VSCROLL or WS_VISIBLE or ES_AUTOHSCROLL or
ES_AUTOVSCROLL or ES_LEFT or ES_MULTILINE or ES_READONLY,
0, 0, 0, 0, hWnd, IDC_WNDINFO, HInstance, nil);
// 如果建立成功
if (hWndEdit <> 0) then
begin
// 设置统一的 tab stops , 使得文本格式整齐
// 采用Dialog单位(大约是字符平均宽度的1/4)
uTabStop := 4 * 20;
SendMessage(hWndEdit, EM_SETTABSTOPS, 1, Integer(@uTabStop));
end;
// 是否建立成功
Result := (hWndEdit <> 0);
end;
// WM_SIZE 消息处理过程
procedure Voyeur_OnSize(hWnd: HWND; state: UINT; cx, cy: Integer);
begin
// 使编辑框充满窗体
SetWindowPos(GetDlgItem(hWnd, IDC_WNDINFO), 0, 0, 0, cx, cy, SWP_NOZORDER);
end;
// WM_PARENTNOTIFY 消息处理过程
procedure Voyeur_OnParentNotify(hWnd: HWND; Msg: UINT; hWndChild: HWND; idChild: Integer);
begin
// 当用户在编辑框控件按下鼠标右键,
// 系统会发WM_PARENTNOTIFY消息通知主窗体
// 此时, 程序开始进入'窥视'状态 ..
if (Msg = WM_RBUTTONDOWN) then
begin
// 自身置于 window manager’s list 的最底端
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
// 强制所有鼠标消息都只发送给本窗口
SetCapture(hWnd);
// 设置鼠标光标为'眼睛', 表明正在'窥视'..
SetCursor(LoadCursor(HInstance, MakeIntResource(IDC_EYES)));
// 最后被选择的窗体的句柄, 保存在本窗体实例的额外内存
// 现在, 先将其初始化为 0
SetWindowLong(hWnd, 0, 0);
end;
end;
// WM_MOUSEMOVE 消息处理过程
procedure Voyeur_OnMouseMove(hWnd: HWND; x, y: Integer; keyFlags: UINT);
var
dwMousePos: DWORD;
ptMouse: TPoint;
hWndSubject: LongWord; // HWND
hWndLastSubject: LongWord; // HWND
begin
// 如果此过程被调用, 应该是由于SetCapture(hWnd),
// 因为平时鼠标移动消息总是只送给前面的编辑控件
// 取得最后一次取出消息时光标在屏幕上的位置(接近于当前位置),
// 注意, 不要使用x和y参数, 因为它是针对hWnd客户区的相对坐标
dwMousePos := GetMessagePos();
// 取出上一个被选择的窗体句柄
hWndLastSubject := GetWindowLong(hWnd, 0);
// 取得当前鼠标所指的窗体句柄
ptMouse.x := LOWORD(dwMousePos);
ptMouse.y := HIWORD(dwMousePos);
hWndSubject := WindowFromPoint(ptMouse);
// 如果鼠标指向的是自己的窗体, 则不处理
if (GetWindowThreadProcessId(hWndSubject, nil) = GetCurrentThreadId()) then Exit;
// 如果和上一个被选择的窗体是同一个, 也不处理
if (hWndLastSubject = hWndSubject) then Exit;
// 恢复上次被选择的窗体的边框
if (hWndLastSubject <> 0) then
VoyHelp_DrawWindowFrame(hWndLastSubject);
// 反转本次被选择的窗体的边框
VoyHelp_DrawWindowFrame(hWndSubject);
// 在编辑框控件内显示此窗体信息
VoyHelp_UpdateWindowInfo(GetDlgItem(hWnd, IDC_WNDINFO), hWndSubject);
// 新的被选窗体句柄保存至额外空间
SetWindowLong(hWnd, 0, hWndSubject);
end;
// WM_CAPTURECHANGED 消息处理过程
procedure Voyeur_OnCaptureChanged(hWnd, hWndNewCapture: HWND);
var
hWndLastSubject: LongWord; // HWND
begin
hWndLastSubject := GetWindowLong(hWnd, 0);
// 当失去鼠标捕获, 如果之前仍有窗体被反转边框, 则..
if (hWndLastSubject <> 0) then
begin
VoyHelp_DrawWindowFrame(hWndLastSubject); // 恢复最后被选择的窗体边框
BringWindowToTop(hWnd); // 自身提至 window manager’s list 顶端
end;
SetWindowLong(hWnd, 0, 0);
end;
// WM_RBUTTONUP 消息处理过程
procedure Voyeur_OnRButtonUp(hWnd: HWND; x, y: Integer; keyFlags: UINT);
begin
// 如果此过程被调用, 应该是由于 SetCapture(),
// 因为平时鼠标抬起消息总是送给前面的编辑框
// 取消鼠标捕获, 使其他窗口可以正常收到鼠标消息
ReleaseCapture();
end;
// WM_QUERYENDSESSION 消息处理过程
function Voyeur_OnQueryEndSession(hWnd: HWND): Boolean;
var
fIsDataUnsaved, fOKToEndSession: Boolean;
n: Integer;
begin
// 此处演示, 如果是关闭前需要保存数据的程序,
// 应该如何正确处理, 即使是由关机导致的关闭
fIsDataUnsaved := TRUE; // 数据未保存
fOKToEndSession := TRUE; // 允许关闭
if (fIsDataUnsaved) then
begin
n := MessageBox(hWnd, 'Do you want to save changes?', // 保存提示对话框
'Voyeur - Example technique for proper shutdown', MB_YESNOCANCEL or MB_ICONWARNING);
if (n = IDYES) then // 需要保存
begin
// 可以在此处弹出文件保存对话框, 比如 n := GetOpenFileName() ..
end;
if (n = IDCANCEL) then fOKToEndSession := FALSE; // 不许关闭
end;
Result := fOKToEndSession;
end;
// WM_ENDSESSION 消息处理过程
procedure Voyeur_OnEndSession(hWnd: HWND; fEnding: BOOL);
begin
if (fEnding) then DestroyWindow(hWnd); // 系统即将关闭, 则注销窗体
// DestroyWindow()会发送WM_DESTROY和WM_NCDESTROY两个消息给窗体过程
end;
// WM_DESTROY 消息处理过程
procedure Voyeur_OnDestroy(hWnd: HWND);
begin
// 注意, 系统会自动注销hWnd的子窗体编辑框控件
// 通知消息循环结束, 之后线程和进程也随即结束
PostQuitMessage(0);
end;
// WM_CLOSE 消息处理过程
procedure Voyeur_OnClose(hWnd: HWND);
begin
// 与关机或注销同样的处理
Voyeur_OnEndSession(hWnd, Voyeur_OnQueryEndSession(hWnd));
end;
// 主窗体消息处理回调函数
function Voyeur_WndProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := 0;
case (uMsg) of
WM_CREATE:
if Voyeur_OnCreate(hWnd, PCreateStruct(lParam)) = FALSE then Result := -1;
WM_SIZE:
Voyeur_OnSize(hWnd, wParam, LOWORD(lParam), HIWORD(lParam));
WM_PARENTNOTIFY:
Voyeur_OnParentNotify(hWnd, LOWORD(wParam), lParam, HIWORD(wParam));
WM_MOUSEMOVE:
Voyeur_OnMouseMove(hWnd, LOWORD(lParam), HIWORD(lParam), wParam);
WM_RBUTTONUP:
Voyeur_OnRButtonUp(hWnd, LOWORD(lParam), HIWORD(lParam), wParam);
WM_CAPTURECHANGED:
Voyeur_OnCaptureChanged(hWnd, lParam);
WM_CLOSE:
Voyeur_OnClose(hWnd);
WM_DESTROY:
Voyeur_OnDestroy(hWnd);
WM_QUERYENDSESSION:
Result := Integer(Voyeur_OnQueryEndSession(hWnd));
WM_ENDSESSION:
Voyeur_OnEndSession(hWnd, BOOL(wParam));
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
end;
// 程序'主线程'入口
var
wc: TWndClassEx;
atomClassNameVoyeur: ATOM;
hWndMain: HWND;
Msg: TMsg;
begin
// 填充类结构
ZeroMemory(@wc, SizeOf(TWndClassEx));
wc.cbSize := SizeOf(TWndClassEx);
wc.lpfnWndProc := @Voyeur_WndProc;
wc.cbWndExtra := SizeOf(HWND); // 窗体额外空间保存最后选择的窗体的句柄
wc.hInstance := HInstance;
wc.hIcon := LoadIcon(HInstance, MakeIntreSource(IDI_VOYEUR));
wc.hCursor := LoadCursor(0, IDC_ARROW);
wc.lpszClassName := 'Voyeur';
wc.hIconSm := wc.hIcon;
// 注册窗体类
atomClassNameVoyeur := RegisterClassEx(wc);
if (atomClassNameVoyeur = INVALID_ATOM) then Exit;
// 建立主窗体
hWndMain := CreateWindowEx(0, MakeIntAtom(atomClassNameVoyeur),
'Voyeur', WS_OVERLAPPEDWINDOW or WS_VISIBLE, Integer(CW_USEDEFAULT),
SW_SHOW, // 注意: 当x为CW_USEDEFAULT, 此参数将作为隐含调用的ShowWindow()的参数
Integer(CW_USEDEFAULT), 0, 0, 0, HInstance, nil);
// 是窗体句柄
if IsWindow(hWndMain) then
begin
while GetMessage(Msg, 0, 0, 0) do // 循环, 直至取出 WM_QUIT
begin
TranslateMessage(Msg); // 转换消息
DispatchMessage(Msg); // 分发消息
end;
end;
// 注销窗体类
UnregisterClass(MakeIntAtom(atomClassNameVoyeur), HInstance);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -