Componentes.Terceros.jvcl/official/3.00/run/JvVCL5Utils.pas

913 lines
25 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvTD5Compat.pas, released on 2005-05-23.
The Initial Developer of the Original Code is Andreas Hausladen <Andreas dott Hausladen att gmx dott de>
Portions created by Andreas Hausladen are Copyright (C) 2005 Andreas Hausladen.
All Rights Reserved.
Contributor(s):
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvVCL5Utils.pas,v 1.9 2005/11/22 13:40:50 ahuser Exp $
unit JvVCL5Utils;
{$I jvcl.inc}
interface
{$IFDEF COMPILER5}
uses
Windows, SysUtils, Classes, TypInfo, ActiveX, MultiMon, Forms, Controls,
Graphics, ImgList, WinInet;
// Classes
type
TInterfacedPersistent = class(TPersistent);
TCollectionNotification = (cnAdded, cnExtracting, cnDeleting);
TCollection = class(Classes.TCollection)
// warning: DO NOT ADD FIELDS !!!
private
function GetNextID: Integer;
protected
procedure Added(var Item: TCollectionItem); virtual; {deprecated;}
procedure Deleting(Item: TCollectionItem); virtual; {deprecated;}
property NextID: Integer read GetNextID;
procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); virtual;
procedure SetItemName(Item: TCollectionItem); override;
procedure Update(Item: TCollectionItem); override;
public
constructor Create(ItemClass: TCollectionItemClass);
function Owner: TPersistent;
procedure Delete(Index: Integer);
end;
TOwnedCollection = class(Classes.TOwnedCollection)
// warning: DO NOT ADD FIELDS !!!
private
function GetNextID: Integer;
protected
procedure Added(var Item: TCollectionItem); virtual; {deprecated;}
procedure Deleting(Item: TCollectionItem); virtual; {deprecated;}
property NextID: Integer read GetNextID;
procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); virtual;
procedure SetItemName(Item: Classes.TCollectionItem); override;
public
constructor Create(AOwner: TPersistent; ItemClass: TCollectionItemClass);
function Owner: TPersistent;
procedure Delete(Index: Integer);
end;
function GetRelocAddress(ProcAddress: Pointer): Pointer;
function InstallProcHook(ProcAddress, HookProc, OrgCallProc: Pointer): Boolean;
function UninstallProcHook(OrgCallProc: Pointer): Boolean;
function AllocateHWnd(Method: TWndMethod): HWND;
procedure DeallocateHWnd(Wnd: HWND);
// SysUtils
const
PathDelim = '\';
DriveDelim = ';';
sLineBreak = #13#10;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
function TryStrToDateTime(const S: string; out Date: TDateTime): Boolean;
function StrToDateTimeDef(const S: string; Default: TDateTime): TDateTime;
function StrToDateDef(const S: string; Default: TDateTime): TDateTime;
// function StrToFloatDef(const Str: string; Default: Extended): Extended;
procedure RaiseLastOSError;
function IncludeTrailingPathDelimiter(const APath: string): string;
function ExcludeTrailingPathDelimiter(const APath: string): string;
function DirectoryExists(const Name: string): Boolean;
function ForceDirectories(Dir: string): Boolean;
function SameFileName(const FN1, FN2: string): Boolean;
function GetEnvironmentVariable(const Name: string): string;
function Supports(Instance: TObject; const Intf: TGUID): Boolean; overload;
function Supports(AClass: TClass; const Intf: TGUID): Boolean; overload;
function FileIsReadOnly(const FileName: string): Boolean;
function WideCompareText(const S1, S2: WideString): Integer;
function WideUpperCase(const S: WideString): WideString;
function WideLowerCase(const S: WideString): WideString;
function CompareDateTime(const A, B: TDateTime): Integer;
// StrUtils
function AnsiStartsText(const SubText, Text: string): Boolean;
function AnsiEndsText(const SubText, Text: string): Boolean;
function AnsiStartsStr(const SubStr, Str: string): Boolean;
function AnsiEndsStr(const SubStr, Str: string): Boolean;
// Math
type
TValueSign = -1..1;
const
NegativeValue = Low(TValueSign);
ZeroValue = 0;
PositiveValue = High(TValueSign);
function Sign(const AValue: Integer): TValueSign; overload;
function Sign(const AValue: Int64): TValueSign; overload;
function Sign(const AValue: Double): TValueSign; overload;
// Variants
function FindVarData(const V: Variant): PVarData;
function VarIsStr(const V: Variant): Boolean;
function VarIsType(const V: Variant; AVarType: TVarType): Boolean;
// Misc
function GetMonitorWorkareaRect(Monitor: TMonitor): TRect;
type
UTF8String = type string;
// System
type
TVarType = Word;
PPointer = ^Pointer;
// Controls
type
TTime = type TDateTime;
{$EXTERNALSYM TTime}
TDate = type TDateTime;
{$EXTERNALSYM TDate}
// Controls
// obones 2005/10/30: Commented out as it clashes in C++ Builder 5 at least.
// Symptoms are a message saying "Cannot load JvStdCtrlsC5D, a class named
// 'TCustomImageList' is already registered.".
// As it seems no one is using the new Draw method, there is no harm done.
{type
TCustomImageList = class(ImgList.TCustomImageList)
// warning: DO NOT ADD FIELDS !!!
public
procedure Draw(Canvas: TCanvas; X, Y, Index: Integer;
ADrawingStyle: TDrawingStyle; AImageType: TImageType;
Enabled: Boolean); overload;
end;}
// Grid
type
TEditStyle = (esSimple, esEllipsis, esPickList);
// DateUtils
function IncYear(const AValue: TDateTime; const ANumberOfYears: Integer): TDateTime;
// Graphics
const
clCream = TColor($F0FBFF);
// WinInet
function FtpGetFileSize(hFile: HINTERNET; lpdwFileSizeHigh: LPDWORD): DWORD; stdcall;
{$ENDIF COMPILER5}
{$IFNDEF COMPILER7_UP}
// For Delphi 5 / BCB 5 and those who will not install Delphi 6 Update 2
// Windows
const
{$EXTERNALSYM SPI_GETMENUSHOWDELAY}
SPI_GETMENUSHOWDELAY = 106;
{$EXTERNALSYM SPI_SETMENUSHOWDELAY}
SPI_SETMENUSHOWDELAY = 107;
{$EXTERNALSYM SPI_GETMENUFADE}
SPI_GETMENUFADE = $1012;
{$EXTERNALSYM SPI_SETMENUFADE}
SPI_SETMENUFADE = $1013;
{$EXTERNALSYM SPI_GETSELECTIONFADE}
SPI_GETSELECTIONFADE = $1014;
{$EXTERNALSYM SPI_SETSELECTIONFADE}
SPI_SETSELECTIONFADE = $1015;
{$EXTERNALSYM SPI_GETTOOLTIPANIMATION}
SPI_GETTOOLTIPANIMATION = $1016;
{$EXTERNALSYM SPI_SETTOOLTIPANIMATION}
SPI_SETTOOLTIPANIMATION = $1017;
{$EXTERNALSYM SPI_GETTOOLTIPFADE}
SPI_GETTOOLTIPFADE = $1018;
{$EXTERNALSYM SPI_SETTOOLTIPFADE}
SPI_SETTOOLTIPFADE = $1019;
{$EXTERNALSYM SPI_GETCURSORSHADOW}
SPI_GETCURSORSHADOW = $101A;
{$EXTERNALSYM SPI_SETCURSORSHADOW}
SPI_SETCURSORSHADOW = $101B;
{$EXTERNALSYM SPI_GETUIEFFECTS}
SPI_GETUIEFFECTS = $103E;
{$EXTERNALSYM SPI_SETUIEFFECTS}
SPI_SETUIEFFECTS = $103F;
{$EXTERNALSYM COLOR_MENUHILIGHT}
COLOR_MENUHILIGHT = 29;
{$EXTERNALSYM COLOR_MENUBAR}
COLOR_MENUBAR = 30;
{$EXTERNALSYM SPI_GETKEYBOARDCUES}
SPI_GETKEYBOARDCUES = $100A;
{$EXTERNALSYM SPI_SETKEYBOARDCUES}
SPI_SETKEYBOARDCUES = $100B;
{$ENDIF !COMPILER7_UP}
implementation
{$IFDEF COMPILER5}
uses
CommCtrl;
var
GlobalCollectionHooked: Boolean = False;
type
TPrivateCollection = class(TPersistent)
public
FItemClass: TCollectionItemClass;
FItems: TList;
FUpdateCount: Integer;
FNextID: Integer; // <-- we are interested in this field
end;
TPrivateCollectionItem = class(TPersistent)
public
FCollection: TCollection;
end;
TPublishedCollectionItem = class(TCollectionItem)
published
property Collection;
end;
procedure OrgTCollection_Delete(Self: Classes.TCollection; Index: Integer);
asm
DD 0, 0, 0, 0 // 16 Bytes
end;
procedure OrgTCollectionItem_SetCollection(Self: TCollectionItem; Value: Classes.TCollection);
asm
DD 0, 0, 0, 0 // 16 Bytes
end;
procedure TCollectionItem_SetCollection(Self: TCollectionItem; Value: Classes.TCollection);
var
Col: Classes.TCollection;
begin
Col := TPrivateCollectionItem(Self).FCollection;
if Col <> Value then
begin
if Col <> nil then
begin
if Col is TCollection then
TCollection(Col).Notify(Self, cnExtracting)
else
if Col is TOwnedCollection then
TOwnedCollection(Col).Notify(Self, cnExtracting);
end;
OrgTCollectionItem_SetCollection(Self, Value);
end;
end;
procedure TCollection_Delete(Self: Classes.TCollection; Index: Integer);
begin
if Self is TOwnedCollection then
TOwnedCollection(Self).Notify(Self.Items[Index], cnDeleting)
else
if Self is TCollection then
TCollection(Self).Notify(Self.Items[Index], cnDeleting);
TCollectionItem(Self.Items[Index]).Free;
end;
procedure HookCollection;
var
Info: PPropInfo;
begin
if not GlobalCollectionHooked then
begin
GlobalCollectionHooked := True;
InstallProcHook(@Classes.TCollection.Delete, @TCollection_Delete, @OrgTCollection_Delete);
Info := GetPropInfo(TPublishedCollectionItem, 'Collection');
InstallProcHook(Info.SetProc, @TCollectionItem_SetCollection, @OrgTCollectionItem_SetCollection);
end;
end;
procedure UnhookCollection;
begin
if GlobalCollectionHooked then
begin
GlobalCollectionHooked := False;
UninstallProcHook(@OrgTCollection_Delete);
UninstallProcHook(@OrgTCollectionItem_SetCollection);
end;
end;
//=== { TCollection } ========================================================
constructor TCollection.Create(ItemClass: Classes.TCollectionItemClass);
begin
inherited Create(ItemClass);
if not GlobalCollectionHooked then
HookCollection;
end;
procedure TCollection.Added(var Item: Classes.TCollectionItem);
begin
end;
procedure TCollection.Delete(Index: Integer);
begin
Notify(TCollectionItem(Items[Index]), cnDeleting);
inherited Delete(Index);
end;
procedure TCollection.Deleting(Item: Classes.TCollectionItem);
begin
end;
function TCollection.GetNextID: Integer;
begin
Result := TPrivateCollection(Self).FNextID;
end;
procedure TCollection.Notify(Item: Classes.TCollectionItem; Action: TCollectionNotification);
begin
case Action of
cnAdded:
Added(Item);
cnDeleting:
Deleting(Item);
end;
end;
function TCollection.Owner: TPersistent;
begin
Result := GetOwner;
end;
procedure TCollection.SetItemName(Item: Classes.TCollectionItem);
begin
inherited SetItemName(Item);
Notify(TCollectionItem(Item), cnAdded);
end;
//=== { TOwnedCollection } ===================================================
constructor TOwnedCollection.Create(AOwner: TPersistent; ItemClass: TCollectionItemClass);
begin
inherited Create(AOwner, ItemClass);
if not GlobalCollectionHooked then
HookCollection;
end;
procedure TOwnedCollection.Delete(Index: Integer);
begin
Notify(TCollectionItem(Items[Index]), cnDeleting);
inherited Delete(Index);
end;
procedure TOwnedCollection.Added(var Item: Classes.TCollectionItem);
begin
end;
procedure TOwnedCollection.Deleting(Item: Classes.TCollectionItem);
begin
end;
function TOwnedCollection.GetNextID: Integer;
begin
Result := TPrivateCollection(Self).FNextID;
end;
procedure TOwnedCollection.Notify(Item: Classes.TCollectionItem; Action: TCollectionNotification);
begin
case Action of
cnAdded:
Added(Item);
cnDeleting:
Deleting(Item);
end;
end;
procedure TCollection.Update(Item: TCollectionItem);
begin
inherited Update(Item);
Notify(Item, cnAdded);
end;
function TOwnedCollection.Owner: TPersistent;
begin
Result := GetOwner;
end;
procedure TOwnedCollection.SetItemName(Item: Classes.TCollectionItem);
begin
inherited SetItemName(Item);
Notify(TCollectionItem(Item), cnAdded);
end;
function ReadProtectedMemory(Address: Pointer; var Buffer; Count: Cardinal): Boolean;
var
N: Cardinal;
begin
Result := ReadProcessMemory(GetCurrentProcess, Address, @Buffer, Count, N);
Result := Result and (N = Count);
end;
function WriteProtectedMemory(Address: Pointer; const Buffer; Count: Cardinal): Boolean;
var
N: Cardinal;
begin
Result := WriteProcessMemory(GetCurrentProcess, Address, @Buffer, Count, N);
Result := Result and (N = Count);
end;
type
TJumpCode = packed record
Jmp: Byte; // jmp Offset
Offset: Integer;
end;
TOrgCallCode = packed record
Code: array[0..SizeOf(TJumpCode) + 4] of Byte;
Jmp: Byte; // jmp Offset
Offset: Integer;
Address: Pointer;
end;
function GetRelocAddress(ProcAddress: Pointer): Pointer;
type
TRelocationRec = packed record
Jump: Word;
Address: PPointer;
end;
var
Relocation: TRelocationRec;
Data: Byte;
begin
Result := ProcAddress;
// the relocation table might be protected
if ReadProtectedMemory(ProcAddress, Data, SizeOf(Data)) then
if Data = $FF then // ProcAddress is in a DLL or package
if ReadProtectedMemory(ProcAddress, Relocation, SizeOf(Relocation)) then
Result := Relocation.Address^;
end;
function AllocateHWnd(Method: TWndMethod): HWND;
begin
Result := Forms.AllocateHWnd(Method);
end;
procedure DeallocateHWnd(Wnd: HWND);
begin
Forms.DeallocateHWnd(Wnd);
end;
type
TModRM = record
Mode: Byte;
RegOp: Byte;
RM: Byte;
end;
function GetModRM(B: Byte): TModRM;
begin
Result.Mode := B shr 6;
Result.RegOp := (B shr 3) and $07;
Result.RM := B and $07;
end;
function GetDisassembledByteCount(const Bytes: array of Byte): Integer;
var
I, LastByteCount: Integer;
ModRM: TModRM;
begin
Result := 0;
LastByteCount := 0;
I := 0;
while I < Length(Bytes) do
begin
LastByteCount := Result;
case Bytes[I] of
$53..$56:
; // push reg
$8B, $3B: // mov/cmp
begin
Inc(I);
ModRM := GetModRM(Bytes[I]);
case ModRM.Mode of
$00:
if ModRM.RM = $07 then
Inc(I, 2); // mov reg, disp16
$01:
Inc(I); // mov reg, [reg]+disp8
$02:
Inc(I, 2); // mov reg, [reg]+disp16
end;
end;
$E8:
Inc(I, 4); // call rel32
$5B..$5E:
; // pop reg
$C3:
; // ret
$E9:
Inc(I, 4); // jmp rel32
$83: // add
Inc(I, 2);
$89:
Inc(I, 2);
end;
Inc(I);
Result := I;
end;
if I > Length(Bytes) then
Result := LastByteCount;
end;
function InstallProcHook(ProcAddress, HookProc, OrgCallProc: Pointer): Boolean;
var
Code: TJumpCode;
OrgCallCode: TOrgCallCode;
I, Count: Integer;
begin
ProcAddress := GetRelocAddress(ProcAddress);
Result := False;
if Assigned(ProcAddress) and Assigned(HookProc) then
begin
if OrgCallProc <> nil then
begin
if ReadProtectedMemory(ProcAddress, OrgCallCode, SizeOf(OrgCallCode.Code)) then
begin
Count := GetDisassembledByteCount(OrgCallCode.Code);
for I := Count to SizeOf(OrgCallCode.Code) do
OrgCallCode.Code[I] := $90; // NOP
OrgCallCode.Jmp := $E9;
OrgCallCode.Offset := (Integer(ProcAddress) {+ SizeOf(Code)}+ Count) -
Integer(OrgCallProc) -
(SizeOf(OrgCallCode) - SizeOf(OrgCallCode.Address));
OrgCallCode.Address := ProcAddress;
WriteProtectedMemory(OrgCallProc, OrgCallCode, SizeOf(OrgCallCode));
FlushInstructionCache(GetCurrentProcess, OrgCallProc, SizeOf(OrgCallCode));
end;
end;
Code.Jmp := $E9;
Code.Offset := Integer(HookProc) - (Integer(ProcAddress)) - SizeOf(Code);
{ The strange thing is that something overwrites the $e9 with a "PUSH xxx" }
if WriteProtectedMemory(Pointer(Cardinal(ProcAddress)), Code, SizeOf(Code)) then
begin
FlushInstructionCache(GetCurrentProcess, ProcAddress, SizeOf(Code));
Result := True;
end;
end;
end;
function UninstallProcHook(OrgCallProc: Pointer): Boolean;
var
OrgCallCode: TOrgCallCode;
ProcAddress: Pointer;
begin
Result := False;
if Assigned(OrgCallProc) then
if OrgCallProc <> nil then
if ReadProtectedMemory(OrgCallProc, OrgCallCode, SizeOf(OrgCallCode)) then
begin
ProcAddress := OrgCallCode.Address;
Result := WriteProtectedMemory(ProcAddress, OrgCallCode, SizeOf(TJumpCode));
FlushInstructionCache(GetCurrentProcess, ProcAddress, SizeOf(OrgCallCode));
end;
end;
// SysUtils
function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
function TryStrToDateTime(const S: string; out Date: TDateTime): Boolean;
begin
Result := True;
try
Date := StrToDateTime(S);
except
Result := False;
end;
end;
{ TODO -oJVCL -cTODO : Implement these better for D5! }
function StrToDateTimeDef(const S: string; Default: TDateTime): TDateTime;
begin
// stupid and slow but at least simple
try
Result := StrToDateTime(S);
except
Result := Default;
end;
end;
function StrToDateDef(const S: string; Default: TDateTime): TDateTime;
begin
// stupid and slow but at least simple
try
Result := StrToDate(S);
except
Result := Default;
end;
end;
const
OneMillisecond = 1 / 24 / 60 / 60 / 1000; // as TDateTime
function CompareDateTime(const A, B: TDateTime): Integer;
begin
if Abs(A - B) < OneMillisecond then
Result := 0
else
if A < B then
Result := -1
else
Result := 1;
end;
procedure RaiseLastOSError;
begin
RaiseLastWin32Error;
end;
function IncludeTrailingPathDelimiter(const APath: string): string;
begin
if (APath <> '') and (APath[Length(APath)] <> PathDelim) then
Result := APath + PathDelim
else
Result := APath;
end;
function ExcludeTrailingPathDelimiter(const APath: string): string;
var
I: Integer;
begin
Result := APath;
I := Length(Result);
while (I > 0) and (Result[I] = PathDelim) do
Dec(I);
SetLength(Result, I);
end;
function DirectoryExists(const Name: string): Boolean;
var
Code: Cardinal;
begin
Code := Integer(GetFileAttributes(PChar(Name)));
Result := (Code <> $FFFFFFFF) and (Code and FILE_ATTRIBUTE_DIRECTORY <> 0);
end;
function ForceDirectories(Dir: string): Boolean;
begin
Result := True;
if Dir[Length(Dir)] = PathDelim then
Delete(Dir, Length(Dir), 1);
if (Length(Dir) < 3) or DirectoryExists(Dir) or (ExtractFilePath(Dir) = Dir) then
Exit; { avoid 'xyz:\' problem }
Result := ForceDirectories(ExtractFilePath(Dir));
if Result then
Result := CreateDir(Dir);
end;
function SameFileName(const FN1, FN2: string): Boolean;
begin
Result := CompareText(FN1, FN2) = 0;
end;
function GetEnvironmentVariable(const Name: string): string;
var
Len: Integer;
begin
SetLength(Result, 4 * 1024);
Len := Windows.GetEnvironmentVariable(PChar(Name), PChar(Result), Length(Result));
if Len <= Length(Result) then
SetLength(Result, Len)
else
begin
SetLength(Result, Len - 1);
Windows.GetEnvironmentVariable(PChar(Name), PChar(Result), Len);
end;
end;
function Supports(Instance: TObject; const Intf: TGUID): Boolean;
begin
Result := (Instance <> nil) and (Instance.GetInterfaceEntry(Intf) <> nil);
end;
function Supports(AClass: TClass; const Intf: TGUID): Boolean;
begin
Result := (AClass <> nil) and (AClass.GetInterfaceEntry(Intf) <> nil);
end;
function FileIsReadOnly(const FileName: string): Boolean;
var
Attr: Cardinal;
begin
Attr := GetFileAttributes(PChar(FileName));
Result := (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_READONLY <> 0);
end;
function WideCompareText(const S1, S2: WideString): Integer;
begin
if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
Result := CompareText(string(S1), string(S2))
else
Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
PWideChar(S1), Length(S1), PWideChar(S2), Length(S2)) - 2;
end;
function WideUpperCase(const S: WideString): WideString;
begin
Result := S;
if Result <> '' then
CharUpperBuffW(Pointer(Result), Length(Result));
end;
function WideLowerCase(const S: WideString): WideString;
begin
Result := S;
if Result <> '' then
CharLowerBuffW(Pointer(Result), Length(Result));
end;
// StrUtils
function AnsiStartsText(const SubText, Text: string): Boolean;
var
SubTextLen: Integer;
begin
SubTextLen := Length(SubText);
if SubTextLen > Length(Text) then
Result := False
else
Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
PChar(Text), SubTextLen, PChar(SubText), SubTextLen) = 2;
end;
function AnsiEndsText(const SubText, Text: string): Boolean;
var
SubTextStart: Integer;
begin
SubTextStart := Length(Text) - Length(SubText) + 1;
if (SubTextStart > 0) and (SubText <> '') and (ByteType(Text, SubTextStart) <> mbTrailByte) then
Result := AnsiStrIComp(Pointer(SubText), PChar(Pointer(Text)) + SubTextStart - 1) = 0
else
Result := False;
end;
function AnsiStartsStr(const SubStr, Str: string): Boolean;
var
SubStrLen: Integer;
begin
SubStrLen := Length(SubStr);
if SubStrLen > Length(Str) then
Result := False
else
Result := CompareString(LOCALE_USER_DEFAULT, 0,
PChar(Str), SubStrLen, PChar(SubStr), SubStrLen) = 2;
end;
function AnsiEndsStr(const SubStr, Str: string): Boolean;
var
SubStrStart: Integer;
begin
SubStrStart := Length(Str) - Length(SubStr) + 1;
if (SubStrStart > 0) and (SubStr <> '') and (ByteType(Str, SubStrStart) <> mbTrailByte) then
Result := AnsiStrComp(Pointer(SubStr), PChar(Pointer(Str)) + SubStrStart - 1) = 0
else
Result := False;
end;
// Math
function Sign(const AValue: Integer): TValueSign;
begin
if AValue < 0 then
Result := NegativeValue
else
if AValue > 0 then
Result := PositiveValue
else
Result := ZeroValue;
end;
function Sign(const AValue: Int64): TValueSign;
begin
if AValue < 0 then
Result := NegativeValue
else
if AValue > 0 then
Result := PositiveValue
else
Result := ZeroValue;
end;
function Sign(const AValue: Double): TValueSign;
begin
if (PInt64(@AValue)^ and $7FFFFFFFFFFFFFFF) = $0000000000000000 then
Result := ZeroValue
else
if (PInt64(@AValue)^ and $8000000000000000) = $8000000000000000 then
Result := NegativeValue
else
Result := PositiveValue;
end;
// Variants
function VarIsStr(const V: Variant): Boolean;
var
VarType: TVarType;
VarData: PVarData;
begin
VarData := @TVarData(V);
while VarData.VType = varByRef or varVariant do
VarData := PVarData(VarData.VPointer);
VarType := VarData^.VType;
Result := (VarType = varOleStr) or (VarType = varString);
end;
function FindVarData(const V: Variant): PVarData;
begin
Result := @TVarData(V);
while Result.VType = varByRef or varVariant do
Result := PVarData(Result.VPointer);
end;
function VarIsType(const V: Variant; AVarType: TVarType): Boolean;
begin
Result := FindVarData(V)^.VType = AVarType;
end;
function GetMonitorWorkareaRect(Monitor: TMonitor): TRect;
var
MonInfo: TMonitorInfo;
begin
MonInfo.cbSize := SizeOf(MonInfo);
GetMonitorInfo(Monitor.Handle, @MonInfo);
Result := MonInfo.rcWork;
end;
//=== { TCustomImageList } ===================================================
{procedure TCustomImageList.Draw(Canvas: TCanvas; X, Y, Index: Integer;
ADrawingStyle: TDrawingStyle; AImageType: TImageType; Enabled: Boolean);
const
DrawingStyles: array[TDrawingStyle] of Longint =
(ILD_FOCUS, ILD_SELECTED, ILD_NORMAL, ILD_TRANSPARENT);
Images: array[TImageType] of Longint =
(0, ILD_MASK);
begin
if HandleAllocated then
DoDraw(Index, Canvas, X, Y, DrawingStyles[ADrawingStyle] or
Images[AImageType], Enabled);
end;}
function IncYear(const AValue: TDateTime;
const ANumberOfYears: Integer): TDateTime;
begin
Result := IncMonth(AValue, ANumberOfYears * 12);
end;
function FtpGetFileSize(hFile: HINTERNET; lpdwFileSizeHigh: LPDWORD): DWORD; stdcall;
external 'wininet.dll' name 'FtpGetFileSize';
initialization
finalization
if GlobalCollectionHooked then
UnhookCollection;
{$ENDIF COMPILER5}
end.