在通过进程 ID 和窗口标题枚举窗口时,我有两种回调函数的实现。
第一个使用固定大小的字符数组作为缓冲区并且工作正常(据我所知)。
另一个使用 PChar 缓冲区并使用 GetMem 分配它,GetMem在离开 GetWindowHandleByPID 函数后,我在不同的内部出现异常。
我的第二个错误在哪里?
TEnumWindowsInfo = record
ProcessID: DWORD;
HWND: THandle;
TitleUp: string;
end;
function GetWindowHandleByPID(const hPID: THandle): THandle;
var
EI: TEnumWindowsInfo;
begin
EI.ProcessID := hPID;
EI.HWND := 0;
EI.TitleUp := 'BLA';
EnumWindows(@OnEnumWindowsProc, Integer(@EI));
Result := EI.HWND;
end;
function OnEnumWindowsProc(Wnd: DWORD; var EI: TEnumWindowsInfo): BOOL; stdcall;
var
pidMatch, titleMatch: BOOL;
buf: array [0..255] of Char;
titleAsUpper: string;
begin
// check PID: GetWindowThreadProcessID
pidMatch := true;
// check window title
GetWindowText(Wnd, buf, 255);
titleAsUpper := AnsiUpperCase(buf);
titleMatch := EI.TitleUp = titleAsUpper;
Result := not (pidMatch and titleMatch); // continue if not found
if not Result then
begin
EI.HWND := WND;
end;
end;
function OnEnumWindowsProc_MEM_CORRUPT(Wnd: DWORD; var EI: TEnumWindowsInfo): BOOL; stdcall;
var
pidMatch, titleMatch: BOOL;
buffer: PChar;
len: Integer;
titleAsUpper: string;
begin
// check PID: GetWindowThreadProcessID
pidMatch := true;
// check window title
len := GetWindowTextLength(Wnd) + 1; // +1 -> null-terminated
if len > 1 then
begin
try
GetMem(buffer, len);
GetWindowText(Wnd, buffer, len);
titleAsUpper := AnsiUpperCase(buffer);
finally
FreeMem(buffer);
end;
titleMatch := EI.TitleUp = titleAsUpper;
end;
Result := not (pidMatch and titleMatch); // continue if not found
if not Result then
begin
EI.HWND := WND;
end;
end;
Run Code Online (Sandbox Code Playgroud)
在 中OnEnumWindowsProc(),您要传递到的缓冲区GetWindowText()已根据您声明的大小进行充分分配GetWindowText()。您分配了 256Char秒,但声明了 255Char秒,这意味着GetWindowText()最多可以检索 254Char秒,加上空终止符。因此,不存在破坏内存的机会。
但是,在 中OnEnumWindowsProc_MEM_CORRUPT(),您没有为缓冲区分配足够的内存。GetMem()对字节而不是字符进行操作。在 Delphi 2009 及更高版本中,GetWindowText()映射到GetWindowTextW(),这意味着它输出 Unicode UTF-16 文本而不是 ANSI 文本。是Sizeof(Char)2 个字节,而不是代码假设的 1 个字节。因此,您只分配了缓冲区所需的一半字节,但声明了完整大小GetWindowText/W(),因此GetWindowText/W()最终会溢出缓冲区,损坏内存。要解决这个问题,您需要将分配的大小乘以SizeOf(Char):
GetMem(buffer, len * SizeOf(Char));
Run Code Online (Sandbox Code Playgroud)
就我个人而言,我不会使用GetMem()这种代码,我会使用动态Char数组,甚至使用 aString代替,例如:
function OnEnumWindowsProc_MEM_CORRUPT(Wnd: DWORD; var EI: TEnumWindowsInfo): BOOL; stdcall;
var
...
buffer: array of Char; // or String
len: Integer;
...
begin
...
len := GetWindowTextLength(Wnd);
if len > 0 then
begin
// for a Char array:
Inc(len);
SetLength(buffer, len);
GetWindowText(Wnd, PChar(buffer), len);
// for a String:
SetLength(buffer, len);
GetWindowText(Wnd, PChar(buffer), len+1);
...
end;
...
end;
Run Code Online (Sandbox Code Playgroud)
附注:
调用时EnumWindows(),您应该使用LPARAM(...)而不是在对第二个参数中的地址Integer(...)进行类型转换时使用@EI,否则如果编译为 64 位可执行文件,您的代码将无法正常工作。
此外,您不需要使用AnsiUpperCase()后跟来不operator=区分大小写地比较两个字符串。RTL 具有用于此目的的函数,例如SysUtils.SameText():
titleMatch := SameText(buffer, EI.TitleUp);
Run Code Online (Sandbox Code Playgroud)
我假设您使用的是Delphi2009或更高版本。在这种情况下,GetWindowText 是 Unicode,而 Getmem 应该是
GetMem(buffer, len*sizeof(char));
Run Code Online (Sandbox Code Playgroud)
在另一种情况下,我猜你很幸运,因为你在堆栈上保留了足够大的缓冲区。
| 归档时间: |
|
| 查看次数: |
152 次 |
| 最近记录: |