此代码创建一个AV:
function PAIsMainAppWindow(Wnd: THandle): Boolean; var ParentWnd: THandle; ExStyle: DWORD; begin if IsWindowVisible(Wnd) then begin ParentWnd := THandle(GetWindowLongPtr(Wnd, GWLP_HWNDPARENT)); ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE); Result := ((ParentWnd = 0) or (ParentWnd = GetDesktopWindow)) and ((ExStyle and WS_EX_TOOLWINDOW = 0) or (ExStyle and WS_EX_APPWINDOW <> 0)); end else Result := False; end; function PAEnumTaskWindowsProc(Wnd: THandle; List: TStrings): Boolean; stdcall; var Caption: array [0..1024] of Char; begin if PAIsMainAppWindow(Wnd) and (GetWindowText(Wnd, Caption, SizeOf(Caption)) > 0) then List.AddObject(ExtractFileName(GetProcessNameFromWnd(Wnd)), Pointer(Wnd)); Result := True; end; function PAGetTaskWindowHandleFromProcess(const AProcessName: string): THandle; var sl: TStringList; i: Integer; begin Result := 0; sl := TStringList.Create(True); // stringlist owns objects try if EnumWindows(@PAEnumTaskWindowsProc, LPARAM(sl)) then begin for i := 0 to sl.Count - 1 do begin if SameText(AProcessName, sl[i]) then begin Result := THandle(sl.Objects[i]); BREAK; end; end; end; finally sl.Free; // AV! end; end; ChromeHandle := PAGetTaskWindowHandleFromProcess('chrome.exe');
很明显AV出现是因为释放stringlist也会破坏功能结果.但是如何避免这种情况呢?
首先,让我们看一下实际的代码.字符串列表不包含对象.它拥有窗口把手.所以OwnsObjects
根本不合适.这将假设这些东西Objects[]
是Delphi类的实例,并调用Free
这些实例.这就是失败发生的地方.
您没有这些窗口句柄,因此您不应该尝试销毁它们.
所以,不要设置OwnsObjects
,True
问题就会消失.那就是取代这一行:
sl := TStringList.Create(True); // stringlist owns objects
有了这个:
sl := TStringList.Create;
此外,您正在将这些对象转换为THandle
.这是错误的,而不是它确实重要.从语义上讲,这些是窗口句柄,所以将它们转换为HWND
.事实上,你使用的任何地方都THandle
应该使用HWND
.
还有其他错误.当你调用use时,GetWindowText
你传递缓冲区的大小而不是它的长度.这意味着你在说缓冲区有多长.因为它们是宽字符,所以缓冲区的长度是您声称的长度的一半.寻找桌面窗口的窗户感觉不对.
让我们假设,为了参数,你的字符串列表确实包含了对象.在这种情况下,并且在理想的世界中,字符串列表类将提供一种Extract
方法,该方法是在不破坏该对象的情况下从拥有容器中移除对象的传统方法.所以你可以执行OwnsObjects
随机播放.
if SameText(AProcessName, sl[i]) then begin sl.OwnsObjects := False; Result := TSomeObject(sl.Objects[i]); sl.Objects[i] := nil; sl.OwnsObjects := True; BREAK; end;
如果您愿意,可以在创建字符串列表时设置OwnsObjects
为False
,并且仅True
在调用Free
它之前将其设置为.