当前位置:  开发笔记 > 前端 > 正文

如何避免Free内部函数破坏函数结果?

如何解决《如何避免Free内部函数破坏函数结果?》经验,为你挑选了1个好方法。

此代码创建一个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也会破坏功能结果.但是如何避免这种情况呢?



1> David Heffer..:

首先,让我们看一下实际的代码.字符串列表不包含对象.它拥有窗口把手.所以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;

如果您愿意,可以在创建字符串列表时设置OwnsObjectsFalse,并且仅True在调用Free它之前将其设置为.

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有