delphi проверить является ли pointer Tobject
Давно мучает вопрос как верно проверить является ли pointer Tobject написал как смог но реализация мне не нравится. какие есть способы?
function CheckObject(aPointer:Pointer):boolean;
var Ob:TObject;
s:string;
//i:integer;
begin
Result:=false;
try
if Assigned(aPointer)then
begin
Ob:= TObject(aPointer);
if Assigned(Ob) then
begin
//i:=sizeof(Ob);
s:=Ob.ToString;
Result:=(Ob is TObject);
end;
end;
except
Result:=false;
end;
end;
Покапал интернет нашел пару функций по этой теме все запихнул в модуль
unit AmSystemBase;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes;
type
AmSys = class
class function PoiterIsObject(P:Pointer;
IsLongCheck:boolean=false;
LMemInfo: PMemoryBasicInformation=nil;
SysInfo:PSystemInfo=nil):boolean;
class function GetObjectClass(APointer: Pointer;
IsLongCheck:boolean=false;
LMemInfo: PMemoryBasicInformation=nil;
SysInfo:PSystemInfo=nil): TClass;
class function IsValidVMTAddress(APAddress: PCardinal;LMemInfo: PMemoryBasicInformation): Boolean;
class function InternalIsValidClass(AClassPointer: Pointer;
ADepth: Integer=0;
IsLongCheck:boolean=false;
LMemInfo: PMemoryBasicInformation=nil
): Boolean;
end;
TAmSysObjectCheck = class
protected
MemInfo: TMemoryBasicInformation;
SysInfo:TSystemInfo;
public
IsLongCheck:boolean;
function PoiterIsObject(APointer:Pointer):boolean;
function GetPointerClass(APointer: Pointer):TClass;
constructor Create;
end;
implementation
{ AmSys }
class function AmSys.PoiterIsObject(P: Pointer;
IsLongCheck: boolean=false;
LMemInfo: PMemoryBasicInformation=nil;
SysInfo:PSystemInfo=nil): boolean;
begin
Result := GetObjectClass(P,IsLongCheck,LMemInfo,SysInfo)<>nil;
end;
class function AmSys.GetObjectClass(APointer: Pointer;
IsLongCheck: boolean=false;
LMemInfo: PMemoryBasicInformation=nil;
SysInfo:PSystemInfo=nil): TClass;
var
LSystemInfo: TSystemInfo;
begin
if SysInfo=nil then
begin
FillChar(LSystemInfo,sizeof(LSystemInfo),0);
SysInfo:=@LSystemInfo;
end;
if (SysInfo.lpMinimumApplicationAddress = nil) and
(SysInfo.lpMaximumApplicationAddress = nil) then
begin
GetSystemInfo(LSystemInfo);
if SysInfo <>@LSystemInfo then
SysInfo^:= LSystemInfo;
end;
if (DWORD(APointer) <= DWORD(SysInfo.lpMinimumApplicationAddress)) or
(DWORD(APointer) >= DWORD(SysInfo.lpMaximumApplicationAddress)) or
IsBadReadPtr(APointer, 4) then
begin
Result := nil;
Exit;
end;
Result := TClass(PCardinal(APointer)^);
if not InternalIsValidClass(Pointer(Result), 0,IsLongCheck,LMemInfo) then
Result := nil;
end;
class function AmSys.InternalIsValidClass(AClassPointer: Pointer;
ADepth: Integer=0;
IsLongCheck:boolean=false;
LMemInfo: PMemoryBasicInformation=nil): Boolean;
var
LParentClassSelfPointer: PCardinal;
// C,C2:TClass;
LMem: TMemoryBasicInformation;
begin
if LMemInfo=nil then
begin
FillChar(LMem,sizeof(LMem),0);
LMemInfo:=@LMem;
end;
{Check that the self pointer as well as parent class self pointer addresses
are valid}
if (ADepth < 1000)
and IsValidVMTAddress(Pointer(Integer(AClassPointer) + vmtSelfPtr),LMemInfo)
and IsValidVMTAddress(Pointer(Integer(AClassPointer) + vmtParent),LMemInfo) then
begin
{Get a pointer to the parent class' self pointer}
LParentClassSelfPointer := PPointer(Integer(AClassPointer) + vmtParent)^;
// C:=TClass(Integer(AClassPointer));
// C2:=TClass(PCardinal(LParentClassSelfPointer)^);
{Check that the self pointer as well as the parent class is valid}
Result := (PPointer(Integer(AClassPointer) + vmtSelfPtr)^ = AClassPointer);
if not IsLongCheck then
begin
Result:= Result
and ( (LParentClassSelfPointer = nil) or (IsValidVMTAddress(LParentClassSelfPointer,LMemInfo)) );
end
else
begin
Result:= Result
and (
(LParentClassSelfPointer = nil)
or (IsValidVMTAddress(LParentClassSelfPointer,LMemInfo)
and InternalIsValidClass(PCardinal(LParentClassSelfPointer^), ADepth + 1))
);
end;
end
else
Result := False;
end;
class function AmSys.IsValidVMTAddress(APAddress: PCardinal;LMemInfo: PMemoryBasicInformation): Boolean;
var LMem: TMemoryBasicInformation;
begin
{Do some basic pointer checks: Must be dword aligned and beyond 64K}
if (Cardinal(APAddress) > 65535)
and (Cardinal(APAddress) and 3 = 0) then
begin
if LMemInfo=nil then
begin
FillChar(LMem,sizeof(LMem),0);
LMemInfo:=@LMem;
end;
{Do we need to recheck the virtual memory?}
if (Cardinal(LMemInfo.BaseAddress) > Cardinal(APAddress))
or ((Cardinal(LMemInfo.BaseAddress) + Cardinal(LMemInfo.RegionSize)) < (Cardinal(APAddress) + 4)) then
begin
{Get the VM status for the pointer}
FillChar(LMem,sizeof(LMem),0);
LMem.RegionSize := 0;
VirtualQuery(APAddress, LMem, SizeOf(LMem));
if LMemInfo <>@LMem then
LMemInfo^:=LMem;
end;
{Check the readability of the memory address}
Result := (Cardinal(LMemInfo.RegionSize) >= 4)
and (LMemInfo.State = MEM_COMMIT)
and (LMemInfo.Protect and (PAGE_READONLY or PAGE_READWRITE
or PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY) <> 0)
and (LMemInfo.Protect and PAGE_GUARD = 0);
end
else
Result := False;
end;
{ TAmSysObjectCheck }
constructor TAmSysObjectCheck.Create;
begin
inherited ;
IsLongCheck:=false;
FillChar(MemInfo,sizeof(MemInfo),0);
FillChar(SysInfo,sizeof(SysInfo),0);
end;
function TAmSysObjectCheck.GetPointerClass(APointer: Pointer): TClass;
begin
Result:= AmSys.GetObjectClass(APointer,IsLongCheck,@MemInfo,@SysInfo);
end;
function TAmSysObjectCheck.PoiterIsObject(APointer: Pointer): boolean;
begin
Result:= AmSys.PoiterIsObject(APointer,IsLongCheck,@MemInfo,@SysInfo);
end;
end.
Использую так
procedure TAmEventStrings.SfInternalClear;
var i:integer;
ObjCheck:TAmSysObjectCheck;//<< ВОТ ОБЪЕКТ КОТОРЫЙ ЧЕКАЕТ POINTER
begin
if not Assigned(Sf) or (Sf.Count<=0) then exit;
ObjCheck:=TAmSysObjectCheck.Create;
try
for I := Sf.Count-1 downto 0 do
SfInternalFreeObject(I,ObjCheck);
finally
FreeAndNil(ObjCheck);
Sf.Clear;
end;
end;
procedure TAmEventStrings.SfInternalFreeObject(Index: integer;ObjCheck:TAmSysObjectCheck);
var I:integer;
ObjCheckCreate:boolean;
P:Pointer;
begin
P:= Sf[Index];
if not Assigned(P) then
exit;
if not Assigned(ObjCheck) then
begin
ObjCheckCreate:=true;
ObjCheck:=TAmSysObjectCheck.Create;
end
else ObjCheckCreate:=false;
try
for I := 0 to FListObjectItemClass.Count-1 do
if Assigned(FListObjectItemClass[i]) then
begin
if ObjCheck.PoiterIsObject(P) and ( TObject(P) is FListObjectItemClass[i]) then
begin
Sf[Index]:=nil;
TObject(P).Free;
//P:=nil;
exit;
end;
end;
finally
if ObjCheckCreate then
FreeAndNil(ObjCheck);
end;
end;
Ответы (1 шт):
Судя по вопросу, вы решаете проблему XY - Что такое «Ошибка молотка» или «Ошибка XY»?
Вы никогда не сможете достоверно проверить, на что указывает случайный указатель.
Указатель это 4 байта (в 32-битной системе), другими словами - число от 0 до 4294967295. То есть любой случайный адрес в памяти приложения. В принципе часть значений можно отсеять (вероятно, есть области памяти куда ваше приложение ТОЧНО не может указывать, и где ТОЧНО не может быть созданных объектов). Но в остальных случаях, как интерпретировать то куда он указывает - дело "смотрящего".
Там может быть ЛЮБАЯ комбинация мусорных значений, а может быть и объект. А может быть только что уничтоженный объект(!). Два последних варианта вы между собой точно никак не разделите, не привлекая какого-то внешнего реестра или системы управления.