没有安全的方法来确定有效指针是否为 TObject 引用
您只能确定指针不是对象引用。
话虽如此,对于调试目的有一种方法可以检测到该指针可能是一个对象引用,但您也可能会得到误报——我们正在检查的内存内容可能完全符合检查条件。
每个对象实例还持有一个指向其类虚方法表——VMT 的指针。该指针的位置由System单元中声明的vmtSelfPtr常量定义。
该入口槽中的类引用保留对 self 的引用,这一事实将告诉我们我们正在寻找可能的类引用。
从给定的指针值开始,我们获得潜在类的地址,并检查该类是否在其自己的类指针中指向自身。
除此之外,对于每个指针,我们将首先检查它是否属于有效地址空间。
您可以在此处找到有关 VMT 的更多信息Internal Data Formats - Class Types
检测指针是否是可能对象的代码取自
Spring4D library:
uses
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF }
TypInfo;
function IsValidObject(p: PPointer): Boolean;
{$IFDEF MSWINDOWS}
var
memInfo: TMemoryBasicInformation;
{$ENDIF}
function IsValidAddress(address: Pointer): Boolean;
begin
// Must be above 64k and 4 byte aligned
if (UIntPtr(address) > $FFFF) and (UIntPtr(address) and 3 = 0) then
begin
{$IFDEF MSWINDOWS}
// do we need to recheck the virtual memory?
if (UIntPtr(memInfo.BaseAddress) > UIntPtr(address))
or ((UIntPtr(memInfo.BaseAddress) + memInfo.RegionSize) < (UIntPtr(address) + SizeOf(Pointer))) then
begin
// retrieve the status for the pointer
memInfo.RegionSize := 0;
VirtualQuery(address, memInfo, SizeOf(memInfo));
end;
// check the readability of the memory address
if (memInfo.RegionSize >= SizeOf(Pointer))
and (memInfo.State = MEM_COMMIT)
and (memInfo.Protect and (PAGE_READONLY or PAGE_READWRITE
or PAGE_WRITECOPY or PAGE_EXECUTE or PAGE_EXECUTE_READ
or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY) <> 0)
and (memInfo.Protect and PAGE_GUARD = 0) then
{$ENDIF}
Exit(True);
end;
Result := False;
end;
begin
Result := False;
if Assigned(p) then
try
{$IFDEF MSWINDOWS}
memInfo.RegionSize := 0;
{$ENDIF}
if IsValidAddress(p)
// not a class pointer - they point to themselves in the vmtSelfPtr slot
and not (IsValidAddress(PByte(p) + vmtSelfPtr)
and (p = PPointer(PByte(p) + vmtSelfPtr)^)) then
if IsValidAddress(p^) and IsValidAddress(PByte(p^) + vmtSelfPtr)
// looks to be an object, it points to a valid class pointer
and (p^ = PPointer(PByte(p^) + vmtSelfPtr)^) then
Result := True;
except
end; //FI:W501
end;
我们可以像这样使用该功能:
var
o: TObject;
p: Pointer;
i: NativeInt;
begin
i := 5;
p := @i;
o := TObject.Create;
Writeln(IsValidObject(Pointer(o))); // TRUE
Writeln(IsValidObject(p)); // FALSE
end.
笔记:IsValidObject 应该只用于有效的指针——意思是指向有效分配内存的指针。您无法检测指针后面的对象实例是否已被释放。
如果您有以下代码,您仍然会得到 TRUE 作为 IsValidObject 调用的结果。
o := TObject.Create;
o.Free;
Writeln(IsValidObject(Pointer(o))); // TRUE