尝试释放带有对象的 StringList 时的复杂问题使我的应用程序崩溃
Complex problem when trying to free a StringList with objects crashes my app
在Delphi10.4.2 win-32 VCL Application in Windows10中,我用这段代码收集了一些windows:[=14的句柄和程序路径=]
function GetPathFromPID(const PID: Cardinal): string;
var
hProcess: THandle;
Path: array[0..MAX_PATH - 1] of Char;
begin
hProcess := Winapi.Windows.OpenProcess(Winapi.Windows.PROCESS_QUERY_INFORMATION or Winapi.Windows.PROCESS_VM_READ, False, PID);
if hProcess <> 0 then
try
if Winapi.PsAPI.GetModuleFileNameEx(hProcess, 0, Path, Winapi.Windows.MAX_PATH) = 0 then
RaiseLastOSError;
Result := Path;
finally
Winapi.Windows.CloseHandle(hProcess)
end
else
RaiseLastOSError;
end;
function EnumWinProc(wHandle: Winapi.Windows.HWND; aList: TStringList): Winapi.Windows.Bool; stdcall;
var
strPath: string;
IsAppMainWin: Boolean;
ProcId: Cardinal;
begin
IsAppMainWin := IsWindowVisible(wHandle) and // Visible
(GetWindow(wHandle, Winapi.Windows.GW_OWNER) = 0) and // Not owned by other windows
(GetParent(wHandle) = 0) and // Does not have any parent
(GetWindowLong(wHandle, Winapi.Windows.GWL_EXSTYLE) and Winapi.Windows.WS_EX_TOOLWINDOW = 0); // Not a tool window
if IsAppMainWin then
begin
GetWindowThreadProcessID(wHandle, ProcId);
try
strPath := GetPathFromPID(ProcId);
except
strPath := 'UnknownProgramPath';
end;
aList.AddObject(strPath, TObject(wHandle));
end;
Result := True;
end;
procedure ClearList(List: TStringList);
//
var
i: Integer;
begin
// crash occurs here!
for i := 0 to pred(List.Count) do
List.Objects[i].Free;
List.Clear;
end;
procedure TformMain.OutputAllAppWindows;
begin
var sl := TStringList.Create;
try
sl.OwnsObjects := True;
EnumWindows(@EnumWinProc, Winapi.Windows.LPARAM(sl));
for var i := 0 to sl.Count - 1 do
begin
CodeSite.Send('window handle', Winapi.Windows.HWND(sl.Objects[i]));
CodeSite.Send('program-path sl[i]', sl[i]);
end;
ClearList(sl); // EDIT: forgot this line!
finally
sl.Free;
end;
end;
EurekaLog 报告崩溃:
2.1 Date : Sat, 31 Jul 2021 22:36:53 +0200
2.2 Address : 005509D2
2.5 Type : EInvalidPointer
2.6 Message : Application made attempt to free invalid or unknown memory block: [=11=]010AE2 OBJECT [?] 0 bytes.
2.7 ID : B5002468
2.8 Count : 1
2.11 Sent : 0
这是在 EurekaLog 调用堆栈的顶部:
这里有两个问题:
首先,您将 整数 作为字符串列表中的对象,因此您不能将这些“对象”视为对象,因为它们不是。
特别是,没有任何东西可以释放,你不能释放这些“对象”;这相当于做 TObject(some random pointer).Free
.
其次,如果你确实在列表中放置了真实的对象,你仍然会有一个错误,因为你会先自己释放它们而不是 nil
ing 它们的引用,因此字符串列表的析构函数将尝试“销毁”这些悬挂指针指向的东西。再次,糟糕。
在Delphi10.4.2 win-32 VCL Application in Windows10中,我用这段代码收集了一些windows:[=14的句柄和程序路径=]
function GetPathFromPID(const PID: Cardinal): string;
var
hProcess: THandle;
Path: array[0..MAX_PATH - 1] of Char;
begin
hProcess := Winapi.Windows.OpenProcess(Winapi.Windows.PROCESS_QUERY_INFORMATION or Winapi.Windows.PROCESS_VM_READ, False, PID);
if hProcess <> 0 then
try
if Winapi.PsAPI.GetModuleFileNameEx(hProcess, 0, Path, Winapi.Windows.MAX_PATH) = 0 then
RaiseLastOSError;
Result := Path;
finally
Winapi.Windows.CloseHandle(hProcess)
end
else
RaiseLastOSError;
end;
function EnumWinProc(wHandle: Winapi.Windows.HWND; aList: TStringList): Winapi.Windows.Bool; stdcall;
var
strPath: string;
IsAppMainWin: Boolean;
ProcId: Cardinal;
begin
IsAppMainWin := IsWindowVisible(wHandle) and // Visible
(GetWindow(wHandle, Winapi.Windows.GW_OWNER) = 0) and // Not owned by other windows
(GetParent(wHandle) = 0) and // Does not have any parent
(GetWindowLong(wHandle, Winapi.Windows.GWL_EXSTYLE) and Winapi.Windows.WS_EX_TOOLWINDOW = 0); // Not a tool window
if IsAppMainWin then
begin
GetWindowThreadProcessID(wHandle, ProcId);
try
strPath := GetPathFromPID(ProcId);
except
strPath := 'UnknownProgramPath';
end;
aList.AddObject(strPath, TObject(wHandle));
end;
Result := True;
end;
procedure ClearList(List: TStringList);
//
var
i: Integer;
begin
// crash occurs here!
for i := 0 to pred(List.Count) do
List.Objects[i].Free;
List.Clear;
end;
procedure TformMain.OutputAllAppWindows;
begin
var sl := TStringList.Create;
try
sl.OwnsObjects := True;
EnumWindows(@EnumWinProc, Winapi.Windows.LPARAM(sl));
for var i := 0 to sl.Count - 1 do
begin
CodeSite.Send('window handle', Winapi.Windows.HWND(sl.Objects[i]));
CodeSite.Send('program-path sl[i]', sl[i]);
end;
ClearList(sl); // EDIT: forgot this line!
finally
sl.Free;
end;
end;
EurekaLog 报告崩溃:
2.1 Date : Sat, 31 Jul 2021 22:36:53 +0200
2.2 Address : 005509D2
2.5 Type : EInvalidPointer
2.6 Message : Application made attempt to free invalid or unknown memory block: [=11=]010AE2 OBJECT [?] 0 bytes.
2.7 ID : B5002468
2.8 Count : 1
2.11 Sent : 0
这是在 EurekaLog 调用堆栈的顶部:
这里有两个问题:
首先,您将 整数 作为字符串列表中的对象,因此您不能将这些“对象”视为对象,因为它们不是。
特别是,没有任何东西可以释放,你不能释放这些“对象”;这相当于做 TObject(some random pointer).Free
.
其次,如果你确实在列表中放置了真实的对象,你仍然会有一个错误,因为你会先自己释放它们而不是 nil
ing 它们的引用,因此字符串列表的析构函数将尝试“销毁”这些悬挂指针指向的东西。再次,糟糕。