这段代码创建了一个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是因为释放字符串列表也会破坏函数结果。但是如何避免这种情况呢?

最佳答案

首先,让我们看一下实际的代码。字符串列表不包含对象。它装有 window Handlebars 。因此,OwnsObjects根本不合适。假设Objects[]中的内容是类的Delphi实例,并在这些实例上调用Free。那就是发生故障的地方。

您不拥有这些窗口句柄,因此不应尝试销毁它们。

因此,不要将OwnsObjects设置为True,问题就会消失。那就是替换这一行:

sl := TStringList.Create(True); // stringlist owns objects

有了这个:
sl := TStringList.Create;

此外,您将这些对象转换为THandle。这是错误的,并不是说它实际上很重要。虽然从语义上讲,它们是窗口句柄,所以将它们强制转换为HWND。实际上,在所有使用THandle的地方都应该使用HWND

也有其他错误。当您调用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

关于delphi - 如何避免函数结果被Free inside函数破坏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34359797/

10-14 16:28
查看更多