📄 jclsysutils.pas
字号:
else
if PExtended(Item1)^ > PExtended(Item2)^ then
Result := 1
else
Result := 0;
end;
function DynArrayCompareFloat(Item1, Item2: Pointer): Integer;
begin
if PFloat(Item1)^ < PFloat(Item2)^ then
Result := -1
else
if PFloat(Item1)^ > PFloat(Item2)^ then
Result := 1
else
Result := 0;
end;
function DynArrayCompareAnsiString(Item1, Item2: Pointer): Integer;
begin
Result := AnsiCompareStr(PAnsiString(Item1)^, PAnsiString(Item2)^);
end;
function DynArrayCompareAnsiText(Item1, Item2: Pointer): Integer;
begin
Result := AnsiCompareText(PAnsiString(Item1)^, PAnsiString(Item2)^);
end;
function DynArrayCompareString(Item1, Item2: Pointer): Integer;
begin
Result := CompareStr(PAnsiString(Item1)^, PAnsiString(Item2)^);
end;
function DynArrayCompareText(Item1, Item2: Pointer): Integer;
begin
Result := CompareText(PAnsiString(Item1)^, PAnsiString(Item2)^);
end;
//=== Object lists ===========================================================
procedure ClearObjectList(List: TList);
var
I: Integer;
begin
if List <> nil then
begin
for I := 0 to List.Count - 1 do
begin
if List[I] <> nil then
begin
if TObject(List[I]) is TList then
begin
// recursively delete TList sublists
ClearObjectList(TList(List[I]));
end;
TObject(List[I]).Free;
List[I] := nil;
end;
end;
List.Clear;
end;
end;
procedure FreeObjectList(var List: TList);
begin
if List <> nil then
begin
ClearObjectList(List);
FreeAndNil(List);
end;
end;
//=== { TJclReferenceMemoryStream } ==========================================
constructor TJclReferenceMemoryStream.Create(const Ptr: Pointer; Size: Longint);
begin
{$IFDEF MSWINDOWS}
Assert(not IsBadReadPtr(Ptr, Size));
{$ENDIF MSWINDOWS}
inherited Create;
SetPointer(Ptr, Size);
end;
function TJclReferenceMemoryStream.Write(const Buffer; Count: Longint): Longint;
begin
raise EJclError.CreateRes(@RsCannotWriteRefStream);
end;
//=== replacement for the C distfix operator ? : =============================
function Iff(const Condition: Boolean; const TruePart, FalsePart: string): string;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Char): Char;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Byte): Byte;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Integer): Integer;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Cardinal): Cardinal;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Float): Float;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Boolean): Boolean;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Pointer): Pointer;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
function Iff(const Condition: Boolean; const TruePart, FalsePart: Int64): Int64;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
{$IFDEF SUPPORTS_VARIANT}
{$IFDEF COMPILER6_UP}
function Iff(const Condition: Boolean; const TruePart, FalsePart: Variant): Variant; overload;
begin
if Condition then
Result := TruePart
else
Result := FalsePart;
end;
{$ENDIF COMPILER6_UP}
{$ENDIF SUPPORTS_VARIANT}
//=== Classes information and manipulation ===================================
// Virtual Methods
// Helper method
procedure SetVMTPointer(AClass: TClass; Offset: Integer; Value: Pointer);
var
WrittenBytes: DWORD;
PatchAddress: PPointer;
begin
PatchAddress := Pointer(Integer(AClass) + Offset);
//! StH: WriteProcessMemory IMO is not exactly the politically correct approach;
// better VirtualProtect, direct patch, VirtualProtect
if not WriteProtectedMemory(PatchAddress, @Value, SizeOf(Value), WrittenBytes) then
raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [SysErrorMessage(GetLastError)]);
if WrittenBytes <> SizeOf(Pointer) then
raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [IntToStr(WrittenBytes)]);
// make sure that everything keeps working in a dual processor setting
FlushInstructionCache{$IFDEF MSWINDOWS}(GetCurrentProcess, PatchAddress, SizeOf(Pointer)){$ENDIF};
end;
{$IFNDEF FPC}
function GetVirtualMethodCount(AClass: TClass): Integer;
var
BeginVMT: Longint;
EndVMT: Longint;
TablePointer: Longint;
I: Integer;
begin
BeginVMT := Longint(AClass);
// Scan the offset entries in the class table for the various fields,
// namely vmtIntfTable, vmtAutoTable, ..., vmtDynamicTable
// The last entry is always the vmtClassName, so stop once we got there
// After the last virtual method there is one of these entries.
EndVMT := PLongint(Longint(AClass) + vmtClassName)^;
// Set iterator to first item behind VMT table pointer
I := vmtSelfPtr + SizeOf(Pointer);
repeat
TablePointer := PLongint(Longint(AClass) + I)^;
if (TablePointer <> 0) and (TablePointer >= BeginVMT) and
(TablePointer < EndVMT) then
EndVMT := Longint(TablePointer);
Inc(I, SizeOf(Pointer));
until I >= vmtClassName;
Result := (EndVMT - BeginVMT) div SizeOf(Pointer);
end;
{$ENDIF ~FPC}
function GetVirtualMethod(AClass: TClass; const Index: Integer): Pointer;
begin
Result := PPointer(Integer(AClass) + Index * SizeOf(Pointer))^;
end;
procedure SetVirtualMethod(AClass: TClass; const Index: Integer; const Method: Pointer);
begin
SetVMTPointer(AClass, Index * SizeOf(Pointer), Method);
end;
// Dynamic Methods
type
TvmtDynamicTable = packed record
Count: Word;
{IndexList: array [1..Count] of Word;
AddressList: array [1..Count] of Pointer;}
end;
function GetDynamicMethodCount(AClass: TClass): Integer; assembler;
asm
MOV EAX, [EAX].vmtDynamicTable
TEST EAX, EAX
JE @@Exit
MOVZX EAX, WORD PTR [EAX]
@@Exit:
end;
function GetDynamicIndexList(AClass: TClass): PDynamicIndexList; assembler;
asm
MOV EAX, [EAX].vmtDynamicTable
ADD EAX, 2
end;
function GetDynamicAddressList(AClass: TClass): PDynamicAddressList; assembler;
asm
MOV EAX, [EAX].vmtDynamicTable
MOVZX EDX, Word ptr [EAX]
ADD EAX, EDX
ADD EAX, EDX
ADD EAX, 2
end;
function HasDynamicMethod(AClass: TClass; Index: Integer): Boolean; assembler;
// Mainly copied from System.GetDynaMethod
asm
{ -> EAX vmt of class }
{ DX dynamic method index }
PUSH EDI
XCHG EAX, EDX
JMP @@HaveVMT
@@OuterLoop:
MOV EDX, [EDX]
@@HaveVMT:
MOV EDI, [EDX].vmtDynamicTable
TEST EDI, EDI
JE @@Parent
MOVZX ECX, WORD PTR [EDI]
PUSH ECX
ADD EDI,2
REPNE SCASW
JE @@Found
POP ECX
@@Parent:
MOV EDX,[EDX].vmtParent
TEST EDX,EDX
JNE @@OuterLoop
MOV EAX, 0
JMP @@Exit
@@Found:
POP EAX
MOV EAX, 1
@@Exit:
POP EDI
end;
{$IFNDEF FPC}
function GetDynamicMethod(AClass: TClass; Index: Integer): Pointer; assembler;
asm
CALL System.@FindDynaClass
end;
{$ENDIF ~FPC}
//=== Interface Table ========================================================
function GetInitTable(AClass: TClass): PTypeInfo; assembler;
asm
MOV EAX, [EAX].vmtInitTable
end;
function GetFieldTable(AClass: TClass): PFieldTable; assembler;
asm
MOV EAX, [EAX].vmtFieldTable
end;
function GetMethodTable(AClass: TClass): PMethodTable; assembler;
asm
MOV EAX, [EAX].vmtMethodTable
end;
function GetMethodEntry(MethodTable: PMethodTable; Index: Integer): PMethodEntry;
begin
Result := Pointer(Cardinal(MethodTable) + 2);
for Index := Index downto 1 do
Inc(Cardinal(Result), Result^.EntrySize);
end;
//=== Class Parent methods ===================================================
procedure SetClassParent(AClass: TClass; NewClassParent: TClass);
var
WrittenBytes: DWORD;
PatchAddress: Pointer;
begin
PatchAddress := PPointer(Integer(AClass) + vmtParent)^;
//! StH: WriteProcessMemory IMO is not exactly the politically correct approach;
// better VirtualProtect, direct patch, VirtualProtect
if not WriteProtectedMemory(PatchAddress, @NewClassParent, SizeOf(Pointer), WrittenBytes) then
raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [SysErrorMessage(GetLastError)]);
if WrittenBytes <> SizeOf(Pointer) then
raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [IntToStr(WrittenBytes)]);
// make sure that everything keeps working in a dual processor setting
FlushInstructionCache{$IFDEF MSWINDOWS}(GetCurrentProcess, PatchAddress, SizeOf(Pointer)){$ENDIF};
end;
function GetClassParent(AClass: TClass): TClass; assembler;
asm
MOV EAX, [EAX].vmtParent
TEST EAX, EAX
JE @@Exit
MOV EAX, [EAX]
@@Exit:
end;
{$IFNDEF FPC}
function IsClass(Address: Pointer): Boolean; assembler;
asm
CMP Address, Address.vmtSelfPtr
JNZ @False
MOV Result, True
JMP @Exit
@False:
MOV Result, False
@Exit:
end;
{$ENDIF ~FPC}
{$IFNDEF FPC}
function IsObject(Address: Pointer): Boolean; assembler;
asm
// or IsClass(Pointer(Address^));
MOV EAX, [Address]
CMP EAX, EAX.vmtSelfPtr
JNZ @False
MOV Result, True
JMP @Exit
@False:
MOV Result, False
@Exit:
end;
{$ENDIF ~FPC}
//=== Interface information ==================================================
function GetImplementorOfInterface(const I: IInterface): TObject;
{ TODO -cDOC : Original code by Hallvard Vassbotn }
{ TODO -cTesting : Check the implemetation for any further version of compiler }
const
AddByte = $04244483; // opcode for ADD DWORD PTR [ESP+4], Shortint
AddLong = $04244481; // opcode for ADD DWORD PTR [ESP+4], Longint
type
PAdjustSelfThunk = ^TAdjustSelfThunk;
TAdjustSelfThunk = packed record
case AddInstruction: Longint of
AddByte: (AdjustmentByte: ShortInt);
AddLong: (AdjustmentLong: Longint);
end;
PInterfaceMT = ^TInterfaceMT;
TInterfaceMT = packed record
QueryInterfaceThunk: PAdjustSelfThunk;
end;
TInterfaceRef = ^PInterfaceMT;
var
QueryInterfaceThunk: PAdjustSelfThunk;
begin
try
Result := Pointer(I);
if Assigned(Result) then
begin
QueryInterfaceThunk := TInterfaceRef(I)^.QueryInterfaceThunk;
case QueryInterfaceThunk.AddInstruction of
AddByte:
Inc(PChar(Result), QueryInterfaceThunk.AdjustmentByte);
AddLong:
Inc(PChar(Result), QueryInterfaceThunk.AdjustmentLong);
else
Result := nil;
end;
end;
except
Result := nil;
end;
end;
//=== Numeric formatting routines ============================================
function IntToStrZeroPad(Value, Count: Integer): AnsiString;
begin
Result := IntToStr(Value);
if Length(Result) < Count then
Result := StrFillChar('0', Count - Length(Result)) + Result;
end;
//=== { TJclNumericFormat } ==================================================
{ TODO -cHelp : Author: Robert Rossmair }
{ Digit: converts a digit value (number) to a digit (char)
DigitValue: converts a digit (char) into a number (digit value)
IntToStr,
FloatToStr,
FloatToHTML: converts a numeric value to a base <Base> numeric representation with formating options
StrToIn: converts a base <Base> numeric representation into an integer, if possible
GetMantisseExponent: similar to AsString, but returns the Exponent separately as an integer
}
const
{$IFDEF MATH_EXTENDED_PRECISION}
BinaryPrecision = 64;
{$ENDIF MATH_EXTENDED_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
BinaryPrecision = 53;
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_SINGLE_PRECISION}
BinaryPrecision = 24;
{$ENDIF MATH_SINGLE_PRECISION}
constructor TJclNumericFormat.Create;
begin
inherited Create;
{ TODO : Initialize, when possible, from locale info }
FBase := 10;
FExpDivision := 1;
SetPrecision(6);
FNumberOfFractionalDigits := BinaryPrecision;
FSignChars[False] := '-';
FSignChars[True] := '+';
FPaddingChar := ' ';
FMultiplier := '
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -