unit magsubs1;
{$IFNDEF VER140}
{$WARN UNSAFE_TYPE off}
{$WARN UNSAFE_CAST off}
{$WARN UNSAFE_CODE off}
{$ENDIF}
{$WARN SYMBOL_PLATFORM OFF}
{$WARN SYMBOL_LIBRARY OFF}
{$WARN SYMBOL_DEPRECATED OFF}
{ Magenta Systems File Transfer Components.
Updated by Angus Robertson, Magenta Systems Ltd, England, 6th July 2021
delphi@magsys.co.uk, https://www.magsys.co.uk/delphi/
Copyright Magenta Systems Ltd
This unit contains a wide range of general purpose functions, some written by
Magenta, some contributed by others but often improved
Thanks to Enzo from iMazzo for MSIEAutoDial improvements
WARNING - Delphi 7 and later only, supports Delphi 2009 and later with Unicode
1st May 2001, Release 4.60
26th June 2001, Release 4.61 - added GetMACAddresses
11th October 2001, Release 4.70 - added IsDestReachable and IsNetAlive
added MSIEAutoDial and MSIEDefConn
added AddThouSeps, IntToCStr, Int64ToCStr
31st October 2001, Release 4.71 - added DirectoryExists and ForceDirs
6th Dec 2001 - Release 4.72 - added more GetShellPath literals
1st January 2002, Release 4.72 - added ExcludeTrailingPathDelimiter for D4
26th May 2002 - Release 4.80 - moved MagRasOSVersion here from magrasapi
corrected MSIEDefConn to work with Windows XP
26th July 2002 - Release 4.90
5 Jan 2004 - Release 4.93 added from magsubs4 to support magcopy, magftp, maghttp
11 Jan 2004 - Release 4.93 made Delphi 5 compatible
22 Jan 2004 - added DTtoAlpha, DTTtoAlpha, DateOnlyPacked
30 Jan 2004 - added MaxLongWrd
6 Feb 2004 - PackedDTtoISODT handles dates only, added SecsToHourStr
1 Apr 2004 - added StrCtrlSafe, StrCtrlRest, UnxToDosPath, DosToUnxPath, StrFileTran
31 May 2004 - added GetSize64File, corrected files over 4 gigs not reported correctly
1 Oct 2004 - messing with FileTimeToDateTime to fix 12/00 bug
- added CheckFileOpen
11 Oct 2004 - Release 4.94 added MSIEAutoDialOpt, fixed MSIEDefConn for XP
25 Nov 2004 - added FormatLastError, IsWinXPE, IsWin2K3
13 Dec 2004 - added CRLF_
14 Jan 2004 - added Int2Kbytes, Int2Mbytes, IntToKbyte from magsubs4
28 Jan 2005 - added EmptyRecycleBin, NULLL to avoid conflict with variants
14 Feb 2005 - more error handling in IndexFiles
22 Mar 2005 - added ULINE, TrimWorkingSetMemory
18 Apr 2005 - added GetTickCountX, DiffTicks, ElapsedTicks, ElapsedSecs, GetTrgMsecs, GetTrgSecs, TestTrgTick
6 May 2005 - added TimeToNStr always numeric hh:mm:ss, and TimeToZStr hh:mm:ss:zzz
added DateToAStr/DateTimeToAStr always alpha month and numeric hh:mm:ss
25 July 2005 - TFindList moved to magclasses.pas
9 Aug 2005 - added IsWinVista, return Windows Vista for NT6
4 Sept 2005 - better 64-bit file size conversion
21 Sept 2005 - added EqualDateTime and DiffDateTime
19 Oct 2005 - fix in ElapsedTicks for zero elapsed
18 Nov 2005 - added FreeAndNilEx
19 Feb 2006 - MagRasOSVersion returns OSVista
21 Mar 2006 - added STX and ETX, StrArrayToMultiSZ, StrArrayFromMultiSZ, StringRemCntlsEx
11 Apr 2006 - added AddTrgMsecs, StringFileTranEx, StrFileTranEx
12 May 2006 - added new CSIDLs for Vista
29 May 2006 - added GetTrgMins, ElapsedMins, WaitingSecs, AddTrgSecs, GetYN
11 June 2006 - added GetUTCTime, DT2ISODT
26 June 2006 - added TruncateFile
14 July 2006 - added MaxInt64
9 August 2006 - made some literals conditional for Delphi to avoid upsetting BCB
23 Aug 2006 - AddThouSeps handles negative values, added Year2030DT
27 Nov 2006 - added PerfCountToSecs, PerfCountGetSecs
19 Feb 2007 - added SecsPerHour
18 Apr 2007 - added StrArrayPosOfEx
4 May 2007 - added TDTtoStamp
18 May 2007 - GetOSVersion supports Vista versions, 2008 (Longhorn) and more XP/2003 versions
- added IsWin2K8 (2008), corrected IsWinVista, moved IsProgAdmin here from magsubs4
24 May 2007 - updated MSIEDefConn for Vista
22 June 2007 - treat negative durations as immediate in GetTrgXXX
11 Apr 2008 - added GetYesNo
11 Aug 2008 - made functions compatible with unicode strings in Delphi 2009
note: before Delphi 2009 String=AnsiString, now String=UnicodeString (not WideString)
now using wide versions of Windows APIs and letting Delphi convert to string
added IsSpace, IsLetterOrDigit, IsPathSep
added GetDevNamePortW and FixedToPasStrW
MSIEAutoDialOpt and MSIEAutoDial should not give exception if reg values missing
added TrimAnsi, TrimLeftAnsi, TrimRightAnsi, PosAnsi
added CompareTextAnsi, LowerCaseAnsi, UpperCaseAnsi
added IntToStrAnsi, IntToHexAnsi, StringTranChAnsi, StripCharsAnsi, StripCharAnsi
added StrLenWide, StrLCopyWide, StrPLCopyWide
15 Aug 2008 - added overloaded PWideChar versions of StrArrayToMultiSZ, StrArrayFromMultiSZ
fixed StrArraySplit with multichar Delimiter missing start of first string and for Unicode
30 Aug 2008 - fixed StrArrayFromMultiSZ stopping on first blank string
fixed some APIs returning buffer length including terminating null
22 Sept 2008 - added UnicodeString/RawByteString for D2007 and earlier
added WideString GetUAgeSizeFileW and similar functions
added UnixToDosPathW, DosToUnixPathW and StringTranChWide, sysTempPathWide
added UnicodeStringArray, WideStringArray and overload versions of most StrArray functions
added StrArrayFindSorted, StrArrayAddSorted
added overloaded StrArray functions with var Total so arrays can be larger than actually uses
TruncateFile now UnicodeString
6 Oct 2008 - added GetLcTypeInfo
13 Oct 2008 - added StringRemCntlsW
4 Nov 2008 - recognise Windows 7
22 Jan 2009 - recognise Windows 2008 R2
18 Feb 2009 - added UpdateFileAge and UpdateUFileAge, GetUnixTime
added GetLocalBiasUTC, DateTimeToUTC, UTCToLocalDT, SetUTCTime
fixed GetTickCountX to never return TriggerDisabled or TriggerImmediate
25 Mar 2008 - added CheckYesNo, GetTrueFalse, CheckTrueFalse
5 June 2009 - added DateMmmMask
22 Oct 2009 - added CSIDL_xxX86 literals
13 Nov 2009 - fixed UpAndLower to use () again (lost in unicode change)
14 Dec 2009 - added IsWow64, called by GetOSVersion
6 May 2010 - added DTtoLongAlpha (1st January 2010)
22 June 2010 - added EscapeBackslashes
16 Aug 2010 - various cast fixed for D2009 and later
updated GetOSVersion to use GetProductInfo for edition with Vista and later
3 Aug 2011 - recognise Windows 8
no longer using ASM for 64-bit compiler, Match and PosN not on 64-bit
added IsWin64, GetOSVersion shows Win64 instead of 64-bit for native 64-bit
16 Aug 2011 - fixed GetFileVerInfo to support zero langid
20 Oct 2011 - IntToKbyte has new argument to added bytes suffix
18 Nov 2011 - added PosRev and BOM_UTF8/16 literals
11 Jul 2012 - recognise Windows Server 2012, more products for Windows 8, MagRasOSVersion returns OS7 and OS8
10 Sep 2012 - added FormatIpAddr, FormatIpAddrPort and StripIpAddr to add and remove [] from IPv6 addresses
added MyFormatSettings to replace formatting public vars removed in XE3
more error handling in MSIEAutoDialOpt which has trouble reading some registry keys
24 Oct 2012 - UpAndLower recognise ' for O'Neil but not Fred's
3 Apr 2013 - recognise Windows 8.1 aka Windows Blue (provisionally)
17 Apr 2013 - PackedISO2Date now handles time only
20 May 2013 - added DisableWow64Redir and RevertWow64Redir for Win32 apps on Win64 OS to disable WOW64 file system redirection
13 Jun 2013 - added externalsym to some constants to support BCB, thanks to Peter Johansson
10 Mar 2014 - correct GetOSInfo to return Windows 8.1 even without manifest saying 8.1 compatible
added IsTouchTablet
7 Apr 2014 - added NullTermToPasStr
26 May 2014 - added SetThreadExecutionState
3 Oct 2014 - updated GetOSInfo/GetOSVersion to report Windows 10 and 2016, and get correct build for 8.1 and later
added OsInfoRaw with reported OS version, OsInfo has corrected version, set during unit initilisation
17 Apr 2015 - renamed TOSVersion to TMagOSVersion to avoid conflict with newer Delphi versions
- corrected Windows 10 version display, now 10.0 and not 6.4
24 July 2015 - corrected Windows Server 2014 to 2016 (tech preview 2 was 10.0.10074)
11 Aug 2015 - updated GetOSInfo/GetOSVersion to use RtlGetVersion which gets correct OS without needing a manifest
20 Jun 2016 - added Year2099DT and Year3000DT
5 Mar 2017 - changed TULargeInteger to ULARGE_INTEGER to keep modern compilers happy
23 Nov 2017 - updated GetOSInfo/GetOSVersion with more Windows 10 product versions and releaseid
added MagGetRegHlmStr
3 Jan 2018 - MagRasOSVersion updated for Windows 10
5 Nov 2018 - updated GetOSInfo/GetOSVersion with Windows 2019 (not tested since unavailable)
3 Dec 2020 - UrlEncode/Decode now MagUrlEncode to avoid conflicts.
6 Jul 2021 - updated GetOSInfo/GetOSVersion with Windows 11/2022
}
(* example manifest file that displays OS versions correctly with GetVersionExW
My application description
*)
interface
uses
Sysutils, Windows, Messages, Classes, ShellAPI, nb30, Registry, StrUtils,
DateUtils {$IFDEF CPUX64}, WideStrUtils {$ENDIF} ;
{$R-} { no range checking, otherwise DWORD=Integer fails with some Windows APIs }
// 8 Sept 2008, simulate D2009 strings in earlier compilers
{$IFNDEF UNICODE}
type
UnicodeString = WideString;
RawByteString = AnsiString;
{$ENDIF}
const
MaxByte: Byte = 255;
MaxShortInt: ShortInt = 127;
MaxWord: Word = 65535;
MaxTriplet: LongInt = $FFFFFF ;
MaxLongInt: LongInt = $7FFFFFFF; // 2147483647
MaxInteger = $7FFFFFFF;
MaxLongWord: LongWord = $FFFFFFFF; // 4294967295
MaxLongWrd = $FFFFFFFF;
MaxInt64: int64 = $7FFFFFFFFFFFFFFF ;
// MaxReal: Real = 1.7e38;
// MaxSingle: Single = 3.4e38;
// MaxDouble: Double = 1.7e308;
// MaxExtended: Extended = 1.1e4932;
// MaxComp: Comp = 9.2e18;
MinByte: Byte = 0;
MinShortInt: ShortInt = -128;
MinInt: Integer = -32768;
MinWord: Word = 0;
MinLongInt = $80000000;
// MinReal: Real = 2.9e-39;
// MinSingle: Single = 1.5e-45;
// MinDouble: Double = 5.0e-324;
// MinExtended: Extended = 3.4e-4932;
const
{ several important ASCII codes }
{$IFNDEF BCB}
NULL = #0;
STX = #2;
ETX = #3;
EOT = #4;
{$ENDIF}
NULLL = #0;
BACKSPACE = #8;
TAB = #9;
LF = #10;
FF = #12;
CR = #13;
EOF_ = #26;
ESC = #27;
FIELDSEP = #28;
RECSEP = #30;
BLANK = #32;
SQUOTE = #39 ;
DQUOTE = #34 ;
SPACE = BLANK;
SLASH = '\'; { used in filenames }
BSLASH = '\'; { used in filenames }
HEX_PREFIX = '$'; { prefix for hexnumbers }
COLON = ':';
FSLASH = '/';
COMMA = ',';
PERIOD = '.';
ULINE = '_';
CRLF : PAnsiChar = CR+LF;
CRLF_ = CR+LF;
UNICODESIG : PChar = #255 + #254 ;
ASCII_NULL = #0;
ASCII_BELL = #7;
ASCII_BS = #8;
ASCII_HT = #9;
ASCII_LF = #10;
ASCII_CR = #13;
ASCII_EOF = #26;
ASCII_ESC = #27;
ASCII_SP = #32;
c_Tab = ASCII_HT;
c_Space = ASCII_SP;
c_EOL = ASCII_CR + ASCII_LF;
c_DecimalPoint = '.';
{ digits as chars }
ZERO = '0'; ONE = '1'; TWO = '2'; THREE = '3'; FOUR = '4';
FIVE = '5'; SIX = '6'; SEVEN = '7'; EIGHT = '8'; NINE = '9';
{ special codes }
{ common computer sizes }
KBYTE = Sizeof(Byte) shl 10;
MBYTE = KBYTE shl 10;
GBYTE = MBYTE shl 10;
DIGITS : set of AnsiChar = [ZERO..NINE];
NumPadCh = #32 ; // Character to use for Left Hand Padding of Numerics - blank
MinsPerDay = SecsPerDay / 60 ;
SecsPerHour = SecsPerDay / 24 ;
OneSecond: TDateTime = 1 / SecsPerDay ;
OneMinute: TDateTime = 1 / (SecsPerDay / 60) ;
OneHour: TDateTime = 1 / (SecsPerDay / (60 * 60)) ;
FileTimeBase = -109205.0; // days between years 1601 and 1900
FileTimeStep: Extended = 24.0 * 60.0 * 60.0 * 1000.0 * 1000.0 * 10.0; // 100 nsec per Day
FileTimeSecond: int64 = 10000000 ;
FileTime1980: int64 = 119600064000000000 ;
FileTime1990: int64 = 122756256000000000 ;
FileTime2000: int64 = 125911584000000000 ;
TicksPerDay: longword = 24 * 60 * 60 * 1000 ;
TicksPerHour: longword = 60 * 60 * 1000 ;
TicksPerMinute: longword = 60 * 1000 ;
TicksPerSecond: longword = 1000 ;
TriggerDisabled: longword = MaxLongWrd ;
TriggerImmediate: longword = 0 ;
Year2030DT = 47484 ;
Year2099DT = 73050 ; // 31/12/2099 - added June 2016
Year3000DT = 73051 ; // 01/01/3000 - added June 2016
UnixStartDate: TDateTime = 25569.0; // 1 Jan 1970
DateOnlyPacked = 'yyyymmdd' ;
DateMaskPacked = 'yyyymmdd"-"hhnnss' ;
DateMaskXPacked = 'yyyymmdd"-"hhnnss"-"zzz' ;
TimeMaskPacked = 'hhnnss' ;
ISODateMask = 'yyyy-mm-dd' ;
ISODateTimeMask = 'yyyy-mm-dd"T"hh:nn:ss' ;
ISODateLongTimeMask = 'yyyy-mm-dd"T"hh:nn:ss.zzz' ;
ISOTimeMask = 'hh:nn:ss' ;
LongTimeMask = 'hh:nn:ss:zzz' ;
FullDateTimeMask = 'yyyy/mm/dd"-"hh:nn:ss' ;
DateAlphaMask = 'dd-mmm-yyyy' ;
ShortTimeMask = 'hh:nn' ;
SDateMaskPacked = 'yymmddhhnnss' ;
DateTimeAlphaMask = 'dd-mmm-yyyy hh:nn:ss' ;
DateMmmMask = 'dd mmm yyyy' ;
// SQL paramater constants
paramY = SQUOTE + 'Y' + SQUOTE {+ SPACE} ;
paramN = SQUOTE + 'N' + SQUOTE {+ SPACE} ;
paramBlank = SQUOTE + SQUOTE ;
paramSep = ',' ;
paramNull = 'NULL' ;
// BOM prefixes for Unicode files and streams
BOM_UTF8: array [0..2] of Byte = ($EF, $BB, $BF) ;
BOM_UTF16: array [0..1] of Byte = ($FF, $EF) ;
BOM_UTF16Be: array [0..1] of Byte = ($EF, $FF) ;
type
CharSet = Set of AnsiChar;
CharSetArray = array of CharSet;
StringArray = array of string;
T2DimStrArray = array of array of string ;
TIntegerArray = array of integer ;
WideStringArray = array of WideString; // 10 Sept 2009
UnicodeStringArray = array of UnicodeString; // 10 Sept 2009
const
// file type extensions - should be in windows.pas, but missing
// {$xFNDEF BCB} 13 June 2013, added externalsym below to support BCB
FILE_ATTRIBUTE_DEVICE = $00000040 ; // old encrypt
{$EXTERNALSYM FILE_ATTRIBUTE_DEVICE}
FILE_ATTRIBUTE_SPARSE_FILE = $00000200 ; // file is missing records
{$EXTERNALSYM FILE_ATTRIBUTE_SPARSE_FILE}
FILE_ATTRIBUTE_REPARSE_POINT = $00000400 ; // attached function??
{$EXTERNALSYM FILE_ATTRIBUTE_REPARSE_POINT}
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $00002000 ;
{$EXTERNALSYM FILE_ATTRIBUTE_NOT_CONTENT_INDEXED}
FILE_ATTRIBUTE_ENCRYPTED = $00004000 ; // W2K encrypt
{$EXTERNALSYM FILE_ATTRIBUTE_ENCRYPTED}
//{$xENDIF}
faNormal = FILE_ATTRIBUTE_COMPRESSED OR FILE_ATTRIBUTE_NORMAL OR
FILE_ATTRIBUTE_ENCRYPTED OR FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ; // NTFS
faNormArch = faNormal OR faArchive ;
// SENS Connectivity APIs from sensapi.h - MSIE5 and later only
const
SensapiDLL = 'SENSAPI.DLL' ;
{ Literals for IsDestReachable, values for QocInfo.dwFlags. }
NETWORK_ALIVE_LAN = $00000001 ;
NETWORK_ALIVE_WAN = $00000002 ;
NETWORK_ALIVE_AOL = $00000004 ;
{ Structure for IsDestReachable, dwFlags }
type
PQocInfo = ^TQocInfo;
TQocInfo = record
dwSize: DWORD;
dwFlags: DWORD;
dwInSpeed: DWORD;
dwOutSpeed: DWORD;
end;
var
MyFormatSettings: TFormatSettings; // 3 Sept 2012
// performance counter and NowPC stuff
var
PerfFreqCountsPerSec: int64 ;
f_PCStartValue: int64 ;
f_TDStartValue: TDateTime ;
f_PCCountsPerDay: extended ;
PerfFreqAligned: boolean = False ; // clear if clock changes
TicksTestOffset: longword ; // 18 Apr 2005, for testing GetTickCount
// functions exported by this unit
function TrimAnsi(const S: AnsiString): Ansistring;
function TrimLeftAnsi(const S: AnsiString): AnsiString;
function TrimRightAnsi(const S: Ansistring): AnsiString;
function CompareTextAnsi(const S1, S2: AnsiString): Integer;
function LowerCaseAnsi(const S: AnsiString): AnsiString;
function UpperCaseAnsi(const S: AnsiString): AnsiString;
function IntToStrAnsi(N : Integer) : AnsiString;
function IntToHexAnsi(N : Integer; Digits: Byte) : AnsiString;
function PosAnsi(const Substr, S: AnsiString): Integer;
{ WideString versions of StrLen and StrCopy missing from Delphi 7 }
function StrLenWide(const Str: PWideChar): Cardinal;
function StrLCopyWide(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar;
function StrPLCopyWide(Dest: PWideChar; const Source: String; MaxLen: Cardinal): PWideChar;
{ Converts a fixed length PAnsiChar string into a Delphi ANSI string, leaving
any embedded or trailing nulls. }
function FixedToPasStr (fixstr: PAnsiChar; fixsize: integer): AnsiString ;
function FixedToPasStrW (fixstr: PWideChar; fixlen: integer): UnicodeString ;
{ gets a null terminated string from within a Delphi string }
function NullTermToPasStr (const nullstr: AnsiString): AnsiString ;
{ Similar to FixedToPasStr, but specialised to split a PChar string into
two Delphi ANSI strings at the first embedded null. }
function GetDevNamePort (fixstr: PAnsiChar; fixsize: integer;
var devport: AnsiString): AnsiString ;
function GetDevNamePortW (fixstr: PWideChar; fixlen: integer;
var devport: UnicodeString): UnicodeString ;
{ Simple version of StrToInt that does not raise any exceptions, it returns
zero for an illegal input. }
function AscToInt (value: string): Integer;
function AscToIntAnsi (value: AnsiString): Integer;
{ Simple version of StrToInt64 that does not raise any exceptions, it returns
zero for an illegal input. }
function AscToInt64 (value: string): Int64 ;
function AscToInt64Ansi (value: AnsiString): Int64 ;
{ Adds thousand separators to an ASCII string (no checking for digits).
The separator value comes from ThousandSeparator and is typically a
comma. }
function AddThouSeps (const S: string): string;
function AddThouSepsAnsi (const S: AnsiString): AnsiString;
{ Converts a 32-bit integer numeric value into a string with thousand
separators, typically commas, calls AddThouSeps. }
function IntToCStr (const N: integer): string ;
function IntToCStrAnsi (const N: integer): AnsiString ;
{ Converts a 64-bit integer numeric value into a string with thousand
separators, typically commas, calls AddThouSeps. }
function Int64ToCStr (const N: int64): string ;
function Int64ToCStrAnsi (const N: int64): AnsiString ;
{ Returns the path of the windows directory. }
function GetWinDir: String;
{ Returns the path of a specific windows shell directory, using the
CSIDL_xx literals. }
function GetShellPath (location: integer): string ;
{ Returns the user name of the current thread. This is the name of the user
currently logged onto the system. }
function GetUsersName: string;
{ Returns the NetBIOS name of the local computer. This name is established
at system startup, when the system reads it from the registry.}
function GetCompName: string;
{ Gets program version information from the string resources keys in an
EXE or DLL file.
AppName is the EXE or DLL file name, KeyName is the literal describing the
key for which information should be returned, includes 'FileDescription',
'FileVersion', 'ProductVersion' (see Delphi Project Options, Version Info
for more keys). }
function GetFileVerInfo (const AppName, KeyName: string): string ;
{ Get one or more ethernet card MAC addresses for a specified PC using
NetBIOS commands. Pcname is blank for the current PC, or specifies the
computer name from which to obtain the MAC addresses. MacAddresses
is a TStringList that is returned with ASCII representations of the
MAC address in hex format, ie 00-30-84-27-C2-1E. Result is number of
MAC addresses returned, or -1 for an error. }
function GetMACAddresses (Pcname: AnsiString; MacAddresses: TStrings): integer ;
{ Load the Synchronization Manager SENSAPI.DLL, returns false if it's
not available. The DLL is only available with MSIE 5 and later, or
Win98, W2K, XP. }
function LoadSensapi: Boolean;
{ Determines whether the local system is connected to a network and the
type of network connection, for example, LAN, WAN, or both. Flags returns
NETWORK_ALIVE_LAN or NETWORK_ALIVE_WAN (RAS). Result is true if there
is a network connection. Note a LAN connection does not necessarily
mean that internet access is also available.
Requires MSIE 5 or later, or Win98, W2K, XP. }
function IsNetAlive (var Flags: DWORD): boolean ;
{ Determines if the specified destination can be reached and provides
Quality of Connection (QOC) information for the destination. Dest can
be an IP address, a UNC name, or an URL, which will be pinged or blank,
if QOC only is required. Returns true if the destination was specified
and can be reached, QocInfo.Flags returns NETWORK_ALIVE_LAN or
NETWORK_ALIVE_WAN (RAS), dwInSpeed/dwOutSpeed are the network adaptor
speed, 1000000 or 100000000 for LANs, 34000, 56000, 64000, etc for RAS.
Note you can not check the speed of a specific device as such, only a route.
Warning - this API does not appear to be totally reliable, ping often fails
or gets blocked.
Requires MSIE 5 or later, or Win98, W2K, XP. }
function IsDestReachable (Dest: string; var QocInfo: TQocInfo): boolean ;
// MSIE internet options
const
CVKey = 'Software\Microsoft\Windows\CurrentVersion' ;
CVNTKey = 'Software\Microsoft\Windows NT\CurrentVersion'; // Nov 2017
{ Allows the MSIE Internet Option 'Dial-Up Settings' to be checked or
set, Value is true for 'always dial my default connection', false for
'never dial a connection' ('dial whenever a connection is not available
is not supported by this function). If Update is false, Value returns
the current setting, if Update is true the Value should be set with the
new setting. Result is false if there's a registy error.
Effectively this is an auto dial option, when set true any application
attempting to access a remote internet server will cause RAS to dial
the default connection (see MSIEDefConn). }
function MSIEAutoDial (var Value: boolean; const Update: boolean): boolean ;
{ Allows the MSIE Internet Option 'Dial-Up Settings' to be checked or set:
0=Never Dial A Connection
1=Dial Whenever A Network Connection Is Not Present
2=Always Dial My Default Connection
If Update is false, Value returns the current setting, if Update is true
the Value should be set with the new setting. Result is false if there's
a registy error. Effectively this is an auto dial option, causing any
applicationattempting to access a remote internet server will cause RAS to
dial the default connection (see MSIEDefConn). }
function MSIEAutoDialOpt (var Value: integer; const Update: boolean): boolean;
{ Allows the MSIE Internet Option 'Dial-Up Settings' default or current
connection entry to be specified. ConnName is the name is the default
connection entry. If Update is false, Value returns the name of the
current default entry, if Update is true the Value should be set with
the new default connection. Result is false if there's a registy error.
This default connection entry is that used by auto dial,
see MSIEAutoDial. }
function MSIEDefConn (var ConnName: string; const Update: boolean): boolean ;
function DirectoryExists(const Name: string): Boolean;
function ForceDirs(Dir: string): Boolean;
// OS version stuff
function IsWin95: boolean ;
function IsWinNT: boolean ;
function IsWin2K: boolean ;
function IsWinXP: boolean ;
function IsWinXPE: boolean ;
function IsWin2K3: boolean ;
function IsWinVista: boolean ;
function IsWin2K8: boolean ;
function IsWin64: boolean ; // 22 July 2011
function IsWow64: boolean ; // 14 Dec 2009
function IsTouchTablet: boolean ; // 10 March 2014
function DisableWow64Redir (var OldRedir: BOOL): boolean ; // 17 May 2013
function RevertWow64Redir (OldRedir: BOOL): boolean ; // 17 May 2013
function GetOSVersion: string ;
procedure GetOSInfo ;
function LoadProdInfoPtr: Boolean; // 10 Aug 2010
function MagGetRegHlmStr (const RegKey, RegValue: string): string ; // Nov 2017
// validation routines
function IsSpace (Ch: Char): Boolean ;
function IsDigit (Ch: Char): Boolean ;
function IsLetterOrDigit (Ch: Char): Boolean ;
function IsPathSep (Ch: Char): Boolean ;
function IsDigitsDec (info: string; decimal: boolean) : boolean ;
function IsDigits (info: string) : boolean ;
procedure ConvHexStr (instr: string; var outstr: string) ;
procedure ByteSwaps(DataPtr : Pointer;NoBytes : integer);
function ConIntHex (value: cardinal): string ; // 32-bit to 8 byte hex
function StripQuotes (filename: string): string ;
function StripNewLines (const S: string): string;
// directory and file listing
function IndexFiles (searchfile: string; mask: integer;
var FileList: TStringList; var totsize: cardinal): integer ;
function DeleteOldFiles (fname: string): integer ;
function GetEnvirVar (const name: UnicodeString): string ;
function StripChars (AString, AChars: String): String ;
function UpAndLower (const S: String): String ;
function StripChar (const AString: String; const AChar: Char): String ;
function StripSpaces (const AString: String): String ;
function StripCommas (const AString: String): String ;
function StripNulls (const AString: String): String ;
function StripAllCntls (const AString: String): String ;
function StripCharsAnsi (AString, AChars: AnsiString): AnsiString ;
function UpAndLowerAnsi (const S: AnsiString): AnsiString ;
function StripCharAnsi (const AString: AnsiString; const AChar: AnsiChar): AnsiString ;
function StripSpacesAnsi (const AString: AnsiString): AnsiString ;
function StripCommasAnsi (const AString: AnsiString): AnsiString ;
function StripNullsAnsi (const AString: AnsiString): AnsiString ;
function StripAllCntlsAnsi (const AString: AnsiString): AnsiString ;
procedure StringTranCh (var S: String; FrCh, ToCh: Char) ;
procedure StringTranChAnsi (var S: AnsiString; FrCh, ToCh: AnsiChar) ;
procedure StringTranChWide (var S: UnicodeString; FrCh, ToCh: WideChar) ;
procedure StringCtrlSafe (var S: AnsiString) ;
procedure StringCtrlRest (var S: AnsiString) ;
function StrCtrlSafe (const S: AnsiString): AnsiString ;
function StrCtrlRest (const S: AnsiString): AnsiString ;
procedure StringFileTran (var S: String) ;
function StringRemCntls (var S: String): boolean ;
function StringRemCntlsW (var S: UnicodeString): boolean ; // 13 Oct 2008
function StringRemCntlsEx (var S: String): boolean ;
procedure DosToUnixPath (var S: String) ;
procedure UnixToDosPath (var S: String) ;
function UnxToDosPath (const S: String): String ;
function DosToUnxPath (const S: String): String ;
procedure UnixToDosPathW (var S: UnicodeString) ;
procedure DosToUnixPathW (var S: UnicodeString) ;
function EscapeBackslashes(const S: string): string; // 22 June 2010
function StrFileTran (const S: String): String ;
procedure StringFileTranEx (var S: String) ;
function StrFileTranEx (const S: String): String ;
{ Copy }
{ Variantions on Delphi's Copy. Just like Delphi's Copy, illegal values for }
{ Start (<1,>len), Stop (len) and Count (<0,>end) are tolerated. }
Function CopyRange (const S : String; const Start, Stop : Integer) : String;
Function CopyFrom (const S : String; const Start : Integer) : String;
Function CopyLeft (const S : String; const Count : Integer) : String;
Function CopyRight (const S : String; const Count : Integer = 1) : String;
{ Match }
{ True if M matches S [Pos] (or S [Pos..Pos+Count-1]) }
{ Returns False if Pos or Count is invalid }
{$IFNDEF CPUX64}
Function Match (const M : CharSet; const S : AnsiString; const Pos : Integer = 1;
const Count : Integer = 1) : Boolean; overload;
Function Match (const M : CharSetArray; const S : AnsiString; const Pos : Integer = 1)
: Boolean; overload;
Function Match (const M, S : AnsiString; const Pos : Integer = 1) : Boolean; overload; // Blazing
{ PosNext }
{ Returns first Match of Find in S after LastPos. }
{ To find the first match, set LastPos to 0. }
{ Returns 0 if not found or illegal value for LastPos (<0,>length(s)) }
Function PosNext (const Find : CharSet; const S : AnsiString;
const LastPos : Integer = 0) : Integer; overload;
Function PosNext (const Find : CharSetArray; const S : AnsiString;
const LastPos : Integer = 0) : Integer; overload;
Function PosNext (const Find : AnsiString; const S : AnsiString;
const LastPos : Integer = 0) : Integer; overload;
Function PosPrev (const Find : AnsiString; const S : AnsiString;
const LastPos : Integer = 0) : Integer;
{ PosN }
{ Finds the Nth occurance of Find in S from the left or the right. }
Function PosN (const Find, S : AnsiString; const N : Integer = 1;
const FromRight : Boolean = False) : Integer;
{$ENDIF}
{ Split/Join }
{ Splits S into pieces seperated by Delimiter. If Delimiter='' or S='' then }
{ returns an empty list. If Token not found in S returns list with one }
{ item, S. }
Function StrArraySplit (const S : String; const Delimiter : String = c_Space) : StringArray; overload ;
Function StrArrayJoin (const S : StringArray; const Delimiter : String = c_Space) : String; overload ;
procedure StrArrayInsert (var S: StringArray; index: integer; T: string) ; overload ;
procedure StrArrayInsert (var S: StringArray; index: integer; T: string; var Total: integer) ; overload ;
procedure StrArrayDelete (var S: StringArray; index: integer) ; overload ;
procedure StrArrayDelete (var S: StringArray; index: integer; var Total: integer) ; overload ;
procedure StrArrayToList (S: StringArray; var T: TStringList) ;
procedure StrArrayFromList (T: TStringList; var S: StringArray) ;
function StrArrayPosOf (const L: string; S: StringArray): integer ; overload ;
procedure StrArrayToMultiSZ (S: StringArray; var Buffer: PAnsiChar) ; overload ;
procedure StrArrayFromMultiSZ (Buffer: PAnsiChar ; Len: integer ; var S: StringArray) ; overload ;
procedure StrArrayToMultiSZ (S: StringArray; var Buffer: PWideChar) ; overload ;
procedure StrArrayFromMultiSZ (Buffer: PWideChar ; Len: integer ; var S: StringArray) ; overload ;
function StrArrayPosOfEx (const L: string; S: StringArray; Total: integer = MaxInt): integer ; overload ;
function StrArrayFindSorted (const S: StringArray; T: string; var Index: longint; Total: integer = MaxInt): Boolean; overload ;
function StrArrayAddSorted (var S: StringArray; T: string): boolean ; overload ;
function StrArrayAddSorted (var S: StringArray; T: string; var Total: integer): boolean ; overload ;
Function StrArraySplit (const S : WideString; const Delimiter : WideString = c_Space) : WideStringArray; overload ;
Function StrArrayJoin (const S : WideStringArray; const Delimiter : WideString = c_Space) : WideString; overload ;
procedure StrArrayInsert (var S: WideStringArray; index: integer; T: Widestring) ; overload ;
procedure StrArrayInsert (var S: WideStringArray; index: integer; T: Widestring; var Total: integer) ; overload ;
procedure StrArrayDelete (var S: WideStringArray; index: integer) ; overload ;
procedure StrArrayDelete (var S: WideStringArray; index: integer; var Total: integer) ; overload ;
//procedure StrArrayToList (S: WideStringArray; var T: TWideStringList) ;
//procedure StrArrayFromList (T: TWideStringList; var S: WideStringArray) ;
function StrArrayPosOf (const L: Widestring; S: WideStringArray): integer ; overload ;
procedure StrArrayToMultiSZ (S: WideStringArray; var Buffer: PWideChar) ; overload ;
procedure StrArrayFromMultiSZ (Buffer: PWideChar ; Len: integer ; var S: WideStringArray) ; overload ;
function StrArrayPosOfEx (const L: Widestring; S: WideStringArray; Total: integer = MaxInt): integer ; overload ;
function StrArrayFindSorted (const S: WideStringArray; T: Widestring; var Index: longint; Total: integer = MaxInt): Boolean; overload ;
function StrArrayAddSorted (var S: WideStringArray; T: Widestring): boolean ; overload ;
function StrArrayAddSorted (var S: WideStringArray; T: Widestring; var Total: integer): boolean ; overload ;
// file time stamp stuff
function UpdateFileAge(const FName: String; const NewDT: TDateTime): boolean;
function UpdateUFileAge(const FName: String; const NewDT: TDateTime): boolean;
function GetUnixTime: Int64;
function GetLocalBiasUTC: integer;
function DateTimeToUTC(dtDT : TDateTime) : TDateTime;
function UTCToLocalDT(dtDT : TDateTime) : TDateTime;
function GetUTCTime: TDateTime;
function SetUTCTime (DateTime: TDateTime): boolean ;
function FileTimeToInt64 (const FileTime: TFileTime): Int64 ;
function Int64ToFileTime (const FileTime: Int64): TFileTime ;
function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
function DateTimeToFileTime(DateTime: TDateTime): TFileTime;
function FileTimeToSecs2K (const FileTime: TFileTime): integer ;
function CheckFileOpen(const FName: String): integer;
function TruncateFile(const FName: UnicodeString; NewSize: int64): int64;
function GetSizeFile (filename: string): LongInt;
function GetSize64File (filename: string): Int64 ;
function GetSizeFileW (filename: UnicodeString): LongInt;
function GetSize64FileW (filename: UnicodeString): Int64 ;
function GetFUAgeSizeFile (filename: string ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
function GetUAgeSizeFile (filename: string ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
function GetFAgeSizeFile (filename: string ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
function GetAgeSizeFile (filename: string ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
function GetFUAgeSizeFileW (filename: UnicodeString ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
function GetUAgeSizeFileW (filename: UnicodeString ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
function GetFAgeSizeFileW (filename: UnicodeString ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
function GetAgeSizeFileW (filename: UnicodeString ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
function TrimSpRight(const S: string): string;
function ExtractNameOnly (FileName: string): string;
function GetExceptMess (ExceptObject: TObject): string;
{ Converts a String into a LongInt }
function Str2LInt (const S: String): LongInt;
{ Converts a String into a Word }
function Str2Word (const S: String): Word;
{ Converts a String into a Byte }
function Str2Byte (const S: String): Byte;
{ Converts a String into a ShortInt }
function Str2SInt (const S: String): ShortInt;
{ Converts a String into an Integer }
function Str2Int (const S: String): Integer;
{ Converts a LongInt into a String of length N with
zeros Padding to the Left }
function Int2StrZ (const L: LongInt; const Len: Byte): String;
{ Converts a LongInt into a String of length N with
NumPadCh Padding to the Left }
function LInt2Str (const L: LongInt; const Len: Byte): String;
{ Converts a LongInt into a String of length N with
NumPadCh Padding to the Left }
function Byte2Str (const L: LongInt; const Len: Byte): String;
{ Converts a LongInt into a String of length N with
NumPadCh Padding to the Left }
function LInt2ZStr (const L: LongInt; const Len: Byte): String;
{ Converts a LongInt into a String of length N with
NumPadCh Padding to the Left, with blanks returned
if Value is 0 }
function LInt2ZBStr (const L: LongInt; const Len: Byte): String;
{ Convert a LongInt into a Comma'ed String of length Len,
with NumPadCh Padding to the Left }
function LInt2CStr (const L : LongInt; const Len : Byte): string;
{ Convert a LongInt into an exact String, No Padding }
function LInt2EStr (const L: LongInt): String;
{ Convert a LongInt into an exact String, No Padding,
with null returned if Value is 0 }
function LInt2ZBEStr (const L: LongInt): String;
{ Convert a LongInt into a Comma'ed String without Padding }
function LInt2CEStr (const L : LongInt): string;
{ Convert an Int64 to a comma'ed string, no padding }
function Int642CEStr (const L : Int64): string;
{ Returns a string composed of N occurrences of Ch. }
function FillStr (const Ch : Char; const N : Integer): string;
{ Returns a string composed of N blank spaces (i.e. #32) }
function BlankStr (const N : Integer): string;
{ Returns a string composed of N occurrences of '-'. }
function DashStr (const N : Integer): String;
{ Returns a string composed of N occurrences of '='. }
function DDashStr (const N : Integer): string;
{ Returns a string composed of N occurrences of 'Ä' (196). }
function LineStr (const N : Integer): string;
{ Returns a string composed of N occurrences of 'Í' (205). }
function DLineStr (const N : Integer): string;
{ Returns a string composed of N occurrences of '*'. }
function StarStr (const N : Integer): string;
{ Returns a string composed of N occurrences of '#'. }
function HashStr (const N : Integer): string;
{ Returns a string with blank spaces added to the end of the
string until the string is of the given length.
If Length (S) >= Len then NO padding occurs, and S is returned. }
function PadRightStr (const S : string; const Len : Integer): string;
{ Returns a string with blank spaces added to the beginning of the
string until the string is of the given length.
If Length (S) >= Len then NO padding occurs, and S is returned. }
function PadLeftStr (const S : string; const Len : Integer): string;
{ Returns a string with specified characters added to the beginning of the
string until the string is of the given length.
If Length (S) >= Len then NO padding occurs, and S is returned. }
function PadChLeftStr (const S : string; const Ch : Char; const Len : Integer): string;
{ time functions }
function DateTimeToAStr(const DateTime: TDateTime): string; // always alpha month and numeric hh:mm:ss
function DateToAStr(const DateTime: TDateTime): string; // always alpha month
function TimeToNStr(const DateTime: TDateTime): string; // always numeric hh:mm:ss
function TimeToZStr(const DateTime: TDateTime): string; // always numeric hh:mm:ss:zzz
function timeHour(T: TDateTime): Integer;
function timeMin(T: TDateTime): Integer;
function timeSec(T: TDateTime): Integer;
function timeToInt(T: TDateTime): Integer; // seconds
function HoursToTime (hours: integer): TDateTime ;
function MinsToTime (mins: integer): TDateTime ;
function SecsToTime (secs: integer): TDateTime ;
function TimerToStr (duration: TDateTime): string ;
function PackedISO2Date (info: string): TDateTime ;
function PackedISO2UKStr (info: string): string ;
function DTtoISODT (D: TDateTime): string ;
function AlphaDTtoISODT (sdate, stime: string): string ;
function ISODTtoPacked (ISO: string): string ;
function PackedDTtoISODT (info: string): string ;
function QuoteNull (S: string): string ;
function QuoteSQLDate (D: TDateTime): string ;
function DT2ISODT (D: TDateTime): string ;
function QuoteSQLTime (T: TDateTime): string ;
function Str2DateTime (const S: String): TDateTime ;
function Str2Time (const S: String): TDateTime ;
function Packed2Secs (info: string): integer ;
function Packed2Time (info: string): TDateTime ;
function Packed2Date (info: string): TDateTime ;
function Date2Packed (infoDT: TDateTime): string ;
function Date2XPacked (infoDT: TDateTime): string ;
function ConvUKDate (info: string): TDateTime ;
function ConvUSADate (info: string): TDateTime ;
function SecsToMinStr (secs: integer): string ;
function SecsToHourStr (secs: integer): string ;
function ConvLongDate (info: string): TDateTime ;
function DTtoAlpha (D: TDateTime): string ;
function DTTtoAlpha (D: TDateTime): string ;
function DTtoLongAlpha (D: TDateTime): string ;
function EqualDateTime(const A, B: TDateTime): boolean;
function DiffDateTime(const A, B: TDateTime): integer ;
{ Returns Delphi TDateTime converted from a UNIX time stamp, being the
number of seconds since 1st January 1970. }
function TStamptoDT (stamp: DWORD): TDateTime ;
function TDTtoStamp (D: TDateTime): DWORD ;
function sysTempPath: string ;
function sysTempPathWide: UnicodeString;
procedure sysBeep ;
function sysWindowsDir: String ;
function strLastCh(const S: String): Char;
procedure strStripLast(var S: String);
function strAddSlash(const S: String): String;
function strDelSlash(const S: String): String;
function ExtractUNIXPath(const FileName: string): string;
function ExtractUNIXName(const FileName: string): string;
function GetYesNo (value: boolean): string ;
function CheckYesNo (const value: string): boolean ;
function GetYN (value: boolean): char ;
function GetTrueFalse (opt: boolean): string ;
function CheckTrueFalse (const value: string): boolean ;
function CharPos (TheChar: AnsiChar; const Str: AnsiString): Integer;
function PosRev (const SubStr: string; const S: string): Integer;
function DownCase( ch : AnsiChar ) : AnsiChar;
function ConvHexQuads (S: string): string ;
// better Now, accurate to nano-seconds (relatively)
function NowPC : TDateTime;
function GetPerfCountsPerSec: int64 ;
function PerfCountCurrent: int64 ;
function PerfCountToMilli (LI: int64): integer ;
function PerfCountGetMilli (startLI: int64): integer ;
function PerfCountGetMillStr (startLI: int64): string ;
function PerfCountToSecs (LI: int64): integer ;
function PerfCountGetSecs (startLI: int64): integer ;
function InetParseDate(const DateStr: string): TDateTime;
function MagURLEncode(const psSrc: AnsiString): AnsiString; // Dec 2020
function MagURLDecode(const AStr: AnsiString): AnsiString; // Dec 2020
function FormatLastError: string ;
function Int2Kbytes (value: integer): string ;
function Int2Mbytes (value: int64): string ;
function IntToKbyte (Value: Int64; Bytes: boolean = false): String;
procedure EmptyRecycleBin (const fname: WideString) ;
procedure TrimWorkingSetMemory ;
procedure FreeAndNilEx(var Obj);
function IsProgAdmin: Boolean;
function GetLcTypeInfo (Id: integer): UnicodeString ;
// working with ticks
function GetTickCountX: longword ;
function DiffTicks (const StartTick, EndTick: longword): longword ;
function ElapsedTicks (const StartTick: longword): longword ;
function ElapsedMsecs (const StartTick: longword): longword ;
function ElapsedSecs (const StartTick: longword): integer ;
function ElapsedMins (const StartTick: longword): integer ;
function WaitingSecs (const EndTick: longword): integer ;
function GetTrgMSecs (const MilliSecs: integer): longword ;
function GetTrgSecs (const DurSecs: integer): longword ;
function GetTrgMins (const DurMins: integer): longword ;
function TestTrgTick (const TrgTick: longword): boolean ;
function AddTrgMsecs (const TickCount, MilliSecs: longword): longword ;
function AddTrgSecs (const TickCount, DurSecs: integer): longword ;
// format an IPv6 address with []
function FormatIpAddr (const Addr: string): string ;
// format an IPv6 address with [] and port
function FormatIpAddrPort (const Addr, Port: string): string ;
// strip [] off IPv6 addresses
function StripIpAddr (const Addr: string): string ;
{ Literals for GetShellPath, to get the windows path to specified
system shell directories. }
const
CSIDL_DESKTOP = $0000 ; //
CSIDL_INTERNET = $0001 ; // Internet Explorer (icon on desktop)
CSIDL_PROGRAMS = $0002 ; // Start Menu\Programs
CSIDL_CONTROLS = $0003 ; // My Computer\Control Panel
CSIDL_PRINTERS = $0004 ; // My Computer\Printers
CSIDL_PERSONAL = $0005 ; // My Documents
CSIDL_FAVORITES = $0006 ; // \Favorites
CSIDL_STARTUP = $0007 ; // Start Menu\Programs\Startup
CSIDL_RECENT = $0008 ; // \Recent
CSIDL_SENDTO = $0009 ; // \SendTo
CSIDL_BITBUCKET = $000a ; // \Recycle Bin
CSIDL_STARTMENU = $000b ; // \Start Menu
CSIDL_MYDOCUMENTS = $000c ; // the user's My Documents folder
CSIDL_MYMUSIC = $000d ;
CSIDL_MYVIDEO = $000e ;
CSIDL_DESKTOPDIRECTORY = $0010 ; // \Desktop 16
CSIDL_DRIVES = $0011 ; // My Computer
CSIDL_NETWORK = $0012 ; // Network Neighborhood
CSIDL_NETHOOD = $0013 ; // \nethood
CSIDL_FONTS = $0014 ; // windows\fonts 20
CSIDL_TEMPLATES = $0015 ;
CSIDL_COMMON_STARTMENU = $0016 ; // All Users\Start Menu
CSIDL_COMMON_PROGRAMS = $0017 ; // All Users\Programs
CSIDL_COMMON_STARTUP = $0018 ; // All Users\Startup 24
CSIDL_COMMON_DESKTOPDIRECTORY = $0019 ; // All Users\Desktop
CSIDL_APPDATA = $001a ; // \Application Data
CSIDL_PRINTHOOD = $001b ; // \PrintHood
CSIDL_LOCAL_APPDATA = $001C ; // non roaming, user\Local Settings\Application Data
CSIDL_ALTSTARTUP = $001d ; // non localized startup
CSIDL_COMMON_ALTSTARTUP = $001e ; // non localized common startup 30
CSIDL_COMMON_FAVORITES = $001f ;
CSIDL_INTERNET_CACHE = $0020 ;
CSIDL_COOKIES = $0021 ;
CSIDL_HISTORY = $0022 ; // 34
CSIDL_COMMON_APPDATA = $0023 ; // All Users\Application Data, new for Win2K
CSIDL_WINDOWS = $0024 ; // GetWindowsDirectory(), new for Win2K
CSIDL_SYSTEM = $0025 ; // GetSystemDirectory(), new for Win2K
CSIDL_PROGRAM_FILES = $0026 ; // C:\Program Files, new for Win2K
CSIDL_MYPICTURES = $0027 ; // My Pictures, new for Win2K
CSIDL_PROFILE = $0028 ; // USERPROFILE
CSIDL_SYSTEMX86 = $0029 ; // x86 system directory on RISC
CSIDL_PROGRAM_FILESX86 = $002a ; // x86 C:\Program Files on RISC
CSIDL_PROGRAM_FILES_COMMON = $002b ; // C:\Program Files\Common, new for Win2K
CSIDL_PROGRAM_FILES_COMMONX86 = $002c ; // x86 Program Files\Common on RISC
CSIDL_COMMON_TEMPLATES = $002d ; // All Users\Templates
CSIDL_COMMON_DOCUMENTS = $002e ; // All Users\Documents 46
CSIDL_COMMON_ADMINTOOLS = $002f ; // All Users\Start Menu\Programs\Administrative Tools
CSIDL_ADMINTOOLS = $0030 ; // \Start Menu\Programs\Administrative Tools 48
CSIDL_CONNECTIONS = $0031 ; // Network and Dial-up Connections - not Win9x 49
CSIDL_COMMON_MUSIC = $0035 ; // new for XP
CSIDL_COMMON_PICTURES = $0036 ; // new for XP
CSIDL_COMMON_VIDEO = $0037 ; // new for XP
CSIDL_RESOURCES = $0038 ; // new for Vista
CSIDL_RESOURCES_LOCALIZED = $0039 ;
CSIDL_COMMON_OEM_LINKS = $003A ;
CSIDL_CDBURN_AREA = $003B ; // new for XP
CSIDL_COMPUTERSNEARME = $003D ;
CSIDL_PLAYLISTS = $003F ; // new for Vista
CSIDL_SAMPLE_MUSIC = $0040 ; // new for Vista
CSIDL_SAMPLE_PLAYLISTS = $0041 ; // new for Vista
CSIDL_SAMPLE_PICTURES = $0042 ; // new for Vista
CSIDL_SAMPLE_VIDEOS = $0043 ; // new for Vista
CSIDL_PHOTOALBUMS = $0045 ; // new for Vista
CSIDL_FLAG_CREATE = $8000 ; // combine with CSIDL_ value to force folder creation in SHGetFolderPath()
CSIDL_FLAG_DONT_VERIFY = $4000 ; // combine with CSIDL_ value to return an unverified folder path
CSIDL_FLAG_NO_ALIAS = $1000 ;
CSIDL_FLAG_PER_USER_INIT = $0800 ;
CSIDL_FLAG_MASK = $FF00 ; // mask for all possible flag values
// literals for SHEmptyRecycleBin
// {$xFNDEF BCB} 13 June 2013, added externalsym below to support BCB
const
SHERB_NOCONFIRMATION = $00000001;
{$EXTERNALSYM SHERB_NOCONFIRMATION}
SHERB_NOPROGRESSUI = $00000002;
{$EXTERNALSYM SHERB_NOPROGRESSUI}
SHERB_NOSOUND = $00000004;
{$EXTERNALSYM SHERB_NOSOUND}
//{$xENDIF}
type
TOSVERSIONINFOEXW = record // NT4 SP6 and later - not Win9x
dwOSVersionInfoSize: DWORD;
dwMajorVersion: DWORD;
dwMinorVersion: DWORD;
dwBuildNumber: DWORD;
dwPlatformId: DWORD;
szCSDVersion: array[0..127] of WideChar; { Maintenance string for PSS usage } // unicode
wServicePackMajor: WORD ;
wServicePackMinor: WORD ;
wSuiteMask: WORD ;
wProductType: BYTE ;
wReserved: BYTE;
end;
TIsWow64Process = function (hProcess: THandle; var Wow64Process: BOOL): BOOL; stdcall; // 14 Dec 2009
TWow64DisableWow64FsRedirection = function (var Wow64FsEnableRedirection: BOOL): BOOL; stdcall; // 17 May 2013
TWow64RevertWow64FsRedirection = function (Wow64FsEnableRedirection: BOOL): BOOL; stdcall; // 17 May 2013
var
OsInfo, OsInfoRaw: TOSVERSIONINFOEXW ;
GetProductInfo: function (dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion,
dwSpMinorVersion: DWORD; var dwReturnedProductType: DWORD): bool; stdcall; // 10 Aug 2010 - Vista and later
VerifyVersionInfoW: function (var VersionInformation: TOSVERSIONINFOEXW; // 10 March 2014 - W2K and later
const dwTypeMask: DWORD; const dwlConditionMask: ULONGLONG): BOOL; stdcall;
VerSetConditionMask: function (const ConditionMask : ULONGLONG; const TypeMask : DWORD; // 10 March 2014 - W2K and later
const Condition : BYTE ): ULONGLONG; stdcall;
function GetVersionExW2 (var lpVersionInfo: TOSVERSIONINFOEXW): BOOL; stdcall;
function GetVersionExW2; external kernel32 name 'GetVersionExW';
// 11 Aug 2015 - this kernel mode version provides accurate OS information without a manifest
function RtlGetVersion (var lpVersionInformation: TOSVERSIONINFOEXW): DWORD; stdcall; // Windows 2000 and later
function RtlGetVersion; external 'ntdll.dll' name 'RtlGetVersion';
// {$xFNDEF BCB} 13 June 2013, added externalsym below to support BCB
CONST
// wProductType
VER_NT_WORKSTATION = $0000001 ;
{$EXTERNALSYM VER_NT_WORKSTATION}
VER_NT_DOMAIN_CONTROLLER = $0000002 ;
{$EXTERNALSYM VER_NT_DOMAIN_CONTROLLER}
VER_NT_SERVER = $0000003 ;
{$EXTERNALSYM VER_NT_SERVER}
// wSuiteMask
VER_SERVER_NT = $80000000 ;
{$EXTERNALSYM VER_SERVER_NT}
VER_WORKSTATION_NT = $40000000 ;
{$EXTERNALSYM VER_WORKSTATION_NT}
VER_SUITE_SMALLBUSINESS = $00000001 ;
{$EXTERNALSYM VER_SUITE_SMALLBUSINESS}
VER_SUITE_ENTERPRISE = $00000002 ;
{$EXTERNALSYM VER_SUITE_ENTERPRISE}
VER_SUITE_BACKOFFICE = $00000004 ;
{$EXTERNALSYM VER_SUITE_BACKOFFICE}
VER_SUITE_COMMUNICATIONS = $00000008 ;
{$EXTERNALSYM VER_SUITE_COMMUNICATIONS}
VER_SUITE_TERMINAL = $00000010 ;
{$EXTERNALSYM VER_SUITE_TERMINAL}
VER_SUITE_SMALLBUSINESS_RESTRICTED = $00000020 ;
{$EXTERNALSYM VER_SUITE_SMALLBUSINESS_RESTRICTED}
VER_SUITE_EMBEDDEDNT = $00000040 ;
{$EXTERNALSYM VER_SUITE_EMBEDDEDNT}
VER_SUITE_DATACENTER = $00000080 ;
{$EXTERNALSYM VER_SUITE_DATACENTER}
VER_SUITE_SINGLEUSERTS = $00000100 ;
{$EXTERNALSYM VER_SUITE_SINGLEUSERTS}
VER_SUITE_PERSONAL = $00000200 ;
{$EXTERNALSYM VER_SUITE_PERSONAL}
VER_SUITE_BLADE = $00000400 ;
{$EXTERNALSYM VER_SUITE_BLADE}
VER_SUITE_EMBEDDED_RESTRICTED = $00000800 ;
{$EXTERNALSYM VER_SUITE_EMBEDDED_RESTRICTED}
VER_SUITE_SECURITY_APPLICANCE = $00001000 ;
{$EXTERNALSYM VER_SUITE_SECURITY_APPLICANCE}
VER_SUITE_STORAGE_SERVER = $00002000 ;
{$EXTERNALSYM VER_SUITE_STORAGE_SERVER}
VER_SUITE_COMPUTE_SERVER = $00004000 ;
{$EXTERNALSYM VER_SUITE_COMPUTE_SERVER}
VER_SUITE_WH_SERVER = $00008000 ;
{$EXTERNALSYM VER_SUITE_WH_SERVER}
// GetSystemMetrics - OS version subtypes
SM_TABLETPC = 86 ;
{$EXTERNALSYM SM_TABLETPC}
SM_MEDIACENTER = 87 ;
{$EXTERNALSYM SM_MEDIACENTER}
SM_STARTER = 88 ;
{$EXTERNALSYM SM_STARTER}
SM_SERVERR2 = 89 ;
{$EXTERNALSYM SM_SERVERR2}
SM_MOUSEHORIZONTALWHEELPRESENT = 91 ;
{$EXTERNALSYM SM_MOUSEHORIZONTALWHEELPRESENT}
SM_CXPADDEDBORDER = 92 ;
{$EXTERNALSYM SM_CXPADDEDBORDER}
SM_DIGITIZER = 94 ;
{$EXTERNALSYM SM_DIGITIZER}
SM_MAXIMUMTOUCHES = 95 ;
{$EXTERNALSYM SM_MAXIMUMTOUCHES}
// GetProductInfo = product types - Vista and later, // 10 Aug 2010
PRODUCT_UNDEFINED = $00000000;
{$EXTERNALSYM PRODUCT_UNDEFINED}
PRODUCT_ULTIMATE = $00000001;
{$EXTERNALSYM PRODUCT_ULTIMATE}
PRODUCT_HOME_BASIC = $00000002;
{$EXTERNALSYM PRODUCT_HOME_BASIC}
PRODUCT_HOME_PREMIUM = $00000003;
{$EXTERNALSYM PRODUCT_HOME_PREMIUM}
PRODUCT_ENTERPRISE = $00000004;
{$EXTERNALSYM PRODUCT_ENTERPRISE}
PRODUCT_HOME_BASIC_N = $00000005;
{$EXTERNALSYM PRODUCT_HOME_BASIC_N}
PRODUCT_BUSINESS = $00000006;
{$EXTERNALSYM PRODUCT_BUSINESS}
PRODUCT_STANDARD_SERVER = $00000007;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER}
PRODUCT_DATACENTER_SERVER = $00000008;
{$EXTERNALSYM PRODUCT_DATACENTER_SERVER}
PRODUCT_SMALLBUSINESS_SERVER = $00000009;
{$EXTERNALSYM PRODUCT_SMALLBUSINESS_SERVER}
PRODUCT_ENTERPRISE_SERVER = $0000000A;
{$EXTERNALSYM PRODUCT_ENTERPRISE_SERVER}
PRODUCT_STARTER = $0000000B;
{$EXTERNALSYM PRODUCT_STARTER}
PRODUCT_DATACENTER_SERVER_CORE = $0000000C;
{$EXTERNALSYM PRODUCT_DATACENTER_SERVER_CORE}
PRODUCT_STANDARD_SERVER_CORE = $0000000D;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER_CORE}
PRODUCT_ENTERPRISE_SERVER_CORE = $0000000E;
{$EXTERNALSYM PRODUCT_ENTERPRISE_SERVER_CORE}
PRODUCT_ENTERPRISE_SERVER_IA64 = $0000000F;
{$EXTERNALSYM PRODUCT_ENTERPRISE_SERVER_IA64}
PRODUCT_BUSINESS_N = $00000010;
{$EXTERNALSYM PRODUCT_BUSINESS_N}
PRODUCT_WEB_SERVER = $00000011;
{$EXTERNALSYM PRODUCT_WEB_SERVER}
PRODUCT_CLUSTER_SERVER = $00000012;
{$EXTERNALSYM PRODUCT_CLUSTER_SERVER}
PRODUCT_HOME_SERVER = $00000013;
{$EXTERNALSYM PRODUCT_HOME_SERVER}
PRODUCT_STORAGE_EXPRESS_SERVER = $00000014;
{$EXTERNALSYM PRODUCT_STORAGE_EXPRESS_SERVER}
PRODUCT_STORAGE_STANDARD_SERVER = $00000015;
{$EXTERNALSYM PRODUCT_STORAGE_STANDARD_SERVER}
PRODUCT_STORAGE_WORKGROUP_SERVER = $00000016;
{$EXTERNALSYM PRODUCT_STORAGE_WORKGROUP_SERVER}
PRODUCT_STORAGE_ENTERPRISE_SERVER = $00000017;
{$EXTERNALSYM PRODUCT_STORAGE_ENTERPRISE_SERVER}
PRODUCT_SERVER_FOR_SMALLBUSINESS = $00000018;
{$EXTERNALSYM PRODUCT_SERVER_FOR_SMALLBUSINESS}
PRODUCT_SMALLBUSINESS_SERVER_PREMIUM = $00000019;
{$EXTERNALSYM PRODUCT_SMALLBUSINESS_SERVER_PREMIUM}
PRODUCT_HOME_PREMIUM_N = $0000001A;
{$EXTERNALSYM PRODUCT_HOME_PREMIUM_N}
PRODUCT_ENTERPRISE_N = $0000001B;
{$EXTERNALSYM PRODUCT_ENTERPRISE_N}
PRODUCT_ULTIMATE_N = $0000001C;
{$EXTERNALSYM PRODUCT_ULTIMATE_N}
PRODUCT_WEB_SERVER_CORE = $0000001D;
{$EXTERNALSYM PRODUCT_WEB_SERVER_CORE}
PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = $0000001E;
{$EXTERNALSYM PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT}
PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = $0000001F;
{$EXTERNALSYM PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY}
PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = $00000020;
{$EXTERNALSYM PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING }
PRODUCT_SERVER_FOUNDATION = $00000021;
{$EXTERNALSYM PRODUCT_SERVER_FOUNDATION}
PRODUCT_HOME_PREMIUM_SERVER = $00000022;
{$EXTERNALSYM PRODUCT_HOME_PREMIUM_SERVER}
PRODUCT_SERVER_FOR_SMALLBUSINESS_V = $00000023;
{$EXTERNALSYM PRODUCT_SERVER_FOR_SMALLBUSINESS_V}
PRODUCT_STANDARD_SERVER_V = $00000024;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER_V}
PRODUCT_DATACENTER_SERVER_V = $00000025;
{$EXTERNALSYM PRODUCT_DATACENTER_SERVER_V}
PRODUCT_ENTERPRISE_SERVER_V = $00000026;
{$EXTERNALSYM PRODUCT_ENTERPRISE_SERVER_V}
PRODUCT_DATACENTER_SERVER_CORE_V = $00000027;
{$EXTERNALSYM PRODUCT_DATACENTER_SERVER_CORE_V}
PRODUCT_STANDARD_SERVER_CORE_V = $00000028;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER_CORE_V}
PRODUCT_ENTERPRISE_SERVER_CORE_V = $00000029;
{$EXTERNALSYM PRODUCT_ENTERPRISE_SERVER_CORE_V}
PRODUCT_HYPERV = $0000002A;
{$EXTERNALSYM PRODUCT_HYPERV}
PRODUCT_STORAGE_EXPRESS_SERVER_CORE = $0000002B;
{$EXTERNALSYM PRODUCT_STORAGE_EXPRESS_SERVER_CORE}
PRODUCT_STORAGE_STANDARD_SERVER_CORE = $0000002C;
{$EXTERNALSYM PRODUCT_STORAGE_STANDARD_SERVER_CORE}
PRODUCT_STORAGE_WORKGROUP_SERVER_CORE = $0000002D;
{$EXTERNALSYM PRODUCT_STORAGE_WORKGROUP_SERVER_CORE}
PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE = $0000002E;
{$EXTERNALSYM PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE}
PRODUCT_STARTER_N = $0000002F;
{$EXTERNALSYM PRODUCT_STARTER_N}
PRODUCT_PROFESSIONAL = $00000030;
{$EXTERNALSYM PRODUCT_PROFESSIONAL}
PRODUCT_PROFESSIONAL_N = $00000031;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_N}
PRODUCT_SB_SOLUTION_SERVER = $00000032;
{$EXTERNALSYM PRODUCT_SB_SOLUTION_SERVER}
PRODUCT_SERVER_FOR_SB_SOLUTIONS = $00000033;
{$EXTERNALSYM PRODUCT_SERVER_FOR_SB_SOLUTIONS}
PRODUCT_STANDARD_SERVER_SOLUTIONS = $00000034;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER_SOLUTIONS}
PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE = $00000035;
{$EXTERNALSYM PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE}
PRODUCT_SB_SOLUTION_SERVER_EM = $00000036;
{$EXTERNALSYM PRODUCT_SB_SOLUTION_SERVER_EM}
PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM = $00000037;
{$EXTERNALSYM PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM}
PRODUCT_SOLUTION_EMBEDDEDSERVER = $00000038;
{$EXTERNALSYM PRODUCT_SOLUTION_EMBEDDEDSERVER}
PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE = $00000039;
{$EXTERNALSYM PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE}
PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE = $0000003F;
{$EXTERNALSYM PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE}
PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT = $0000003B;
{$EXTERNALSYM PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT}
PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL = $0000003C;
{$EXTERNALSYM PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL}
PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC = $0000003D;
{$EXTERNALSYM PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC}
PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC = $0000003E;
{$EXTERNALSYM PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC}
PRODUCT_CLUSTER_SERVER_V = $00000040;
{$EXTERNALSYM PRODUCT_CLUSTER_SERVER_V}
PRODUCT_EMBEDDED = $00000041;
{$EXTERNALSYM PRODUCT_EMBEDDED}
PRODUCT_STARTER_E = $00000042;
{$EXTERNALSYM PRODUCT_STARTER_E}
PRODUCT_HOME_BASIC_E = $00000043;
{$EXTERNALSYM PRODUCT_HOME_BASIC_E}
PRODUCT_HOME_PREMIUM_E = $00000044;
{$EXTERNALSYM PRODUCT_HOME_PREMIUM_E}
PRODUCT_PROFESSIONAL_E = $00000045;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_E}
PRODUCT_ENTERPRISE_E = $00000046;
{$EXTERNALSYM PRODUCT_ENTERPRISE_E}
PRODUCT_ULTIMATE_E = $00000047;
{$EXTERNALSYM PRODUCT_ULTIMATE_E}
PRODUCT_ENTERPRISE_EVALUATION = $00000048; // following Windows 8 SDK
{$EXTERNALSYM PRODUCT_ENTERPRISE_EVALUATION}
PRODUCT_MULTIPOINT_STANDARD_SERVER = $0000004C;
{$EXTERNALSYM PRODUCT_MULTIPOINT_STANDARD_SERVER}
PRODUCT_MULTIPOINT_PREMIUM_SERVER = $0000004D;
{$EXTERNALSYM PRODUCT_MULTIPOINT_PREMIUM_SERVER}
PRODUCT_STANDARD_EVALUATION_SERVER = $0000004F;
{$EXTERNALSYM PRODUCT_STANDARD_EVALUATION_SERVER}
PRODUCT_DATACENTER_EVALUATION_SERVER = $00000050;
{$EXTERNALSYM PRODUCT_DATACENTER_EVALUATION_SERVER}
PRODUCT_ENTERPRISE_N_EVALUATION = $00000054;
{$EXTERNALSYM PRODUCT_ENTERPRISE_N_EVALUATION}
PRODUCT_EMBEDDED_AUTOMOTIVE = $00000055;
{$EXTERNALSYM PRODUCT_EMBEDDED_AUTOMOTIVE}
PRODUCT_EMBEDDED_INDUSTRY_A = $00000056;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY_A}
PRODUCT_THINPC = $00000057;
{$EXTERNALSYM PRODUCT_THINPC}
PRODUCT_EMBEDDED_A = $00000058;
{$EXTERNALSYM PRODUCT_EMBEDDED_A}
PRODUCT_EMBEDDED_INDUSTRY = $00000059;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY}
PRODUCT_EMBEDDED_E = $0000005A;
{$EXTERNALSYM PRODUCT_EMBEDDED_E}
PRODUCT_EMBEDDED_INDUSTRY_E = $0000005B;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY_E}
PRODUCT_EMBEDDED_INDUSTRY_A_E = $0000005C;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY_A_E}
PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER = $0000005F;
{$EXTERNALSYM PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER}
PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER = $00000060;
{$EXTERNALSYM PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER}
PRODUCT_CORE_ARM = $00000061;
{$EXTERNALSYM PRODUCT_CORE_ARM}
PRODUCT_CORE_N = $00000062; // Windows 10 Home
{$EXTERNALSYM PRODUCT_CORE_N}
PRODUCT_CORE_COUNTRYSPECIFIC = $00000063; // Windows 10 Home China
{$EXTERNALSYM PRODUCT_CORE_COUNTRYSPECIFIC}
PRODUCT_CORE_SINGLELANGUAGE = $00000064; // Windows 10 Home Single Language
{$EXTERNALSYM PRODUCT_CORE_SINGLELANGUAGE}
PRODUCT_CORE = $00000065; // Windows 10 Home
{$EXTERNALSYM PRODUCT_CORE}
PRODUCT_PROFESSIONAL_WMC = $00000067; // Professional with Media Center
{$EXTERNALSYM PRODUCT_PROFESSIONAL_WMC}
// following Nov 2017
PRODUCT_MOBILE_CORE = $00000068; // Windows 10 Mobile
{$EXTERNALSYM PRODUCT_MOBILE_CORE}
PRODUCT_EMBEDDED_INDUSTRY_EVAL = $00000069;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY_EVAL}
PRODUCT_EMBEDDED_INDUSTRY_E_EVAL = $0000006A;
{$EXTERNALSYM PRODUCT_EMBEDDED_INDUSTRY_E_EVAL}
PRODUCT_EMBEDDED_EVAL = $0000006B;
{$EXTERNALSYM PRODUCT_EMBEDDED_EVAL}
PRODUCT_EMBEDDED_E_EVAL = $0000006C;
{$EXTERNALSYM PRODUCT_EMBEDDED_E_EVAL}
PRODUCT_NANO_SERVER = $0000006D;
{$EXTERNALSYM PRODUCT_NANO_SERVER}
PRODUCT_CLOUD_STORAGE_SERVER = $0000006E;
{$EXTERNALSYM PRODUCT_CLOUD_STORAGE_SERVER}
PRODUCT_CORE_CONNECTED = $0000006F;
{$EXTERNALSYM PRODUCT_CORE_CONNECTED}
PRODUCT_PROFESSIONAL_STUDENT = $00000070;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_STUDENT}
PRODUCT_CORE_CONNECTED_N = $00000071;
{$EXTERNALSYM PRODUCT_CORE_CONNECTED_N}
PRODUCT_PROFESSIONAL_STUDENT_N = $00000072;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_STUDENT_N}
PRODUCT_CORE_CONNECTED_SINGLELANGUAGE = $00000073;
{$EXTERNALSYM PRODUCT_CORE_CONNECTED_SINGLELANGUAGE}
PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC = $00000074;
{$EXTERNALSYM PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC}
PRODUCT_CONNECTED_CAR = $00000075;
{$EXTERNALSYM PRODUCT_CONNECTED_CAR}
PRODUCT_INDUSTRY_HANDHELD = $00000076;
{$EXTERNALSYM PRODUCT_INDUSTRY_HANDHELD}
PRODUCT_PPI_PRO = $00000077;
{$EXTERNALSYM PRODUCT_PPI_PRO}
PRODUCT_ARM64_SERVER = $00000078;
{$EXTERNALSYM PRODUCT_ARM64_SERVER}
PRODUCT_EDUCATION = $00000079; // Windows 10 Education
{$EXTERNALSYM PRODUCT_EDUCATION}
PRODUCT_EDUCATION_N = $0000007A; // Windows 10 Education
{$EXTERNALSYM PRODUCT_EDUCATION_N}
PRODUCT_IOTUAP = $0000007B; // Windows 10 IoT Core
{$EXTERNALSYM PRODUCT_IOTUAP}
PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER = $0000007C;
{$EXTERNALSYM PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER}
PRODUCT_ENTERPRISE_S = $0000007D; // Windows 10 Enterprise 2015 LTSB
{$EXTERNALSYM PRODUCT_ENTERPRISE_S}
PRODUCT_ENTERPRISE_S_N = $0000007E; // Windows 10 Enterprise 2015 LTSB
{$EXTERNALSYM PRODUCT_ENTERPRISE_S_N}
PRODUCT_PROFESSIONAL_S = $0000007F;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_S}
PRODUCT_PROFESSIONAL_S_N = $00000080;
{$EXTERNALSYM PRODUCT_PROFESSIONAL_S_N}
PRODUCT_ENTERPRISE_S_EVALUATION = $00000081; // Windows 10 Enterprise 2015 LTSB
{$EXTERNALSYM PRODUCT_ENTERPRISE_S_EVALUATION}
PRODUCT_ENTERPRISE_S_N_EVALUATION = $00000082; // Windows 10 Enterprise 2015 LTSB
{$EXTERNALSYM PRODUCT_ENTERPRISE_S_N_EVALUATION}
PRODUCT_IOTUAPCOMMERCIAL = $00000083; // Windows 10 IoT Core Commercial
{$EXTERNALSYM PRODUCT_IOTUAPCOMMERCIAL}
PRODUCT_MOBILE_ENTERPRISE = $00000085; // Windows 10 Mobile Enterprise
{$EXTERNALSYM PRODUCT_MOBILE_ENTERPRISE}
PRODUCT_CLOUD = $000000B2; // Windows 10 S
{$EXTERNALSYM PRODUCT_CLOUD}
PRODUCT_CLOUDN = $000000B3; // Windows 10 S N
{$EXTERNALSYM PRODUCT_CLOUDN}
//{$xENDIF}
const
//from winnt.h
VER_EQUAL = 1;
{$EXTERNALSYM VER_EQUAL}
VER_GREATER = 2;
{$EXTERNALSYM VER_GREATER}
VER_GREATER_EQUAL = 3;
{$EXTERNALSYM VER_GREATER_EQUAL}
VER_LESS = 4;
{$EXTERNALSYM VER_LESS}
VER_LESS_EQUAL = 5;
{$EXTERNALSYM VER_LESS_EQUAL}
VER_AND = 6;
{$EXTERNALSYM VER_AND}
VER_OR = 7;
{$EXTERNALSYM VER_OR}
VER_CONDITION_MASK = 7;
{$EXTERNALSYM VER_CONDITION_MASK}
VER_NUM_BITS_PER_CONDITION_MASK = 3;
{$EXTERNALSYM VER_NUM_BITS_PER_CONDITION_MASK}
VER_MINORVERSION = $0000001;
{$EXTERNALSYM VER_MINORVERSION}
VER_MAJORVERSION = $0000002;
{$EXTERNALSYM VER_MAJORVERSION}
VER_BUILDNUMBER = $0000004;
{$EXTERNALSYM VER_BUILDNUMBER}
VER_PLATFORMID = $0000008;
{$EXTERNALSYM VER_PLATFORMID}
VER_SERVICEPACKMINOR = $0000010;
{$EXTERNALSYM VER_SERVICEPACKMINOR}
VER_SERVICEPACKMAJOR = $0000020;
{$EXTERNALSYM VER_SERVICEPACKMAJOR}
VER_SUITENAME = $0000040;
{$EXTERNALSYM VER_SUITENAME}
VER_PRODUCT_TYPE = $0000080;
{$EXTERNALSYM VER_PRODUCT_TYPE}
// handle for DLL
var
SensapiModule: THandle;
// OS version checking, 4.80 moved from magrasapi
type
TMagOSVersion = (OSW9x, OSNT4, OSW2K, OSWXP, OSVista, OS7, OS8, OS10) ;
// 15 Apr 2015 renamed from TOSVersion to avoid conflict with newer Delphi versions
// Windows Server 2003 is OSWXP, Windows Server 2008 is OSVista, 2008 R2 is 7, 2012 is 8, 2015 is 10
var
MagRasOSVersion: TMagOSVersion ;
// ----------------------------------------------------------------------------
// externals
var
IsDestinationReachable: function (lpszDestination: PWideChar ; var QocInfo: TQocInfo): bool; stdcall; // unicode
IsNetworkAlive: function (var Flags: DWORD): bool; stdcall;
function SHGetSpecialFolderLocation (handle: HWND; nFolderL: integer; LPITEMIDLIST: pointer): bool stdcall;
function SHGetPathFromIDList (LPCITEMIDLIST: pointer; pszPath: PWideChar): bool stdcall; // unicode
function SHEmptyRecycleBin (Wnd:HWnd; pszRootPath:PWideChar; Flags:DWORD):Integer; stdcall; // unicode
function SHGetSpecialFolderLocation; external shell32 name 'SHGetSpecialFolderLocation';
function SHGetPathFromIDList; external shell32 name 'SHGetPathFromIDListW'; // unicode
function SHEmptyRecycleBin; external shell32 name 'SHEmptyRecycleBinW'; // unicode
procedure SetThreadExecutionState(ESFlags: DWORD) ; stdcall; external kernel32 name 'SetThreadExecutionState';
const
ES_SYSTEM_REQUIRED = $00000001;
ES_DISPLAY_REQUIRED = $00000002;
ES_USER_PRESENT = $00000004;
ES_AWAYMODE_REQUIRED = $00000040;
ES_CONTINUOUS = $80000000;
implementation
// 4 Aug 2008 - ANSI versions of common string utils
function TrimAnsi(const S: AnsiString): Ansistring;
var
I, L: Integer;
begin
L := Length(S);
I := 1;
while (I <= L) and (S[I] <= ' ') do Inc(I);
if I > L then Result := '' else
begin
while S[L] <= ' ' do Dec(L);
Result := Copy(S, I, L - I + 1);
end;
end;
function TrimLeftAnsi(const S: AnsiString): AnsiString;
var
I, L: Integer;
begin
L := Length(S);
I := 1;
while (I <= L) and (S[I] <= ' ') do Inc(I);
Result := Copy(S, I, Maxint);
end;
function TrimRightAnsi(const S: Ansistring): AnsiString;
var
I: Integer;
begin
I := Length(S);
while (I > 0) and (S[I] <= ' ') do Dec(I);
Result := Copy(S, 1, I);
end;
{ Author Arno Garrels - Feel free to optimize! }
{ It's anyway faster than the RTL routine. }
function LowerCaseAnsi(const S: AnsiString): AnsiString;
var
Ch : AnsiChar;
L, I : Integer;
Source, Dest: PAnsiChar;
begin
L := Length(S);
if L = 0 then
Result := ''
else begin
SetLength(Result, L);
Source := Pointer(S);
Dest := Pointer(Result);
for I := 1 to L do begin
Ch := Source^;
if Ch in ['A'..'Z'] then Inc(Ch, 32);
Dest^ := Ch;
Inc(Source);
Inc(Dest);
end;
end;
end;
{ Author Arno Garrels - Feel free to optimize! }
{ It's anyway faster than the RTL routine. }
function UpperCaseAnsi(const S: AnsiString): AnsiString;
var
Ch : AnsiChar;
L, I : Integer;
Source, Dest: PAnsiChar;
begin
L := Length(S);
if L = 0 then
Result := ''
else begin
SetLength(Result, L);
Source := Pointer(S);
Dest := Pointer(Result);
for I := 1 to L do begin
Ch := Source^;
if Ch in ['a'..'z'] then Dec(Ch, 32);
Dest^ := Ch;
Inc(Source);
Inc(Dest);
end;
end;
end;
{ Author Arno Garrels - Feel free to optimize! }
{ It's anyway faster than the RTL routine. }
function CompareTextAnsi(const S1, S2: AnsiString): Integer;
var
L1, L2, I : Integer;
MinLen : Integer;
Ch1, Ch2 : AnsiChar;
P1, P2 : PAnsiChar;
begin
L1 := Length(S1);
L2 := Length(S2);
if L1 > L2 then
MinLen := L2
else
MinLen := L1;
P1 := Pointer(S1);
P2 := Pointer(S2);
for I := 1 to MinLen do
begin
Ch1 := P1[I];
Ch2 := P2[I];
if (Ch1 <> Ch2) then
begin
{ Strange, but this is how the original works, }
{ for instance, "a" is smaller than "[" . }
if (Ch1 > Ch2) then
begin
if Ch1 in ['a'..'z'] then
Dec(Byte(Ch1), 32);
end
else begin
if Ch2 in ['a'..'z'] then
Dec(Byte(Ch2), 32);
end;
end;
if (Ch1 <> Ch2) then
begin
Result := Byte(Ch1) - Byte(Ch2);
Exit;
end;
end;
Result := L1 - L2;
end;
{ Author Arno Garrels - Needs optimization! }
{ It's as fast as the RTL routine. }
{ We should realy use a FastCode function here. }
function IntToStrAnsi(N : Integer) : AnsiString;
var
I : Integer;
Buf : array [0..11] of AnsiChar;
Sign : Boolean;
begin
if N >= 0 then
Sign := FALSE
else begin
Sign := TRUE;
if N = Low(Integer) then
begin
Result := '-2147483648';
Exit;
end
else
N := Abs(N);
end;
I := Length(Buf);
repeat
Dec(I);
Buf[I] := AnsiChar(N mod 10 + $30);
N := N div 10;
until N = 0;
if Sign then begin
Dec(I);
Buf[I] := '-';
end;
SetLength(Result, Length(Buf) - I);
Move(Buf[I], Pointer(Result)^, Length(Buf) - I);
end;
{ Author Arno Garrels - Feel free to optimize! }
{ It's anyway faster than the RTL routine. }
function IntToHexAnsi(N : Integer; Digits: Byte) : AnsiString;
var
Buf : array [0..7] of Byte;
V : Cardinal;
I : Integer;
begin
V := Cardinal(N);
I := Length(Buf);
if Digits > I then Digits := I;
repeat
Dec(I);
Buf[I] := V mod 16;
if Buf[I] < 10 then
Inc(Buf[I], $30)
else
Inc(Buf[I], $37);
V := V div 16;
until V = 0;
while Digits > Length(Buf) - I do begin
Dec(I);
Buf[I] := $30;
end;
SetLength(Result, Length(Buf) - I);
Move(Buf[I], Pointer(Result)^, Length(Buf) - I);
end;
function PosAnsi(const Substr, S: AnsiString): Integer;
var
P: PAnsiChar;
begin
Result := 0;
P := AnsiStrPos(PAnsiChar(S), PAnsiChar(SubStr));
if P <> nil then
Result := Integer(P) - Integer(PAnsiChar(S)) + 1;
end;
// borrowed from fastcode due to no widestring stuff in Delphi 7
{$IFNDEF CPUX64}
function StrLenWide(const Str: PWideChar): Cardinal;
asm
{Check the first byte}
cmp word ptr [eax], 0
je @ZeroLength
{Get the negative of the string start in edx}
mov edx, eax
neg edx
@ScanLoop:
mov cx, [eax]
add eax, 2
test cx, cx
jnz @ScanLoop
lea eax, [eax + edx - 2]
shr eax, 1
ret
@ZeroLength:
xor eax, eax
end;
{$ELSE}
function StrLenWide(const Str: PWideChar): Cardinal;
begin
result := WStrLen (str);
end;
{$ENDIF}
function StrLCopyWide(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar;
var
Len: Cardinal;
begin
Result := Dest;
Len := StrLenWide(Source);
if Len > MaxLen then
Len := MaxLen;
Move(Source^, Dest^, Len * SizeOf(WideChar));
Dest[Len] := #0;
end;
function StrPLCopyWide(Dest: PWideChar; const Source: String; MaxLen: Cardinal): PWideChar;
var
W: UnicodeString ;
begin
W := Source ;
Result := StrLCopyWide(Dest, PWideChar(W), MaxLen);
end;
// convert fixed length trailing null string to pascal ansi string
function FixedToPasStr (fixstr: PAnsiChar; fixsize: integer): AnsiString ;
var
temp: AnsiString ;
begin
SetLength (temp, fixsize);
Move (fixstr^, PAnsiChar (temp)^, fixsize); // may include embedded nulls
result := temp ;
end ;
{ gets a null terminated string from within a Delphi string }
function NullTermToPasStr (const nullstr: AnsiString): AnsiString ;
var
I: integer ;
begin
result := nullstr ;
for I := 1 to Length (nullstr) do
begin
if result [I] = null then
begin
SetLength (result, 1 - 1) ;
exit ;
end;
end ;
end;
// separate two PAnsiChar strings
function GetDevNamePort (fixstr: PAnsiChar; fixsize: integer;
var devport: AnsiString): AnsiString ;
var
I: integer ;
temp: AnsiString ;
begin
devport := '' ;
result := '' ;
temp := TrimRightAnsi (FixedToPasStr (fixstr, fixsize)) ;
if Length (temp) = 0 then exit ;
I := CharPos (#0, temp) ; // see if port follows device, NT only
if I > 1 then
begin
temp [I] := '{' ;
devport := LowerCaseAnsi (TrimAnsi (Copy (temp, I + 1, 99))) ;
result := TrimAnsi (Copy (temp, 1, I - 1)) ;
end
else
result := temp ;
end ;
function FixedToPasStrW (fixstr: PWideChar; fixlen: integer): UnicodeString ;
begin
SetLength (Result, fixlen);
Move (fixstr^, PWideChar (result)^, fixlen * 2); // may include embedded nulls
end ;
// separate two PWideChar strings into WideStings
function GetDevNamePortW (fixstr: PWideChar; fixlen: integer;
var devport: UnicodeString): UnicodeString ;
var
I: integer ;
temp: UnicodeString ;
begin
devport := '' ;
result := '' ;
temp := TrimRight (FixedToPasStrW (fixstr, fixlen)) ;
if Length (temp) = 0 then exit ;
I := Pos (#0, temp) ; // see if port follows device, NT only - should call wide version
{ for I := 1 to Length (temp) do
begin
if temp [I] = #0 then break ;
end ; }
if (I > 1) and (I < Length (temp)) then
begin
temp [I] := '{' ;
devport := LowerCase (Trim (Copy (temp, I + 1, 99))) ;
result := Trim (Copy (temp, 1, I - 1)) ;
end
else
result := temp ;
end ;
// returns %System root%
function GetWinDir: String;
var
Path: array [0..MAX_PATH] of WideChar ; // Unicode
begin
Path [0] := #0 ;
GetWindowsDirectoryW (Path, Length (Path)) ; // Unicode
Result := Path ;
end;
// returns a shell path according to the CSIDL literals, ie CSIDL_STARTUP
function GetShellPath (location: integer): string ;
var
PIDL: Pointer;
Path: array [0..MAX_PATH] of WideChar ; // Unicode
begin
Result := '' ;
Path [0] := #0 ;
SHGetSpecialFolderLocation (HInstance, location, @PIDL) ;
if SHGetPathFromIDList (PIDL, Path) = true then Result := Path ;
end ;
// Get the name of the currently logged in user
function GetUsersName: string;
var
Buffer: array[0..255] of WideChar ;
NLen: DWORD ;
begin
Buffer [0] := #0 ;
result := '' ;
NLen := Length (Buffer) ;
if GetUserNameW (Buffer, NLen) then Result := Buffer ;
end;
// get the computer name from networking
function GetCompName: string;
var
Buffer: array[0..255] of WideChar ;
NLen: DWORD ;
begin
Buffer [0] := #0 ;
result := '' ;
NLen := Length (Buffer) ;
if GetComputerNameW (Buffer, NLen) then Result := Buffer ;
end ;
// convert seconds since 1 Jan 1970 (UNIX time stamp) to proper Delphi stuff
function TStamptoDT (stamp: DWORD): TDateTime ;
begin
result := (stamp / SecsPerDay) + 25569 ;
end ;
// convert Delphi time to seconds since 1 Jan 1970 (UNIX time stamp)
function TDTtoStamp (D: TDateTime): DWORD ;
begin
result := 0 ;
if D < 25569 then exit ;
D := D - 25569 ;
if D > 21900 then exit ; // sanity test, year 2030
result := Trunc (D * SecsPerDay) ;
end ;
// This function gets program version information from the string resources
// keys include FileDescription, FileVersion, ProductVersion
function GetFileVerInfo (const AppName, KeyName: string): string ;
const
DEFAULT_LANG_ID = $0409;
DEFAULT_CHAR_SET_ID = $04E4;
type
TTranslationPair = packed record
Lang,
CharSet: word;
end;
PTranslationIDList = ^TTranslationIDList;
TTranslationIDList = array[0..MAXINT div SizeOf(TTranslationPair)-1] of TTranslationPair;
var
buffer, PStr: PWideChar ;
bufsize, temp: DWORD ;
strsize, IDsLen: UInt ;
succflag: boolean ;
LangCharSet, lpSubBlock, WideFileName: UnicodeString ; // Unicode
Dummy: DWORD;
IDs: PTranslationIDList;
// IDCount: integer;
begin
result := '' ;
WideFileName := AppName ;
bufsize := GetFileVersionInfoSizeW (PWideChar(WideFileName), temp) ;
if bufsize = 0 then exit ;
GetMem (buffer, bufsize) ;
try
// get all version info into buffer
succflag := GetFileVersionInfoW (PWideChar(WideFileName), 0, bufsize, buffer) ;
if NOT succflag then exit ;
// set language Id
LangCharSet := '040904E4' ;
lpSubBlock := '\VarFileInfo\Translation' ;
if VerQueryValueW (buffer, PWideChar (lpSubBlock), Pointer(IDs), IDsLen) then
begin
// IDCount := IDsLen div SizeOf(TTranslationPair);
// for Dummy := 0 to IDCount-1 do // only need first language
// begin
Dummy := 0 ;
if (IDs^[Dummy].Lang = 0) and (IDs^[Dummy].CharSet = 0) then // 16 Aug 2011 charset may be zero so don't set default
begin
IDs^[Dummy].Lang := DEFAULT_LANG_ID;
IDs^[Dummy].CharSet := DEFAULT_CHAR_SET_ID;
end;
LangCharSet := Format('%.4x%.4x', [IDs^[Dummy].Lang, IDs^[Dummy].CharSet]) ;
// end;
end;
// now read real information
lpSubBlock := '\StringFileInfo\' + LangCharSet + '\' + KeyName ;
succflag := VerQueryValueW (buffer, PWideChar (lpSubBlock), Pointer (PStr), strsize) ;
temp := strsize ;
if succflag then result := PStr ;
finally
FreeMem (buffer) ;
end ;
end ;
// get ethernet MAC address
// WARNING this code is not totally reliable, does not like multiple adaptors
// and sometimes returns the same adaptor more than once
// IpHlpAdaptersInfo is more reliable for OSs that support it
function GetMACAddresses (Pcname: AnsiString; MacAddresses: TStrings): integer ;
const
HEAP_ZERO_MEMORY = $8;
HEAP_GENERATE_EXCEPTIONS = $4;
type
TAStat = packed record
adapt : nb30.TAdapterStatus ;
NameBuff : array [0..30] of TNameBuffer ;
end;
var
NCB: TNCB ;
Enum: TLanaEnum ;
PASTAT : Pointer ;
AST : TAStat ;
I: integer ;
begin
result := -1 ;
if NOT Assigned (MacAddresses) then exit ; // sanity test
MacAddresses.Clear ;
// For machines with multiple network adapters you need to
// enumerate the LANA numbers and perform the NCBASTAT
// command on each. Even when you have a single network
// adapter, it is a good idea to enumerate valid LANA numbers
// first and perform the NCBASTAT on one of the valid LANA
// numbers. It is considered bad programming to hardcode the
// LANA number to 0 (see the comments section below).
FillChar(NCB, Sizeof(NCB), 0) ;
NCB.ncb_buffer := Pointer (@Enum) ;
NCB.ncb_length := SizeOf (Enum) ;
NCB.ncb_command := AnsiChar (NCBENUM) ;
if NetBios (@NCB) <> Char (NRC_GOODRET) then exit ;
for I := 0 to Pred (Ord (Enum.Length)) do
begin
// The IBM NetBIOS 3.0 specifications defines four basic
// NetBIOS environments under the NCBRESET command. Win32
// follows the OS/2 Dynamic Link Routine (DLR) environment.
// This means that the first NCB issued by an application
// must be a NCBRESET, with the exception of NCBENUM.
// The Windows NT implementation differs from the IBM
// NetBIOS 3.0 specifications in the NCB_CALLNAME field.
FillChar(NCB, Sizeof(NCB), 0);
NCB.ncb_command := AnsiChar(NCBRESET);
NCB.ncb_lana_num := Enum.lana [I] ;
NetBios (@NCB) ;
// To get the Media Access Control (MAC) address for an
// ethernet adapter programmatically, use the Netbios()
// NCBASTAT command and provide a "*" as the name in the
// NCB.ncb_CallName field (in a 16-chr string).
// NCB.ncb_callname = "* "
FillChar(NCB, Sizeof (NCB), 0) ;
FillChar(NCB.ncb_callname [0], 16, ' ') ;
if PCName = '' then PCName := '*' ;
Move (PCName [1], NCB.ncb_callname [0], Length (PCName));
NCB.ncb_command := AnsiChar (NCBASTAT);
NCB.ncb_lana_num := Enum.lana [I] ;
NCB.ncb_length := Sizeof (AST);
PASTAT := HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS or
HEAP_ZERO_MEMORY, NCB.ncb_length) ;
if PASTAT = nil then exit ;
NCB.ncb_buffer := PASTAT;
if NetBios (@NCB) = Char (NRC_GOODRET) then
begin
CopyMemory (@AST, NCB.ncb_buffer, SizeOf (AST)) ;
with AST.adapt do
MacAddresses.Add (Format ('%.2x-%.2x-%.2x-%.2x-%.2x-%.2x',
[Ord (adapter_address [0]), Ord (adapter_address [1]),
Ord (adapter_address [2]), Ord (adapter_address [3]),
Ord (adapter_address [4]), Ord (adapter_address [5])])) ;
HeapFree (GetProcessHeap, 0, PASTAT);
inc (result) ;
end ;
end ;
end;
// the following functions using SENSAPI.DLL need MSIE 5 or later installed
function LoadSensapi: Boolean;
begin
Result := True;
if SensapiModule <> 0 then Exit;
// open DLL
SensapiModule := LoadLibrary (SensapiDLL);
if SensapiModule = 0 then
begin
Result := false;
exit ;
end ;
IsDestinationReachable := GetProcAddress (SensapiModule,
'IsDestinationReachableW') ; // Unioode
IsNetworkAlive := GetProcAddress (SensapiModule, 'IsNetworkAlive') ;
end;
// check whether local system has a LAN or RAS connections
function IsNetAlive (var Flags: DWORD): boolean ;
begin
Flags := 0 ;
result := false ;
if NOT LoadSensapi then exit ;
result := IsNetworkAlive (Flags) ;
end ;
// check whether local system has a LAN or RAS connections and/or can reach
// a specific host, returning some quality of connection information
// uses ping to reach host, which is not very reliable!!!
function IsDestReachable (Dest: string; var QocInfo: TQocInfo): boolean ;
var
WideName: UnicodeString ; // Unicode
begin
WideName := Dest ;
FillChar (QocInfo, SizeOf (QocInfo), #0) ;
QocInfo.dwSize := SizeOf (QocInfo) ;
result := false ;
if NOT LoadSensapi then exit ;
result := IsDestinationReachable (PWideChar (WideName), QocInfo) ;
end ;
// get or update MSIE autodial key in registry
function MSIEAutoDial (var Value: boolean; const Update: boolean): boolean ;
var
IniFile: TRegistry ;
const
AutoDial = 'EnableAutoDial' ;
IntSet = 'Internet Settings' ;
begin
result := false ;
IniFile := TRegistry.Create ;
Try
with IniFile do
begin
try
RootKey := HKEY_CURRENT_USER;
if OpenKey (CVKey + '\' + IntSet, true) then
begin
if Update then
WriteBool (AutoDial, Value)
else
begin
Value := false ;
if ValueExists (AutoDial) then // 4 Aug 2008 ensure values exist
begin
if GetDataType (AutoDial) = rdBinary then // 4.94
ReadBinaryData (AutoDial, Value, GetDataSize(AutoDial)) // 4.94
else
Value := ReadBool (AutoDial) ;
end;
end ;
result := true ;
end ;
CloseKey ;
except
Value := false ;
end ;
end ;
finally
if Assigned (IniFile) then IniFile.Free;
end;
end ;
// get or update MSIE autodial keys in registry // 4.94
// 0=Never Dial A Connection: EnableAutoDial=false, NoNetAutodial=false
// 1=Dial Whenever A Network Connection Is Not Present: EnableAutoDial=true, NoNetAutodial=true
// 2=Always Dial My Default Connection: EnableAutoDial=true, NoNetAutodial=false
function MSIEAutoDialOpt (var Value: integer; const Update: boolean): boolean;
var
IniFile: TRegistry ;
benabledad, bnonetad: boolean ;
const
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
IntSet = 'Internet Settings' ;
AutoDial = 'EnableAutoDial' ;
NoNetAutodial = 'NoNetAutodial' ;
begin
result := false;
IniFile := TRegistry.Create ;
Try
with IniFile do
begin
try
RootKey := HKEY_CURRENT_USER;
if OpenKey(CVKey + '\' + IntSet, True) then
begin
if Update then // Set the values
begin
benabledad := false ;
bnonetad := false ;
if Value = 1 then bnonetad := true ;
if Value >= 1 then benabledad := true ;
WriteBool (AutoDial, benabledad) ;
WriteBool (NoNetAutodial, bnonetad) ;
end
else // Only READ the values
begin
benabledad := false ;
bnonetad := false ;
if ValueExists (AutoDial) and ValueExists (NoNetAutodial) then // 4 Aug 2008 ensure values exist
begin
if GetDataType(AutoDial) = rdBinary then
ReadBinaryData(AutoDial, benabledad, 4)
else
benabledad := ReadBool (AutoDial);
// sometimes get a windows exception reading this key
try
if GetDataType (NoNetAutodial) = rdInteger then // 3 Sept 2012 sometimes DWORD
bnonetad := ReadBool (NoNetAutodial)
else if GetDataType (NoNetAutodial) = rdBinary then
ReadBinaryData (NoNetAutodial, benabledad, 4)
else
bnonetad := ReadBool (NoNetAutodial);
except
bnonetad := false ;
end;
end;
Value := 0 ;
if benabledad then
begin
if bnonetad then
Value := 1
else
Value := 2 ;
end ;
end ;
result := true ;
CloseKey ;
end ;
except
Value := 0 ;
end ;
end ;
finally
if Assigned (IniFile) then IniFile.Free ;
end ;
end ;
// get or update MSIE default connection key in registry
function MSIEDefConn (var ConnName: string; const Update: boolean): boolean ;
var
IniFile: TRegistry ;
const
RemAcc = 'RemoteAccess' ; // W9x/NT4/W2K
IntProf = 'InternetProfile' ; // W9x/NT4/W2K
RasAD = 'Software\Microsoft\RAS AutoDial\Default' ; // XP
RasDef = 'DefaultInternet' ; // XP
begin
result := false ;
if NOT Update then ConnName := '' ;
IniFile := TRegistry.Create ;
Try
with IniFile do
begin
try
if MagRasOSVersion >= OSWXP then // 4.80, new key in Windows XP, 4.94 look in HCU no HLM, 5.21 also Vista
begin
RootKey := HKEY_CURRENT_USER;
if OpenKey (RasAD, false) then
begin
if Update then
WriteString (RasDef, ConnName)
else
ConnName := ReadString (RasDef) ;
result := true ;
end ;
// 5.21 Just in case the connection is set a usable for all users Windows Vista Build > 5717
// The value below will overwrite the CU value if a LM value is ALSO available (Can happen!)
RootKey := HKEY_LOCAL_MACHINE;
if OpenKey(RasAD, false) then
begin
if Update then
WriteString (RasDef, ConnName)
else
begin
if ConnName='' then ConnName := ReadString (RasDef) ;
end;
result := true ;
end;
end
else
begin
RootKey := HKEY_CURRENT_USER;
if OpenKey (RemAcc, false) then
begin
if Update then
WriteString (IntProf, ConnName)
else
ConnName := ReadString (IntProf) ;
result := true ;
end ;
CloseKey ;
end ;
except
end ;
end ;
finally
if Assigned (IniFile) then IniFile.Free;
end;
end ;
// borrowed from fileutils - removed raise exception
function ExcludeTrailingBackslash(const S: string): string;
begin
Result := S;
if IsPathDelimiter(Result, Length(Result)) then
SetLength(Result, Length(Result)-1);
end;
function DirectoryExists(const Name: string): Boolean;
var
Code: DWORD;
begin
Code := GetFileAttributes (PChar(Name)) ;
Result := (Code <> $FFFFFFFF) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0) ;
end;
function ExcludeTrailingPathDelimiter(const S: string): string;
begin
Result := S;
if IsPathDelimiter(Result, Length(Result)) then
SetLength(Result, Length(Result)-1);
end;
function ForceDirs (Dir: string): Boolean;
begin
Result := True;
if Length(Dir) = 0 then
begin
Result := false ;
exit ;
end ;
Dir := ExcludeTrailingPathDelimiter(Dir);
if (Length(Dir) < 3) or DirectoryExists(Dir)
or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
Result := ForceDirs (ExtractFilePath(Dir)) and CreateDir (Dir);
end;
// get windows name and version
function IsWin95: boolean ;
begin
if OsInfo.dwPlatformId = 0 then GetOSInfo ;
result := false ;
if OsInfo.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then result := true ;
end ;
function IsWinNT: boolean ;
begin
if OsInfo.dwPlatformId = 0 then GetOSInfo ;
result := false ;
if OsInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then result := true ;
end ;
function IsWin2K: boolean ;
begin
result := false ;
if IsWinNT and (OsInfo.dwMajorVersion >= 5) then result := true ;
end ;
function IsWinXP: boolean ;
begin
result := false ;
if IsWin2K and (OsInfo.dwMinorVersion > 0) then result := true ;
end ;
function IsWinXPE: boolean ;
begin
result := false ;
if IsWinXP and (((OsInfo.wSuiteMask AND VER_SUITE_EMBEDDEDNT) <> 0) or
((OsInfo.wSuiteMask AND VER_SUITE_EMBEDDED_RESTRICTED) <> 0)) then result := true ;
end ;
function IsWin2K3: boolean ;
begin
result := false ;
if IsWin2K and (OsInfo.dwMinorVersion >= 2) then result := true ;
end ;
function IsWinVista: boolean ;
begin
result := false ;
if IsWinNT and (OsInfo.dwMajorVersion = 6) and
(OsInfo.wProductType <= VER_NT_WORKSTATION) then result := true ;
end ;
function IsWin2K8: boolean ;
begin
result := false ;
if IsWinNT and (OsInfo.dwMajorVersion = 6) and
(OsInfo.wProductType > VER_NT_WORKSTATION) then result := true ;
end ;
// 10 March 2014 see if touch tablet
function IsTouchTablet: boolean ;
var
mask: DWORD ;
begin
result := false ;
if IsWinNT and (OsInfo.dwMajorVersion >= 6) and (GetSystemMetrics (SM_TABLETPC) > 0) then
begin
mask := GetSystemMetrics (SM_DIGITIZER) ;
if mask > 0 then result := true ;
// could check for specific digitizers...
end;
end;
function IsWin64: boolean ; // 3 August 2011
begin
{$IFDEF CPUX64}
result := true ;
{$ELSE}
result := false ;
{$ENDIF}
end ;
// 14 Dec 2009 are we running under a 64-bit windows OS
function IsWow64: boolean ;
var
IsWow64Process: TIsWow64Process;
flag: BOOL;
begin
result := false ;
IsWow64Process := GetProcAddress (GetModuleHandle ('kernel32'), 'IsWow64Process') ;
if Assigned(IsWow64Process) then
begin
flag := false ; // warning, returns false for 64-bit application under 64-bit windows
if IsWow64Process (GetCurrentProcess(), flag) then result := flag ;
end ;
end ;
// 17 May 2013, for Win32 apps on Win64 OS, disable WOW64 file system redirection, follow with RevertWow64Redir
function DisableWow64Redir (var OldRedir: BOOL): boolean ;
var
Wow64DisableWow64FsRedirection: TWow64DisableWow64FsRedirection ;
begin
result := false ;
if IsWin64 then exit ;
if NOT IsWow64 then exit ;
Wow64DisableWow64FsRedirection := GetProcAddress (GetModuleHandle ('kernel32'), 'Wow64DisableWow64FsRedirection') ;
if Assigned (Wow64DisableWow64FsRedirection) then
begin
result := Wow64DisableWow64FsRedirection (OldRedir) ;
end ;
end;
// 17 May 2013, for Win32 apps on Win64 OS, revert WOW64 file system redirection after DisableWow64Redir
function RevertWow64Redir (OldRedir: BOOL): boolean ;
var
Wow64RevertWow64FsRedirection: TWow64RevertWow64FsRedirection ;
begin
result := false ;
if IsWin64 then exit ;
if NOT IsWow64 then exit ;
Wow64RevertWow64FsRedirection := GetProcAddress (GetModuleHandle ('kernel32'), 'Wow64RevertWow64FsRedirection') ;
if Assigned (Wow64RevertWow64FsRedirection) then
begin
result := Wow64RevertWow64FsRedirection (OldRedir) ;
end ;
end;
// get a string HLM registry entry - Nov 2017
function MagGetRegHlmStr (const RegKey, RegValue: string): string ;
var
IniFile: TRegistry ;
begin
result := '' ;
IniFile := TRegistry.Create ;
Try
with IniFile do
begin
try
RootKey := HKEY_LOCAL_MACHINE;
Access := KEY_QUERY_VALUE ;
if OpenKey (RegKey, false) then
begin
if ValueExists (RegValue) then
begin
if GetDataType (RegValue) = rdString then
result := ReadString (RegValue) ;
end;
end ;
CloseKey ;
except
end ;
end ;
finally
if Assigned (IniFile) then IniFile.Free;
end;
end ;
// GetProductInfo is Vista and later only 10 Aug 2010
function LoadProdInfoPtr: Boolean;
var
Kernel: THandle;
begin
Result := false ;
if (OsInfo.dwPlatformId <> VER_PLATFORM_WIN32_NT) then exit ;
if (OsInfo.dwMajorVersion < 6) then exit ;
Kernel := GetModuleHandle(Windows.Kernel32) ;
if Kernel = 0 then exit ;
Result := true ;
if NOT Assigned (GetProductInfo) then
begin
@GetProductInfo := GetProcAddress (Kernel, 'GetProductInfo') ;
@VerifyVersionInfoW := GetProcAddress (Kernel, 'VerifyVersionInfoW') ; // 10 March 2014 - W2K and later
@VerSetConditionMask := GetProcAddress (Kernel, 'VerSetConditionMask') ; // 10 March 2014 - W2K and later
end ;
end;
function GetOSVersion: string ;
var
info, inf2, inf3: string ;
ProductType: longword ; // 10 Aug 2010
begin
if OsInfo.dwPlatformId = 0 then GetOSInfo ;
case OsInfo.dwPlatformId of
VER_PLATFORM_WIN32s: info := 'Windows 3.1';
VER_PLATFORM_WIN32_WINDOWS:
begin
info := 'Windows 95';
if OsInfo.dwMinorVersion >= 10 then info := 'Windows 98';
if OsInfo.dwMinorVersion >= 90 then info := 'Windows ME';
end ;
VER_PLATFORM_WIN32_NT:
begin
inf2 := '' ;
inf3 := '' ;
if OsInfo.wProductType = VER_NT_WORKSTATION then inf2 := ' WS' ;
if OsInfo.wProductType = VER_NT_DOMAIN_CONTROLLER then inf2 := ' Domain Cont' ;
if OsInfo.wProductType = VER_NT_SERVER then inf2 := ' Server' ;
if (OsInfo.wSuiteMask AND VER_SUITE_SMALLBUSINESS) <> 0 then inf2 := ' SmallBus' ;
if (OsInfo.wSuiteMask AND VER_SUITE_ENTERPRISE) <> 0 then inf2 := ' Enterprise' ;
if (OsInfo.wSuiteMask AND VER_SUITE_DATACENTER) <> 0 then inf2 := ' Datacentre' ;
if (OsInfo.wSuiteMask AND VER_SUITE_BLADE) <> 0 then inf2 := ' Web Server' ;
if (OsInfo.wSuiteMask AND VER_SUITE_STORAGE_SERVER) <> 0 then inf2 := ' Storage Server' ;
if (OsInfo.wSuiteMask AND VER_SUITE_COMPUTE_SERVER) <> 0 then inf2 := ' Compute Cluster' ;
info := 'Windows NT' + inf2 ;
// 16 Aug 2010 Vista and later reports editions with new API and literals
if OsInfo.dwMajorVersion >= 6 then
begin
if NOT LoadProdInfoPtr then exit ;
if GetProductInfo (OsInfo.dwMajorVersion, OsInfo.dwMinorVersion,
OsInfo.wServicePackMajor, OsInfo.wServicePackMinor, ProductType) then
begin
case ProductType of
PRODUCT_ULTIMATE, PRODUCT_ULTIMATE_E: inf3 := ' Ultimate' ;
PRODUCT_PROFESSIONAL, PRODUCT_PROFESSIONAL_E: inf3 := ' Professional' ;
PRODUCT_HOME_PREMIUM, PRODUCT_HOME_PREMIUM_E: inf3 := ' Home Premium' ;
PRODUCT_HOME_BASIC, PRODUCT_HOME_BASIC_E: inf3 := ' Home Basic' ;
PRODUCT_ENTERPRISE, PRODUCT_ENTERPRISE_E: inf3 := ' Enterprise' ;
PRODUCT_ENTERPRISE_S, PRODUCT_ENTERPRISE_S_N: inf3 := ' Enterprise 2015 LTSB' ; // Nov 2017
PRODUCT_BUSINESS: inf3 := ' Business' ;
PRODUCT_STARTER, PRODUCT_STARTER_E: inf3 := ' Starter' ;
PRODUCT_CLUSTER_SERVER, PRODUCT_CLUSTER_SERVER_V: inf3 := ' HPC Server' ;
PRODUCT_DATACENTER_SERVER: inf3 := ' Datacenter' ;
PRODUCT_DATACENTER_SERVER_CORE: inf3 := ' Datacenter (core)' ;
PRODUCT_ENTERPRISE_SERVER: inf3 := ' Enterprise' ;
PRODUCT_ENTERPRISE_SERVER_CORE: inf3 := ' Enterprise (core)' ;
PRODUCT_ENTERPRISE_SERVER_IA64: inf3 := ' Enterprise Itanium' ;
PRODUCT_SMALLBUSINESS_SERVER: inf3 := ' Small Business Server' ;
PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:inf3 := ' Small Business Server Premium' ;
PRODUCT_STANDARD_SERVER: inf3 := ' Standard' ;
PRODUCT_STANDARD_SERVER_CORE: inf3 := ' Standard (core)' ;
PRODUCT_WEB_SERVER: inf3 := ' Web Server' ;
PRODUCT_NANO_SERVER: inf3 := ' Nano Server' ; // Nov 2017
PRODUCT_EMBEDDED: inf3 := ' Embedded' ;
PRODUCT_HYPERV: inf3 := ' Hyper-V' ;
PRODUCT_HOME_SERVER: inf3 := ' Home Server' ;
PRODUCT_CORE, PRODUCT_CORE_N, PRODUCT_CORE_SINGLELANGUAGE: inf3 := ' Home' ; // Nov 2017 Windows 10 Home
PRODUCT_CLOUD, PRODUCT_CLOUDN: inf3 := ' S' ; // Nov 2017 Windows S (streamlined, school)
PRODUCT_HOME_PREMIUM_SERVER: inf3 := ' Home Premium Server' ;
PRODUCT_CORE_ARM: inf3 := ' ARM' ; // Windows 8,
PRODUCT_CORE_COUNTRYSPECIFIC: inf3 := ' China' ; // Nov 2017
PRODUCT_IOTUAP, PRODUCT_IOTUAPCOMMERCIAL: inf3 := ' IoT Core' ; // Nov 2017
PRODUCT_EDUCATION, PRODUCT_EDUCATION_N: inf3 := ' Education' ; // Nov 2017
PRODUCT_MOBILE_CORE, PRODUCT_MOBILE_ENTERPRISE: inf3 := ' Mobile' ; // Nov 2017
else
inf3 := ' Unknown ProductType x' + IntToHex (ProductType, 2) ;
end;
end;
end;
// now find major version name
if OsInfo.dwMajorVersion = 5 then
begin
info := 'Windows 2000' + inf2 ;
if OsInfo.dwMinorVersion = 1 then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
begin
if (OsInfo.wSuiteMask AND VER_SUITE_PERSONAL) <> 0 then
begin
if GetSystemMetrics (SM_MEDIACENTER) > 0 then
info := 'Windows XP Media Centre'
else if GetSystemMetrics (SM_STARTER) > 0 then
info := 'Windows XP Starter'
else if GetSystemMetrics (SM_TABLETPC) > 0 then
info := 'Windows XP Tablet PC'
else
info := 'Windows XP Home' ;
end
else if ((OsInfo.wSuiteMask AND VER_SUITE_EMBEDDEDNT) <> 0) or
((OsInfo.wSuiteMask AND VER_SUITE_EMBEDDED_RESTRICTED) <> 0) then
info := 'Windows XP Embedded'
else if ((OsInfo.wSuiteMask AND VER_SUITE_WH_SERVER) <> 0) then
info := 'Windows Home Server'
else
info := 'Windows XP Pro' ;
end ;
end
else if OsInfo.dwMinorVersion = 2 then
begin
if GetSystemMetrics (SM_SERVERR2)> 0 then
info := 'Windows Server 2003 R2' + inf2
else
info := 'Windows Server 2003' + inf2
end
else if OsInfo.dwMinorVersion >= 3 then
info := 'Unknown Windows 2000 version' ;
end
else if OsInfo.dwMajorVersion = 6 then
begin
if OsInfo.dwMinorVersion = 0 then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
info := 'Windows Vista' + inf3
else
info := 'Windows Server 2008' + inf3 ; // Longhorn
end
else if OsInfo.dwMinorVersion = 1 then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
info := 'Windows 7' + inf3 // 4 Nov 2008
else
info := 'Windows Server 2008 R2' + inf3 ; // 22 Jan 2009
end
else if OsInfo.dwMinorVersion = 2 then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
info := 'Windows 8' + inf3 // 7 July 2011
else
info := 'Windows Server 2012' + inf3 ; // 6 July 2012
end
else if OsInfo.dwMinorVersion = 3 then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
info := 'Windows 8.1' + inf3 // 3 April 2013
else
info := 'Windows Server 2012 R2' + inf3 ;
end
else
info := 'Unknown Windows 6 version' ;
end
else if OsInfo.dwMajorVersion = 10 then // 16 Apr 2015
begin
// Nov 2017 added version, ie 1709
inf2 := MagGetRegHlmStr (CVNTKey, 'DisplayVersion') ; // July 2021 20H2, replaces ReleaseId
if inf2 = '' then inf2 := MagGetRegHlmStr (CVNTKey, 'ReleaseId'); // last used for 2009
if inf2 <> '' then inf3 := inf3 + space + inf2;
if (OsInfo.dwMinorVersion = 0) then
begin
if OsInfo.wProductType <= VER_NT_WORKSTATION then
begin
if OsInfo.dwBuildNumber >= 20000 then // Windows 11 seems to be build number based
info := 'Windows 11' + inf3 // July 2021
else
info := 'Windows 10' + inf3; // 3 Oct 2014
end
else begin
if OsInfo.dwBuildNumber >= 20000 then // Server 2021
info := 'Windows Server 2022' + inf3 // July 2021
else if OsInfo.dwBuildNumber >= 17677 then // Server 2019 preview 1803, should be 1809!
info := 'Windows Server 2019' + inf3 // Oct 2018
else
info := 'Windows Server 2016' + inf3 ; // July 2015
end;
end
else
info := 'Unknown Windows 10 version' ;
end
else info := 'Unknown Windows Major version' ;
end
else
info := 'Unknown Windows platform' ;
end;
// 10 March 2014 see if tablet
if IsTouchTablet then
info := info + ' Tablet' ;
if IsWin64 then // 22 July 2011
info := info + ' Win64 '
else if IsWow64 then // 14 Dec 2009
info := info + ' 64-bit '
else
info := info + ' 32-bit ' ;
info := info + IntToStr(OsInfo.dwMajorVersion) + '.' +
IntToStr(OsInfo.dwMinorVersion) + '.' + IntToStr(LOWORD(OsInfo.dwBuildNumber)) ;
if (OsInfo.szCSDVersion [0] <> null) and (OsInfo.wServicePackMajor > 0) then
info := info + ' SP' + IntToStr (OsInfo.wServicePackMajor)
else if OsInfo.szCSDVersion <> '' then
info := info + ' ' + OsInfo.szCSDVersion ;
result := info ;
end ;
// 8 Aug 2002 - try and get extended info with service packs and product
// 10 March 2014 - with Windows 8.1 and later GetVersionEx returns 8.0 unless manifest says 8.1 or 10 compatible.
// Aug 2015 kernel mode version gets accurate OS
// note this function is called from Initliasation of this unit
procedure GetOSInfo ;
begin
FillChar (OsInfo, sizeof (TOSVERSIONINFOEXW), 0) ;
FillChar (OsInfoRaw, sizeof (TOSVERSIONINFOEXW), 0) ;
OsInfo.dwOSVersionInfoSize := sizeof (TOSVERSIONINFOEXW); // NT4 SP6 and later
OsInfoRaw.dwOSVersionInfoSize := sizeof (TOSVERSIONINFOEXW); // NT4 SP6 and later
if NOT GetVersionExW2 (OsInfoRaw) then
begin
OsInfoRaw.dwOSVersionInfoSize := sizeof (TOSVERSIONINFOW); // fall back to older version
GetVersionExW2 (OsInfoRaw) ;
end;
if RtlGetVersion (OsInfo) <> 0 then // Aug 2015 kernel mode version gets accurate OS - Windows 2000 and later
begin
OsInfo := OsInfoRaw ; // not available, fall back
end;
end ;
// validation functions, don't use sets for Unicode
function IsSpace (Ch: Char): Boolean ;
begin
Result := (Ch = ' ') or (Ch = Char($09)) ;
end;
function IsLetterOrDigit (Ch: Char): Boolean ;
begin
Result := ((Ch >= 'a') and (Ch <= 'z')) or
((Ch >= 'A') and (Ch <= 'Z')) or
((Ch >= '0') and (Ch <= '9')) ;
end;
function IsDigit(Ch : Char): Boolean;
begin
Result := (ch >= '0') and (ch <= '9');
end;
function IsPathSep (Ch: Char): Boolean ;
begin
Result := (Ch = '.') or (Ch = '\') or (Ch = ':') ;
end;
function IsDigitsDec (info: string; decimal: boolean) : boolean ;
var
count, len: integer ;
onedotflag: boolean ;
begin
result := false ;
onedotflag := false ;
info := trim (info) ;
len := length (info) ;
if len = 0 then exit ;
for count := 1 to len do
begin
if NOT IsDigit (info [count]) then
begin // allow minus sign at start
if (count <> 1) then
begin
if NOT decimal then exit ;
if info [count] <> MyFormatSettings.DecimalSeparator then exit ;
if onedotflag then exit ;
onedotflag := true ;
end
else
begin
if (info [1] = '-') or (info [1] = '+') then
begin
if (len = 1) then exit ;
end
else
exit ;
end ;
end ;
end ;
result := true ;
end ;
function IsDigits (info: string) : boolean ;
begin
result := IsDigitsDec (info, false) ;
end ;
// swap any number of bytes, integer, double, extended, anything
// ByteSwaps (@value, sizeof (value)) ;
procedure ByteSwaps(DataPtr : Pointer;NoBytes : integer);
var
i : integer;
dp : PAnsiChar;
tmp : AnsiChar;
begin
// Perform a sanity check to make sure that the function was called properly
if (NoBytes > 1) then
begin
Dec(NoBytes);
dp := PAnsiChar(DataPtr);
// we are now safe to perform the byte swapping
for i := NoBytes downto (NoBytes div 2 + 1) do
begin
tmp := PAnsiChar(Integer(dp)+i)^;
PAnsiChar(Integer(dp)+i)^ := PAnsiChar(Integer(dp)+NoBytes-i)^;
PAnsiChar(Integer(dp)+NoBytes-i)^ := tmp;
end;
end;
end;
// convert binary or BCD strings to hex
procedure ConvHexStr (instr: string; var outstr: string) ;
var
flen, inx, nr1, nr2, outpos: integer ;
begin
flen := Length (instr) ; // original BCD or binary field
if flen = 0 then exit ;
SetLength (outstr, flen * 2) ;
outpos := 1 ;
for inx := 1 To flen do
begin
nr1 := ord (instr [inx]) ;
nr2 := nr1 SHR 4 ; // hi nybble
If (nr2 > 9) then nr2 := nr2 + 7 ; // handle ascii characters
outstr [outpos] := Chr (nr2 + 48) ;
inc (outpos) ;
nr2 := nr1 and 15 ; // lo nybble
If (nr2 > 9) then nr2 := nr2 + 7 ; // handle ascii characters
outstr [outpos] := Chr(nr2 + 48) ;
inc (outpos) ;
end ;
End ;
// convert cardinal into eight hex bytes
function ConIntHex (value: cardinal): string ;
var
reshex: string ;
serbin: string [6] ;
begin
Move (value, serbin [1], 4) ;
ByteSwaps (@serbin [1], 4) ;
serbin [0] := chr(4) ;
ConvHexStr (string (serbin), reshex) ; // 7 Aug 2010
result := reshex ;
end ;
function StripQuotes (filename: string): string ;
var
delim: char ;
flen: integer ;
begin
// strip file name delimiters
result := filename ;
flen := length (filename) ;
if flen < 2 then exit ;
delim := filename [1] ;
if ((delim = SQUOTE) or (delim = DQUOTE)) then
begin
if (filename [flen] = delim) then
begin
if flen > 2 then
result := copy (filename, 2, flen - 2)
else
result := '' ;
end ;
end ;
if (delim = '<') then
begin
if (filename [flen] = '>') then
begin
if flen > 2 then
result := copy (filename, 2, flen - 2)
else
result := '' ;
end ;
end ;
end ;
function StripNewLines (const S: string): string;
var
I: Integer;
begin
result := S ;
if Length (result) = 0 then exit ;
for I := 1 to Length (result) do
begin
if (result [I] = CR) or (result [I] = LF) or // Unicode
(result [I] = TAB) then result [I] := Space ;
end ;
end;
// builds list of files in a directory, but without search path!
function IndexFiles (searchfile: string; mask: integer;
var FileList: TStringList; var totsize: cardinal): integer ;
var
SearchRec: TSearchRec ;
SearchResult: integer ;
begin
totsize := 0 ;
result := 0 ;
if NOT Assigned (FileList) then exit ; // 14 Feb 2005
try
FileList.Clear ;
// loop through directory getting all file names in directory
SearchResult := SysUtils.FindFirst (searchfile, mask, SearchRec) ;
while SearchResult = 0 do
begin
if ((SearchRec.Attr and mask) = SearchRec.Attr) then
begin
if (SearchRec.Name <> '.') and
(SearchRec.Name <> '..') then
begin
FileList.Add (SearchRec.Name) ;
inc (totsize, SearchRec.Size) ;
end ;
end ;
SearchResult := SysUtils.FindNext (SearchRec);
end;
SysUtils.FindClose (SearchRec);
FileList.Sort ;
result := FileList.Count ;
except
SysUtils.FindClose (SearchRec);
result := 0 ;
end ;
end ;
// delete multiple files, allowing wildcards, returns total zapped
function DeleteOldFiles (fname: string): integer ;
var
flist: TStringList ;
I: integer ;
totsize: cardinal ;
begin
result := 0 ;
flist := TStringList.Create ;
try
if IndexFiles (fname, faNormArch, flist, totsize) = 0 then exit ;
for I := 0 to Pred (flist.Count) do
begin
if SysUtils.DeleteFile (ExtractFilePath (fname) + flist [I]) then
inc (result) ;
end ;
finally
flist.Free ;
end ;
end ;
function GetEnvirVar (const Name: UnicodeString): string ;
var
Buffer: array[0..1023] of WideChar;
len: integer ;
begin
result := '' ;
len := GetEnvironmentVariableW (PWideChar (Name), Buffer, Length (Buffer)) ;
if len <> 0 then result := buffer ;
end;
function StripChars (AString, AChars: String): String ;
var
K: integer ;
begin
if Length (AChars) <> 0 then
begin
while Length (AString) <> 0 do
begin
K := Pos (AChars, AString) ;
if K = 0 then break ;
Delete (AString, K, Length (AChars)) ;
end ;
end ;
result := AString ;
end ;
function StripCharsAnsi (AString, AChars: AnsiString): AnsiString ;
var
K: integer ;
begin
if Length (AChars) <> 0 then
begin
while Length (AString) <> 0 do
begin
K := PosAnsi (AChars, AString) ;
if K = 0 then break ;
Delete (AString, K, Length (AChars)) ;
end ;
end ;
result := AString ;
end ;
function StripChar (const AString: String; const AChar: Char): String ;
var
Ch: Char;
L, M: Integer;
Source, Dest: PChar;
begin
L := Length (AString) ;
SetLength (Result, L) ;
Source := Pointer (AString) ;
Dest := Pointer (Result) ;
M := 0 ;
while L <> 0 do
begin
Ch := Source^ ;
if AChar = #255 then // special case means all control codes
begin
if (Ch >= space) then
begin
Dest^ := Ch ;
Inc (Dest) ;
Inc (M) ;
end ;
end
else
begin
if (Ch <> AChar) then
begin
Dest^ := Ch ;
Inc(Dest) ;
Inc(M) ;
end ;
end ;
Inc(Source);
Dec(L);
end;
SetLength(Result, M);
end ;
function StripCharAnsi (const AString: AnsiString; const AChar: AnsiChar): AnsiString ;
var
Ch: AnsiChar;
L, M: Integer;
Source, Dest: PAnsiChar;
begin
L := Length (AString) ;
SetLength (Result, L) ;
Source := Pointer (AString) ;
Dest := Pointer (Result) ;
M := 0 ;
while L <> 0 do
begin
Ch := Source^ ;
if AChar = #255 then // special case means all control codes
begin
if (Ch >= space) then
begin
Dest^ := Ch ;
Inc (Dest) ;
Inc (M) ;
end ;
end
else
begin
if (Ch <> AChar) then
begin
Dest^ := Ch ;
Inc(Dest) ;
Inc(M) ;
end ;
end ;
Inc(Source);
Dec(L);
end;
SetLength(Result, M);
end ;
function StripSpaces (const AString: String): String ;
begin
result := StripChar (AString, space) ;
end ;
function StripSpacesAnsi (const AString: AnsiString): AnsiString ;
begin
result := StripCharAnsi (AString, space) ;
end ;
function StripCommas (const AString: String): String ;
begin
result := StripChar (AString, comma) ;
end ;
function StripCommasAnsi (const AString: AnsiString): AnsiString ;
begin
result := StripCharAnsi (AString, comma) ;
end ;
function StripNulls (const AString: String): String ;
begin
result := StripChar (AString, nulll) ;
end ;
function StripNullsAnsi (const AString: AnsiString): AnsiString ;
begin
result := StripCharAnsi (AString, nulll) ;
end ;
function StripAllCntls (const AString: String): String ;
begin
result := StripChar (AString, #255) ;
end ;
function StripAllCntlsAnsi (const AString: AnsiString): AnsiString ;
begin
result := StripCharAnsi (AString, #255) ;
end ;
// convert upper case string to upper and lower (upper start and after punc)
function UpAndLower(const S: String): String ;
var
Ch, LCh: Char;
L, I: Integer;
Source, Dest: PChar;
begin
L := Length(S);
SetLength(Result, L);
Source := Pointer(S);
Dest := Pointer(Result);
LCh := #32 ;
I := 1 ;
while L <> 0 do
begin
Ch := Source^;
if (Ch >= 'A') and (Ch <= 'Z') and (LCh <> #32) then Inc(Ch, 32);
Dest^ := Ch;
LCh := Ch ;
// if (LCh in ['-','/','.','(',')','+','_','=']) then LCh := #32 ;
if (LCh = '-') or (LCh = '/') or (LCh = '.') or (LCh = ',') or // Unicode
(LCh = '(') or (LCh = ')') or (LCh = '+') or (LCh = '_') or (LCh = '=') then LCh := #32 ;
// 13 Nov 2009 fixed missing () from unicode change
if (LCh = '''') then // Oct 2012 added ' for O'Neal but not Fred's
begin
if I = 2 then LCh := #32 ;
end;
Inc(Source);
Inc(Dest);
Dec(L);
Inc(I);
end;
end;
function UpAndLowerAnsi (const S: AnsiString): AnsiString ;
var
Ch, LCh: AnsiChar;
L: Integer;
Source, Dest: PAnsiChar;
begin
L := Length(S);
SetLength(Result, L);
Source := Pointer(S);
Dest := Pointer(Result);
LCh := #32 ;
while L <> 0 do
begin
Ch := Source^;
if (Ch >= 'A') and (Ch <= 'Z') and (LCh <> #32) then Inc(Ch, 32);
Dest^ := Ch;
LCh := Ch ;
// if (LCh in ['-','/','.','(',')','+','_','=']) then LCh := #32 ;
if (LCh = '-') or (LCh = '/') or (LCh = '.') or (LCh = ',') or // Unicode
(LCh = '+') or (LCh = '_') or (LCh = '=') then LCh := #32 ;
Inc(Source);
Inc(Dest);
Dec(L);
end;
end;
// translate specific single characters in a string to another single character
procedure StringTranChAnsi (var S: AnsiString; FrCh, ToCh: AnsiChar) ;
var
L: Integer;
Source: PAnsiChar;
begin
UniqueString (S) ;
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ = FrCh) then Source^ := ToCh ;
Inc (Source) ;
Dec (L) ;
end;
end;
procedure StringTranCh (var S: String; FrCh, ToCh: Char) ; // Unicode
var
L: Integer;
Source: PChar;
begin
UniqueString (S) ; // 10 July 2002
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ = FrCh) then Source^ := ToCh ;
Inc (Source) ;
Dec (L) ;
end;
end;
procedure StringTranChWide (var S: UnicodeString; FrCh, ToCh: WideChar) ; // Unicode
var
L: Integer;
Source: PWideChar;
begin
UniqueString (S) ;
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ = FrCh) then Source^ := ToCh ;
Inc (Source) ;
Dec (L) ;
end;
end;
// translate some common ASCII control codes to hi- 8-bit characters (to save in registry)
// 8-bits are punctuation unlikely to be used in file names or URLs
procedure StringCtrlSafe (var S: AnsiString) ;
begin
StringTranChAnsi (S, CR, #139) ;
StringTranChAnsi (S, LF, #155) ;
StringTranChAnsi (S, TAB, #171) ;
StringTranChAnsi (S, RECSEP, #187) ;
end ;
function StrCtrlSafe (const S: AnsiString): AnsiString ;
begin
result := S ;
StringCtrlSafe (result) ;
end ;
// restore some common ASCII control codes from hi- 8-bit characters
procedure StringCtrlRest (var S: AnsiString) ;
begin
StringTranChAnsi (S, #139, CR) ;
StringTranChAnsi (S, #155, LF) ;
StringTranChAnsi (S, #171, TAB) ;
StringTranChAnsi (S, #187, RECSEP) ;
end ;
function StrCtrlRest (const S: AnsiString): AnsiString ;
begin
result := S ;
StringCtrlRest (result) ;
end ;
// simple translation for illegal file name characters
procedure StringFileTran (var S: String) ;
begin
StringTranCh (S, '/', ' ') ;
StringTranCh (S, ':', ' ') ;
StringTranCh (S, '\', ' ') ;
end ;
function StrFileTran (const S: String): String ;
begin
result := S ;
StringFileTran (result) ;
end ;
procedure StringFileTranEx (var S: String) ;
begin
StringTranCh (S, '/', '_') ;
StringTranCh (S, ':', '_') ;
StringTranCh (S, '\', '_') ;
end ;
function StrFileTranEx (const S: String): String ;
begin
result := S ;
StringFileTranEx (result) ;
end ;
// convert path separators from UNIX to DOS
procedure UnixToDosPath (var S: String) ;
begin
StringTranCh (S, '/', '\') ;
end;
procedure UnixToDosPathW (var S: UnicodeString) ;
begin
StringTranChWide (S, '/', '\') ;
end;
function UnxToDosPath (const S: String): String ;
begin
result := S ;
UnixToDosPath (result) ;
end ;
// convert path separators from DOS to UNIX
procedure DosToUnixPath (var S: String) ;
begin
StringTranCh (S, '\', '/') ;
end;
procedure DosToUnixPathW (var S: UnicodeString) ;
begin
StringTranChWide (S, '\', '/') ;
end;
function DosToUnxPath (const S: String): String ;
begin
result := S ;
DosToUnixPath (result) ;
end ;
function EscapeBackslashes(const S: string): string; // 22 June 2010
var
I: Integer;
begin
Result := S;
for I := Length(Result) downto 1 do
if Result[I] = '\' then Insert('\', Result, I);
end;
// replace control codes with spaces, true if string changed
function StringRemCntls (var S: String): boolean ;
var
L: Integer;
Source: PChar;
begin
result := false ;
UniqueString (S) ; // 10 July 2002
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ < space) then
begin
Source^ := space ;
result := true ;
end ;
Inc (Source) ;
Dec (L) ;
end;
end;
function StringRemCntlsW (var S: UnicodeString): boolean ; // 13 Oct 2008
var
L: Integer;
Source: PWideChar;
begin
result := false ;
UniqueString (S) ;
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ < space) then
begin
Source^ := space ;
result := true ;
end ;
Inc (Source) ;
Dec (L) ;
end;
end;
// replace control codes (except CRLF) with spaces, true if string changed
function StringRemCntlsEx (var S: String): boolean ;
var
L: Integer;
Source: PChar;
begin
result := false ;
UniqueString (S) ; // 10 July 2002
L := Length (S) ;
Source := Pointer (S) ;
while L <> 0 do
begin
if (Source^ < space) then
begin
if (Source^ <> CR) and (Source^ <> LF) then
begin
Source^ := space ;
result := true ;
end ;
end ;
Inc (Source) ;
Dec (L) ;
end;
end;
{ }
{ Copy }
{ }
Function CopyRange (const S : String; const Start, Stop : Integer) : String;
Begin
Result := Copy (S, Start, Stop - Start + 1);
End;
Function CopyFrom (const S : String; const Start : Integer) : String;
Begin
Result := Copy (S, Start, Length (S) - Start + 1);
End;
Function CopyLeft (const S : String; const Count : Integer) : String;
Begin
Result := Copy (S, 1, Count);
End;
Function CopyRight (const S : String; const Count : Integer) : String;
Begin
Result := Copy (S, Length (S) - Count + 1, Count);
End;
{ }
{ Match }
{ }
{$IFNDEF CPUX64}
Function Match (const M : CharSet; const S : AnsiString; const Pos : Integer; const Count : Integer) : Boolean;
var I, PosEnd : Integer;
Begin
PosEnd := Pos + Count - 1;
if (M = []) or (Pos < 1) or (Count = 0) or (PosEnd > Length (S)) then
begin
Result := False;
exit;
end;
For I := Pos to PosEnd do
if not (S [I] in M) then
begin
Result := False;
exit;
end;
Result := True;
End;
Function Match (const M : CharSetArray; const S : AnsiString; const Pos : Integer) : Boolean;
var J, C : Integer;
Begin
C := Length (M);
if (C = 0) or (Pos < 1) or (Pos + C - 1 > Length (S)) then
begin
Result := False;
exit;
end;
For J := 0 to C - 1 do
if not (S [J + Pos] in M [J]) then
begin
Result := False;
exit;
end;
Result := True;
End;
{ Highly optimized version of Match. Equivalent to, but much faster and more }
{ memory efficient than: M = Copy (S, Pos, Length (M)) }
{ Does compare in 32-bit chunks (CPU's native type) }
Function Match (const M, S : AnsiString; const Pos : Integer) : Boolean;
Asm
push esi
push edi
push edx // save state
push Pos
push M
push S // push parameters
pop edi // edi = S [1]
pop esi // esi = M [1]
pop ecx // ecx = Pos
cmp ecx, 1
jb @NoMatch // if Pos < 1 then @NoMatch
mov edx, [esi - 4]
or edx, edx
jz @NoMatch // if Length (M) = 0 then @NoMatch
add edx, ecx
dec edx // edx = Pos + Length (M) - 1
cmp edx, [edi - 4]
ja @NoMatch // if Pos + Length (M) - 1 > Length (S) then @NoMatch
add edi, ecx
dec edi // edi = S [Pos]
mov ecx, [esi - 4] // ecx = Length (M)
// All the following code is an optimization of just two lines: //
// rep cmsb //
// je @Match //
mov dl, cl //
and dl, $03 //
shr ecx, 2 //
jz @CheckMod { Length (M) < 4 } //
//
{ The following is faster than: {} //
{ rep cmpsd {} //
{ jne @NoMatch {} //
@c1: {} //
mov eax, [esi] {} //
cmp eax, [edi] {} //
jne @NoMatch {} //
add esi, 4 {} //
add edi, 4 {} //
dec ecx {} //
jnz @c1 {} //
//
or dl, dl //
jz @Match //
//
{ Check remaining dl (0-3) bytes {} //
@CheckMod: {} //
mov eax, [esi] {} //
mov ecx, [edi] {} //
cmp al, cl {} //
jne @NoMatch {} //
dec dl {} //
jz @Match {} //
cmp ah, ch {} //
jne @NoMatch {} //
dec dl {} //
jz @Match {} //
and eax, $00ff0000 {} //
and ecx, $00ff0000 {} //
cmp eax, ecx {} //
je @Match {} //
@NoMatch:
xor al, al // Result := False
jmp @Fin
@Match:
mov al, 1 // Result := True
@Fin:
pop edx // restore state
pop edi
pop esi
End;
// borrowed from math.pas
function Max(A,B: Integer): Integer;
begin
if A > B then
Result := A
else
Result := B;
end;
{ }
{ PosNext }
{ }
Function PosNext (const Find : CharSet; const S : AnsiString; const LastPos : Integer) : Integer;
var I : Integer;
Begin
if Find = [] then
begin
Result := 0;
exit;
end;
For I := Max (LastPos + 1, 1) to Length (S) do
if S [I] in Find then
begin
Result := I;
exit;
end;
Result := 0;
End;
Function PosNext (const Find : CharSetArray; const S : AnsiString; const LastPos : Integer) : Integer;
var I, C : Integer;
Begin
C := Length (Find);
if C = 0 then
begin
Result := 0;
exit;
end;
For I := Max (LastPos + 1, 1) to Length (S) - C + 1 do
if Match (Find, S, I) then
begin
Result := I;
exit;
end;
Result := 0;
End;
Function PosNext (const Find : AnsiString; const S : AnsiString; const LastPos : Integer = 0) : Integer;
var I : Integer;
Begin
if Find = '' then
begin
Result := 0;
exit;
end;
For I := LastPos + 1 to Length (S) - Length (Find) + 1 do
if Match (Find, S, I) then
begin
Result := I;
exit;
end;
Result := 0;
End;
Function PosPrev (const Find : AnsiString; const S : AnsiString; const LastPos : Integer = 0) : Integer;
var I, J : Integer;
Begin
if Find = '' then
begin
Result := 0;
exit;
end;
if LastPos = 0 then
J := Length (S) - Length (Find) + 1 else
J := LastPos - 1;
For I := J downto 1 do
if Match (Find, S, I) then
begin
Result := I;
exit;
end;
Result := 0;
End;
{ }
{ PosN }
{ }
Function PosN (const Find, S : AnsiString; const N : Integer = 1;
const FromRight : Boolean = False) : Integer;
var F, I : Integer;
Begin
F := 0;
For I := 1 to N do
begin
if FromRight then
F := PosPrev (Find, S, F) else
F := PosNext (Find, S, F);
if F = 0 then
break;
end;
Result := F;
End;
{$ENDIF}
{ }
{ Split }
{ }
Function StrArraySplit (const S : String; const Delimiter : String = ' ') : StringArray;
var
I, J, L, K : Integer;
Begin
SetLength (Result, 0);
if (Delimiter = '') or (S = '') then exit;
I := 0;
L := 0;
Repeat
SetLength (Result, L + 1);
// J := PosNext (Delimiter, S, I);
J := PosEx (Delimiter, S, I + 1); // 15 Aug 2008 use StrUtils version for unicode compatibility
if L = 0 then // 15 Aug 2008 allow for multichar Delimiter missing start of first string
K := 1
else
K := I + Length (Delimiter) ;
if J = 0 then
Result [L] := CopyFrom (S, K)
else
begin
Result [L] := CopyRange (S, K, J - 1);
I := J;
Inc (L);
end;
Until J = 0;
End;
Function StrArraySplit (const S : WideString; const Delimiter : WideString = ' ') : WideStringArray;
var
I, J, L, K : Integer;
Begin
SetLength (Result, 0);
if (Delimiter = '') or (S = '') then exit;
I := 0;
L := 0;
Repeat
SetLength (Result, L + 1);
// J := PosNext (Delimiter, S, I);
J := PosEx (Delimiter, S, I + 1); // 15 Aug 2008 use StrUtils version for unicode compatibility
if L = 0 then // 15 Aug 2008 allow for multichar Delimiter missing start of first string
K := 1
else
K := I + Length (Delimiter) ;
if J = 0 then
Result [L] := CopyFrom (S, K)
else
begin
Result [L] := CopyRange (S, K, J - 1);
I := J;
Inc (L);
end;
Until J = 0;
End;
Function StrArrayJoin (const S : StringArray; const Delimiter : String = c_Space) : String;
var
I : Integer;
Begin
Result := '';
For I := 0 to High (S) do
begin
if I > 0 then
Result := Result + Delimiter;
Result := Result + S [I];
end;
End;
Function StrArrayJoin (const S : WideStringArray; const Delimiter : WideString = c_Space) : WideString;
var
I : Integer;
Begin
Result := '';
For I := 0 to High (S) do
begin
if I > 0 then
Result := Result + Delimiter;
Result := Result + S [I];
end;
End;
procedure StrArrayDelete (var S: StringArray; Index: integer; var Total: integer) ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
if (Total = 0) or (index >= Total) then exit ;
dec (Total) ;
if Total > 0 then
begin
for I := index to Pred (Total) do S [I] := S [Succ(I)] ;
end ;
SetLength (S, Total) ;
end ;
procedure StrArrayDelete (var S: WideStringArray; Index: integer; var Total: integer) ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
if (Total = 0) or (index >= Total) then exit ;
dec (Total) ;
if Total > 0 then
begin
for I := index to Pred (Total) do S [I] := S [Succ(I)] ;
end ;
SetLength (S, Total) ;
end ;
procedure StrArrayDelete (var S: StringArray; Index: integer) ;
var
Total: integer ;
begin
Total := MaxInt ;
StrArrayDelete (S, Index, Total) ;
end ;
procedure StrArrayDelete (var S: WideStringArray; Index: integer) ;
var
Total: integer ;
begin
Total := MaxInt ;
StrArrayDelete (S, Index, Total) ;
end ;
procedure StrArrayInsert (var S: StringArray; Index: integer; T: string;
var Total: integer) ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
if Length (S) <= Total then SetLength (S, Succ (Total)) ;
if index > Total then index := Total ; // add at end if index too large
if (index < Total) and (Total <> 0) then
begin
for I := Total downto Succ (index) do S [I] := S [Pred(I)] ;
end ;
S [index] := T ;
inc (Total) ;
end ;
procedure StrArrayInsert (var S: WideStringArray; Index: integer; T: Widestring;
var Total: integer) ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
if Length (S) <= Total then SetLength (S, Succ (Total)) ;
if index > Total then index := Total ; // add at end if index too large
if (index < Total) and (Total <> 0) then
begin
for I := Total downto Succ (index) do S [I] := S [Pred(I)] ;
end ;
S [index] := T ;
inc (Total) ;
end ;
procedure StrArrayInsert (var S: StringArray; Index: integer; T: string) ;
var
Total: integer ;
begin
Total := MaxInt ;
StrArrayInsert (S, Index, T, Total) ;
if Length (S) > Total then SetLength (S, Total) ;
end ;
procedure StrArrayInsert (var S: WideStringArray; Index: integer; T: Widestring) ;
var
Total: integer ;
begin
Total := MaxInt ;
StrArrayInsert (S, Index, T, Total) ;
if Length (S) > Total then SetLength (S, Total) ;
end ;
// find string in sorted array, returns position to insert if not found
function StrArrayFindSorted (const S: StringArray; T: string; var Index: longint;
Total: integer): Boolean;
var
I, res: integer ;
begin
result := false ;
Index := 0 ;
if Total > Length (S) then Total := Length (S) ;
if Total = 0 then exit ;
// pending - use binary chop sort for speed
for I := 0 to pred (Total) do
begin
res := CompareStr (T, S [I]) ;
if res = 0 then
begin
result := true ; // found OK
break ;
end ;
if res < 0 then break ; // passed it
end ;
Index := I ;
end ;
function StrArrayFindSorted (const S: WideStringArray; T: Widestring; var Index: longint;
Total: integer): Boolean;
var
I, res: integer ;
begin
result := false ;
Index := 0 ;
if Total > Length (S) then Total := Length (S) ;
if Total = 0 then exit ;
// pending - use binary chop sort for speed
for I := 0 to pred (Total) do
begin
res := CompareStr (T, S [I]) ;
if res = 0 then
begin
result := true ; // found OK
break ;
end ;
if res < 0 then break ; // passed it
end ;
Index := I ;
end ;
// insert into array sorted correctly, skipping duplicates
function StrArrayAddSorted (var S: StringArray; T: string; var Total: integer): boolean ;
var
Index: integer ;
begin
result := StrArrayFindSorted (S, T, Index, Total) ;
if result then exit ;
StrArrayInsert (S, Index, T, Total) ;
end ;
function StrArrayAddSorted (var S: WideStringArray; T: Widestring; var Total: integer): boolean ;
var
Index: integer ;
begin
result := StrArrayFindSorted (S, T, Index, Total) ;
if result then exit ;
StrArrayInsert (S, Index, T, Total) ;
end ;
function StrArrayAddSorted (var S: StringArray; T: string): boolean ;
var
Total: integer ;
begin
Total := MaxInt ;
result := StrArrayAddSorted (S, T, Total) ;
if Length (S) > Total then SetLength (S, Total) ;
end ;
function StrArrayAddSorted (var S: WideStringArray; T: Widestring): boolean ;
var
Total: integer ;
begin
Total := MaxInt ;
result := StrArrayAddSorted (S, T, Total) ;
if Length (S) > Total then SetLength (S, Total) ;
end ;
procedure StrArrayFromList (T: TStringList; var S: StringArray) ;
var
I, tot: integer ;
begin
tot := T.Count ;
SetLength (S, tot) ;
if tot = 0 then exit ;
for I := 0 to Pred (tot) do S [I] := T [I] ;
end ;
// pending wide versions need wide string list
procedure StrArrayToList (S: StringArray; var T: TStringList) ;
var
I, tot: integer ;
begin
tot := Length (S) ;
T.Clear ;
if tot = 0 then exit ;
for I := 0 to pred (tot) do T.Add (S [I]) ;
end ;
function StrArrayPosOf (const L: string; S: StringArray): integer ;
begin
result := StrArrayPosOfEx (L, S, MaxInt) ;
end ;
function StrArrayPosOf (const L: Widestring; S: WideStringArray): integer ;
begin
result := StrArrayPosOfEx (L, S, MaxInt) ;
end ;
// pos in part of an array - where the array has unused elements
function StrArrayPosOfEx (const L: string; S: StringArray; Total: integer = MaxInt): integer ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
result := -1 ;
if Total = 0 then exit ;
for I := 0 to pred (Total) do
begin
if L = S [I] then
begin
result := I ;
exit ;
end ;
end ;
end ;
function StrArrayPosOfEx (const L: Widestring; S: WideStringArray; Total: integer = MaxInt): integer ;
var
I: integer ;
begin
if Total > Length (S) then Total := Length (S) ;
result := -1 ;
if Total = 0 then exit ;
for I := 0 to pred (Total) do
begin
if L = S [I] then
begin
result := I ;
exit ;
end ;
end ;
end ;
// warning, must FreeMem (Buffer) after use
procedure StrArrayToMultiSZ (S: StringArray; var Buffer: PAnsiChar) ;
var
I, tot, size: integer ;
P: PAnsiChar ;
begin
tot := Length (S) ;
size := 2 ;
if tot > 0 then // find length of all strings
begin
for I := 0 to Pred (tot) do inc (size, Length (S [I]) + 1) ;
end ;
GetMem (Buffer, size);
P := Buffer;
if tot > 0 then // build array of null-separated names
begin
for I := 0 to Pred (tot) do
begin
LstrcpyA (P, PAnsiChar (AnsiString (S [I]))) ; // 7 Aug 2010
inc (P, LstrlenA (P) + 1) ;
end ;
end;
P^ := #0; // add double null termination
inc (P) ;
P^ := #0;
end ;
procedure StrArrayToMultiSZ (S: StringArray; var Buffer: PWideChar) ; // 14 Aug 2008 wide overload
var
I, tot, size: integer ;
P: PWideChar ;
W: UnicodeString ;
begin
tot := Length (S) ;
size := 2 ;
if tot > 0 then // find length of all strings
begin
for I := 0 to Pred (tot) do inc (size, Length (S [I]) + 1) ;
end ;
GetMem (Buffer, size * 2);
P := Buffer;
if tot > 0 then // build array of null-separated names
begin
for I := 0 to Pred (tot) do
begin
W := S [I] ;
LstrcpyW (P, PWideChar (W));
inc (P, LstrlenW (P) + 1) ;
end ;
end;
P^ := #0; // add double null termination
inc (P) ;
P^ := #0;
end ;
procedure StrArrayToMultiSZ (S: WideStringArray; var Buffer: PWideChar) ; // 10 Sept 2008 wide overload
var
I, tot, size: integer ;
P: PWideChar ;
W: WideString ;
begin
tot := Length (S) ;
size := 2 ;
if tot > 0 then // find length of all strings
begin
for I := 0 to Pred (tot) do inc (size, Length (S [I]) + 1) ;
end ;
GetMem (Buffer, size * 2);
P := Buffer;
if tot > 0 then // build array of null-separated names
begin
for I := 0 to Pred (tot) do
begin
W := S [I] ;
LstrcpyW (P, PWideChar (W));
inc (P, LstrlenW (P) + 1) ;
end ;
end;
P^ := #0; // add double null termination
inc (P) ;
P^ := #0;
end ;
procedure StrArrayFromMultiSZ (Buffer: PAnsiChar ; Len: integer ; var S: StringArray) ;
var
I, J, tot: integer ;
P: PAnsiChar ;
begin
tot := 0 ;
if Len > 0 then
begin
P := Buffer;
for I := 1 to Len do
begin
if P^ = #0 then inc (tot) ; // count strings
inc (P) ;
end ;
end ;
SetLength (S, tot) ; // might include end nulls
if tot = 0 then exit ;
P := Buffer ;
tot := 0 ;
I := 1 ;
while (I < Len) do // 28 Aug 2008 allow for empty strings, except last null
begin
S [tot] := Char (P) ; // 7 Aug 2010
inc (tot) ;
J := Windows.LStrLenA (P) + 1 ;
inc (I, J) ;
inc (P, J) ;
end ;
SetLength (S, tot) ;
end ;
procedure StrArrayFromMultiSZ (Buffer: PWideChar ; Len: integer ; var S: StringArray) ; // 14 Aug 2008 wide overload
var
I, J, tot: integer ;
P: PWideChar ;
begin
tot := 0 ;
if Len > 0 then // length is bytes in buffer, not characters
begin
P := Buffer;
for I := 1 to (Len div 2) do
begin
if P^ = #0 then inc (tot) ; // count strings
inc (P) ;
end ;
end ;
SetLength (S, tot) ; // might include end nulls
if tot = 0 then exit ;
P := Buffer ;
tot := 0 ;
I := 1 ;
while (I < (Len div 2)) do
begin
S [tot] := P ;
inc (tot) ;
J := Windows.LStrLenW (P) + 1 ;
inc (I, J) ;
inc (P, J) ;
end ;
SetLength (S, tot) ;
end ;
procedure StrArrayFromMultiSZ (Buffer: PWideChar ; Len: integer ; var S: WideStringArray) ; // 10 Sept 2008 wide overload
var
I, J, tot: integer ;
P: PWideChar ;
begin
tot := 0 ;
if Len > 0 then // length is bytes in buffer, not characters
begin
P := Buffer;
for I := 1 to (Len div 2) do
begin
if P^ = #0 then inc (tot) ; // count strings
inc (P) ;
end ;
end ;
SetLength (S, tot) ; // might include end nulls
if tot = 0 then exit ;
P := Buffer ;
tot := 0 ;
I := 1 ;
while (I < (Len div 2)) do
begin
S [tot] := P ;
inc (tot) ;
J := Windows.LStrLenW (P) + 1 ;
inc (I, J) ;
inc (P, J) ;
end ;
SetLength (S, tot) ;
end ;
// check if file open
function CheckFileOpen(const FName: String): integer;
var
H: Integer;
begin
result := -1; // file not found
if NOT FileExists (FName) then exit ;
H := FileOpen(FName, fmOpenReadWrite);
result := 1; // file open
if H < 0 then exit;
FileClose(H);
result := 0; // file found but closed
end;
// truncate file
function TruncateFile(const FName: UnicodeString; NewSize: int64): int64;
var
H: Integer;
begin
result := -1; // file not found
if GetSizeFileW (FName) < 0 then exit; // unicode
// H := FileOpen(FName, fmOpenReadWrite);
H := Integer(CreateFileW (PWideChar (FName), GENERIC_READ or GENERIC_WRITE, 0,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) ;
if H < 0 then exit;
result := FileSeek (H, Int64 (0), soFromEnd) ; // size of file
if NewSize < result then
begin
result := FileSeek (H, NewSize, soFromBeginning) ; // seek from start
if result >= 0 then SetEndOfFile (H) ; // change file size
end ;
FileClose(H);
end;
// Set file time stamp, local time - 18 Feb 2009
function UpdateFileAge(const FName: String; const NewDT: TDateTime): boolean;
var
H: Integer;
begin
Result := FALSE;
H := FileOpen (FName, fmOpenWrite);
if H < 0 then Exit;
FileSetDate (H, DateTimeToFileDate (NewDT));
FileClose (H);
Result := TRUE;
end;
// Set file time stamp, UTC time - 18 Feb 2009
function UpdateUFileAge(const FName: String; const NewDT: TDateTime): boolean;
var
H: Integer ;
FileTime: TFileTime ;
begin
Result := FALSE;
H := FileOpen (FName, fmOpenWrite);
if H < 0 then Exit;
FileTime := DateTimeToFileTime (NewDT);
if SetFileTime (H, nil, nil, @FileTime) then Result := TRUE;
FileClose (H);
end;
// various time and date manipulation functions
// TDateTime is a double floating point, days since 30th December 1899, fractional part of day
// TFileTime is 64-bits as two longwords, being a count in 100ns increments since 1st January 1601
// (we cast TFileTime to Int64 for ease of manipulation, but this may fail in 64-bit Windows)
// Unix time is a long word being seconds since 1st January 1970 - it wraps in year 2036
// UTC time is unaffected by timezones and summer time changes - Windows uses UTC internally
// and NTFS formatted disks keep file time stamps as UTC
// Local time is adjusted from UTC by time zone and summer time
// internal convert TFileTime to Int64
function FileTimeToInt64 (const FileTime: TFileTime): Int64 ;
begin
Move (FileTime, result, SizeOf (result)) ;
end;
// internal convert Int64 to TFileTime
function Int64ToFileTime (const FileTime: Int64): TFileTime ;
begin
Move (FileTime, result, SizeOf (result)) ;
end;
// convert TFileTime to TDateTime
function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
begin
Result := FileTimeToInt64 (FileTime) / FileTimeStep ;
Result := Result + FileTimeBase ;
end;
// convert TDateTime to TFileTime
function DateTimeToFileTime(DateTime: TDateTime): TFileTime;
var
E: Extended;
begin
E := (DateTime - FileTimeBase) * FileTimeStep;
result := Int64ToFileTime (Round(E)) ;
end;
// convert Filetime to seconds since year 2000 - very non-standard!
function FileTimeToSecs2K (const FileTime: TFileTime): integer ;
begin
result := (FileTimeToInt64 (FileTime) - FileTime2000) div FileTimeSecond ;
end ;
// get current Unix time (in UTC) - 18 Feb 2009
function GetUnixTime: Int64;
begin
result := DateTimeToUnix (GetUTCTime) ;
end ;
// get local time bias from UTC and negative or positive minutes - 18 Feb 2009
function GetLocalBiasUTC: integer;
var
ZoneInfo: TTimeZoneInformation;
begin
case GetTimeZoneInformation (ZoneInfo) of
TIME_ZONE_ID_STANDARD: Result := ZoneInfo.Bias + ZoneInfo.StandardBias ;
TIME_ZONE_ID_DAYLIGHT: Result := ZoneInfo.Bias + ZoneInfo.DaylightBias ;
else
Result := ZoneInfo.Bias ;
end;
end;
// convert local time to UTC time - 18 Feb 2009
function DateTimeToUTC(dtDT : TDateTime) : TDateTime;
begin
Result := dtDT + GetLocalBiasUTC / (60.0 * 24.0);
end;
// convert UTC time to local time - 18 Feb 2009
function UTCToLocalDT(dtDT : TDateTime) : TDateTime;
begin
Result := dtDT - GetLocalBiasUTC / (60.0 * 24.0);
end;
// get system date and time as UTC/GMT into Delphi time
function GetUTCTime: TDateTime;
var
SystemTime: TSystemTime;
begin
GetSystemTime(SystemTime);
with SystemTime do
begin
Result := EncodeTime (wHour, wMinute, wSecond, wMilliSeconds) +
EncodeDate (wYear, wMonth, wDay);
end ;
end;
// set system date and time as UTC/GMT - needs administrator privilige - 18 Feb 2009
function SetUTCTime (DateTime: TDateTime): boolean ;
var
SystemTime: TSystemTime;
begin
with SystemTime do DecodeDateTime (DateTime, wYear, wMonth,
wDay, wHour, wMinute, wSecond, wMilliSeconds) ;
result := SetSystemTime (SystemTime) ;
end ;
// get file written UTC TFileTime and size in bytes - no change for summer time
function GetFUAgeSizeFile (filename: string ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
var
SResult: integer ;
SearchRec: TSearchRec ;
TempSize: ULARGE_INTEGER ; // 64-bit integer record - Mar 2017 was TULargeInteger
begin
Result := false ;
SResult := SysUtils.FindFirst(filename, faAnyFile, SearchRec);
if SResult = 0 then
begin
TempSize.LowPart := SearchRec.FindData.nFileSizeLow ; // 4 Sept 2005
TempSize.HighPart := SearchRec.FindData.nFileSizeHigh ;
FSize := TempSize.QuadPart ;
FileTime := SearchRec.FindData.ftLastWriteTime ;
result := true ;
end ;
SysUtils.FindClose(SearchRec);
end ;
function GetFUAgeSizeFileW (filename: UnicodeString ; var FileTime: TFileTime ; // 8 Sept 2008
var FSize: Int64): boolean ;
var
FindHandle: THandle;
FindData: TWin32FindDataW;
TempSize: ULARGE_INTEGER ; // 64-bit integer record - Mar 2017 was TULargeInteger
ExcludeAttr: integer ;
const
faSpecial = faHidden or faSysFile or faDirectory;
begin
Result := false ;
ExcludeAttr := not faAnyFile and faSpecial;
FindHandle := Windows.FindFirstFileW (PWideChar(filename), FindData) ;
if (FindHandle <> INVALID_HANDLE_VALUE) then
begin
while FindData.dwFileAttributes and ExcludeAttr <> 0 do
if not Windows.FindNextFileW(FindHandle, FindData) then exit ;
TempSize.LowPart := FindData.nFileSizeLow ; // 4 Sept 2005
TempSize.HighPart := FindData.nFileSizeHigh ;
FSize := TempSize.QuadPart ;
FileTime := FindData.ftLastWriteTime ;
result := true ;
Windows.FindClose(FindHandle);
end ;
end ;
// get file written local TFileTime and size in bytes - changes for summer time
function GetFAgeSizeFile (filename: string ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
var
UTCFileTime: TFileTime ;
begin
Result := GetFUAgeSizeFile (filename, UTCFileTime, FSize);
if Result then FileTimeToLocalFileTime (UTCFileTime, FileTime) ;
end ;
function GetFAgeSizeFileW (filename: UnicodeString ; var FileTime: TFileTime ;
var FSize: Int64): boolean ;
var
UTCFileTime: TFileTime ;
begin
Result := GetFUAgeSizeFileW (filename, UTCFileTime, FSize);
if Result then FileTimeToLocalFileTime (UTCFileTime, FileTime) ;
end ;
// get file written UTC TDateTime and size in bytes - no change for summer time
function GetUAgeSizeFile (filename: string ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
var
UTCFileTime: TFileTime ;
begin
Result := GetFUAgeSizeFile (filename, UTCFileTime, FSize);
if Result then FileDT := FileTimeToDateTime (UTCFileTime);
end ;
function GetUAgeSizeFileW (filename: UnicodeString ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
var
UTCFileTime: TFileTime ;
begin
Result := GetFUAgeSizeFileW (filename, UTCFileTime, FSize);
if Result then FileDT := FileTimeToDateTime (UTCFileTime);
end ;
// get file written local TDateTime and size in bytes - changes for summer time
function GetAgeSizeFile (filename: string ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
var
LocalFileTime: TFileTime ;
begin
Result := GetFAgeSizeFile (filename, LocalFileTime, FSize);
if Result then FileDT := FileTimeToDateTime (LocalFileTime);
end ;
function GetAgeSizeFileW (filename: UnicodeString ; var FileDT: TDateTime;
var FSize: Int64): boolean ;
var
LocalFileTime: TFileTime ;
begin
Result := GetFAgeSizeFileW (filename, LocalFileTime, FSize);
if Result then FileDT := FileTimeToDateTime (LocalFileTime);
end ;
// get file size in bytes
function GetSizeFile (filename: string): LongInt;
var
FileDT: TDateTime;
FSize: Int64;
begin
Result := -1 ;
if GetAgeSizeFile (filename, FileDT, FSize) then result := FSize ;
end ;
function GetSizeFileW (filename: UnicodeString): LongInt;
var
FileDT: TDateTime;
FSize: Int64;
begin
Result := -1 ;
if GetAgeSizeFileW (filename, FileDT, FSize) then result := FSize ;
end ;
// get file size in bytes
function GetSize64File (filename: string): Int64 ;
var
FileDT: TDateTime;
FSize: Int64;
begin
Result := -1 ;
if GetAgeSizeFile (filename, FileDT, FSize) then result := FSize ;
end ;
function GetSize64FileW (filename: UnicodeString): Int64 ;
var
FileDT: TDateTime;
FSize: Int64;
begin
Result := -1 ;
if GetAgeSizeFileW (filename, FileDT, FSize) then result := FSize ;
end ;
// remove trailing spaces from string
function TrimSpRight(const S: string): string;
var
I: Integer;
begin
I := Length(S);
while (I > 0) and (S[I] = ' ') do Dec(I);
Result := Copy(S, 1, I);
end;
// extract file name less extension, drive and path
function ExtractNameOnly(FileName: string): string;
var
I: Integer;
begin
FileName := ExtractFileName (FileName) ; // remove path
I := Length(FileName);
while (I > 0) and not (IsPathSep (FileName[I])) do Dec(I); // Unicode
if (I = 0) or (FileName[I] <> '.') then I := MaxInt;
Result := Copy(FileName, 1, I - 1) ;
end;
// get exception literal message
function GetExceptMess (ExceptObject: TObject): string;
var
MsgPtr: PChar;
MsgEnd: PChar;
MsgLen: Integer;
MessEnd: String ;
begin
MsgPtr := '';
MsgEnd := '';
if ExceptObject is Exception then
begin
MsgPtr := PChar(Exception(ExceptObject).Message);
MsgLen := StrLen(MsgPtr);
if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
end;
result := Trim (MsgPtr) ;
MessEnd := Trim (MsgEnd) ;
if Length (MessEnd) > 5 then result := result + ' - ' + MessEnd ;
end;
// string to numeric conversions
// also convert Hexadecimal numbers with leading $, ie $00001234
function AscToInt (value: string): Integer; // simple version of StrToInt
var
E: Integer;
begin
Val (value, result, E) ;
end;
function AscToInt64 (value: string): Int64 ; // simple version of StrToInt
var
E: Integer;
begin
Val (value, result, E) ;
end;
function AscToIntAnsi (value: AnsiString): Integer; // simple version of StrToInt
var
E: Integer;
begin
Val (string (value), result, E) ; // 7 Aug 2010
end;
function AscToInt64Ansi (value: AnsiString): Int64 ; // simple version of StrToInt
var
E: Integer;
begin
Val (string (value), result, E) ; // 7 Aug 2010
end;
function Str2LInt (const S: String): LongInt;
begin
result := AscToInt (Trim (S)) ; // remove leading and trailing spaces
end;
function Str2Byte (const S: String): Byte;
var
L: LongInt;
begin
L := Str2LInt (S);
if L > MaxByte then
Result := MaxByte
else if L < MinByte then
Result := MinByte
else
Result := L;
end;
function Str2SInt (const S: String): ShortInt;
var
L: LongInt;
begin
L := Str2LInt (S);
if L > MaxShortInt then
Result := MaxShortInt
else if L < MinShortInt then
Result := MinShortInt
else
Result := L;
end;
function Str2Int (const S: String): Integer;
begin
result := Str2LInt (S);
end;
function Str2Word (const S: String): Word;
var
L: LongInt;
begin
L := Str2LInt (S);
if L > MaxWord then
Result := MaxWord
else if L < MinWord then
Result := MinWord
else
Result := L;
end;
// improved integer to string conversions
function AddThouSeps (const S: string): string;
var
LS, L2, I, N: Integer;
Temp : string;
begin
result := S ;
LS := Length (S);
N := 1 ;
if LS > 1 then
begin
if S [1] = '-' then // check for negative value
begin
N := 2 ;
LS := LS - 1 ;
end ;
end ;
if LS <= 3 then exit ;
L2 := (LS - 1) div 3;
Temp := '';
for I := 1 to L2 do
Temp := MyFormatSettings.ThousandSeparator + Copy (S, LS - 3 * I + 1, 3) + Temp;
Result := Copy (S, N, (LS - 1) mod 3 + 1) + Temp;
if N > 1 then Result := '-' + Result ;
end;
function IntToCStr (const N: integer): string ;
begin
result := AddThouSeps (IntToStr (N)) ;
end ;
function Int64ToCStr (const N: int64): string ;
begin
result := AddThouSeps (IntToStr (N)) ;
end ;
function AddThouSepsAnsi (const S: AnsiString): AnsiString;
var
LS, L2, I, N: Integer;
Temp : AnsiString;
begin
result := S ;
LS := Length (S);
N := 1 ;
if LS > 1 then
begin
if S [1] = '-' then // check for negative value
begin
N := 2 ;
LS := LS - 1 ;
end ;
end ;
if LS <= 3 then exit ;
L2 := (LS - 1) div 3;
Temp := '';
for I := 1 to L2 do
Temp := AnsiString (MyFormatSettings.ThousandSeparator) + Copy (S, LS - 3 * I + 1, 3) + Temp; // 7 Aug 2010
Result := Copy (S, N, (LS - 1) mod 3 + 1) + Temp;
if N > 1 then Result := '-' + Result ;
end;
function IntToCStrAnsi (const N: integer): AnsiString ;
begin
result := AddThouSepsAnsi (IntToStrAnsi (N)) ;
end ;
function Int64ToCStrAnsi (const N: int64): AnsiString ;
begin
result := AddThouSepsAnsi (IntToStrAnsi (N)) ;
end ;
function LInt2Str (const L: LongInt; const Len: Byte): String;
begin
try
Result := IntToStr (L);
except
Result := '';
end;
Result := PadChLeftStr (CopyLeft (Result, Len), NumPadCh, Len);
end;
function LInt2EStr (const L: LongInt): String;
begin
try
Result := IntToStr (L);
except
Result := '';
end;
end;
function LInt2ZBEStr (const L: LongInt): String;
begin
if L = 0 then
Result := ''
else
try
Result := IntToStr (L);
except
Result := '';
end;
end;
function FillStr (const Ch : Char; const N : Integer): string;
var
I: integer ;
begin
SetLength (Result, N);
// FillChar (Result [1], N * SizeOf (Char), Ch) ; // Unicode
for I := 1 to N do Result [I] := Char (Ch) ;
end;
function BlankStr (const N : Integer): string;
begin
Result := FillStr (' ', N);
end;
function DashStr (const N : Integer): string;
begin
Result := FillStr ('-', N);
end;
function DDashStr (const N : Integer): string;
begin
Result := FillStr ('=', N);
end;
function LineStr (const N : Integer): string;
begin
Result := FillStr (#196, N);
end;
function DLineStr (const N : Integer): string;
begin
Result := FillStr (#205, N);
end;
function StarStr (const N : Integer): string;
begin
Result := FillStr ('*', N);
end;
function HashStr (const N : Integer): string;
begin
Result := FillStr ('#', N);
end;
function PadRightStr (const S : string; const Len : Integer): string;
var
N: Integer;
begin
N := Length (S);
if N < Len then
Result := S + BlankStr (Len - N)
else
Result := S;
end;
function PadLeftStr (const S : string; const Len : Integer): string;
var
N: Integer;
begin
N := Length (S);
if N < Len then
Result := BlankStr (Len - N) + S
else
Result := S;
end;
function PadChLeftStr (const S : string; const Ch : Char; const Len : Integer): string;
var
N: Integer;
begin
N := Length (S);
if N < Len then
Result := FillStr (Ch, Len - N) + S
else
Result := S;
end;
// angus, leading zeros
function Int2StrZ (const L: LongInt; const Len: Byte): String;
begin
try
Result := IntToStr (L);
except
Result := '';
end;
Result := PadChLeftStr (CopyLeft (Result, Len), '0', Len);
end;
function Byte2Str (const L: LongInt; const Len: Byte): String;
begin
try
Result := IntToStr (L);
except
Result := '';
end;
Result := PadChLeftStr (CopyLeft (Result, Len), NumPadCh, Len);
end;
function LInt2ZBStr (const L: LongInt; const Len: Byte): String;
begin
Result := LInt2ZBEStr (L);
Result := PadChLeftStr (CopyLeft (Result, Len), NumPadCh, Len);
end;
function LInt2ZStr (const L: LongInt; const Len: Byte): String;
begin
Result := LInt2EStr (L);
Result := PadChLeftStr (CopyLeft (Result, Len), '0', Len);
end;
function LInt2CStr (const L : LongInt; const Len : Byte): string;
begin
Result := LInt2CEStr (L);
Result := PadChLeftStr (CopyLeft (Result, Len), NumPadCh, Len);
end;
function LInt2CEStr (const L : LongInt): string;
begin
try
Result := AddThouSeps (IntToStr (L)) ;
except
Result := '';
end;
end;
function Int642CEStr (const L : Int64): string;
begin
try
Result := AddThouSeps (IntToStr (L)) ;
except
Result := '';
end;
end;
function Str2DateTime (const S: String): TDateTime; // WARNING = format is system dependent
begin
Result := 0 ;
if length (S) < 8 then exit ;
if S [1] = space then exit ;
{$IFDEF VER130} // D5
try
Result := StrToDateTime (S)
except
Result := 0 ;
end;
{$ELSE}
{$IFDEF VER120} // D4
try
Result := StrToDateTime (S)
except
Result := 0 ;
end;
{$ELSE}
Result := StrToDateTimeDef (S, 0) ; // D6 and later
{$ENDIF}
{$ENDIF}
end;
function Str2Time (const S: String): TDateTime;
begin
Result := 0 ;
if length (S) < 3 then exit ;
if S [1] = space then exit ;
{$IFDEF VER130} // D5
try
Result := StrToTime (S)
except
Result := 0 ;
end;
{$ELSE}
{$IFDEF VER120} // D4
try
Result := StrToTime (S)
except
Result := 0 ;
end;
{$ELSE}
Result := StrToTimeDef (S, 0) ; // D6 only
{$ENDIF}
{$ENDIF}
end;
// yyyymmdd-hhnnss
function Date2Packed (infoDT: TDateTime): string ;
begin
result := '' ;
if infoDT < 1 then exit ; // ensure there's a date and not just time
result := FormatDateTime (DateMaskPacked, infoDT)
end ;
// yyyymmdd-hhnnsszzz
function Date2XPacked (infoDT: TDateTime): string ;
begin
result := '' ;
if infoDT < 1 then exit ; // ensure there's a date and not just time
result := FormatDateTime (DateMaskXPacked, infoDT)
end ;
function Packed2Time (info: string): TDateTime ;
// hhnnss-zzz
// 1234567890
var
hh, nn, ss, zz: word ;
begin
result := -1 ;
info := trim (info) ;
if length (info) < 6 then exit ;
zz := 0 ;
hh := Str2Word (copy (info, 1, 2)) ;
nn := Str2Word (copy (info, 3, 2)) ;
ss := Str2Word (copy (info, 5, 2)) ;
if length (info) = 10 then zz := Str2Word (copy (info, 8, 3)) ;
if NOT TryEncodeTime (hh, nn, ss, zz, result) then exit ; // D6 only
end ;
function Packed2Date (info: string): TDateTime ;
// yyyymmdd-hhnnss-zzz (DateMaskXPacked) = 19
// yyyymmdd-hhnnss (DateMaskPacked) = 15
// or just yyyymmdd = 8
// 123456789012345
var
yy, mm, dd: word ;
timeDT: TDateTime ;
begin
result := 0 ;
info := trim (info) ;
if length (info) < 8 then exit ;
yy := Str2Word (copy (info, 1, 4)) ;
mm := Str2Word (copy (info, 5, 2)) ;
dd := Str2Word (copy (info, 7, 2)) ;
if NOT TryEncodeDate (yy, mm, dd, result) then // D6 only
begin
result := -1 ;
exit ;
end ;
if length (info) < 15 then exit ;
if info [9] <> '-' then exit ;
timeDT := Packed2Time (copy (info, 10, 10)) ;
if timeDT < 0 then exit ;
result := result + timeDT ;
end ;
function PackedISO2Date (info: string): TDateTime ;
// yyyy-mm-ddThh:nn:ss (ISODateTimeMask), might be NULL
// or just yyyy-mm-dd
// or just hh:nn:ss
// 1234567890123456789
var
yy, mm, dd: word ;
hh, nn, ss: word ;
timeDT: TDateTime ;
begin
result := 0 ;
info := trim (info) ;
if length (info) = 8 then // 17 Apr 2013 check time only
begin
if info [3] <> ':' then exit ;
if info [6] <> ':' then exit ;
hh := Str2Word (copy (info, 1, 2)) ;
nn := Str2Word (copy (info, 4, 2)) ;
ss := Str2Word (copy (info, 7, 2)) ;
if NOT TryEncodeTime (hh, nn, ss, 0, result) then exit ; // D6 only
exit ;
end;
if length (info) < 10 then exit ;
if info [5] <> '-' then exit ;
if info [8] <> '-' then exit ;
yy := Str2Word (copy (info, 1, 4)) ;
mm := Str2Word (copy (info, 6, 2)) ;
dd := Str2Word (copy (info, 9, 2)) ;
if NOT TryEncodeDate (yy, mm, dd, result) then // D6 only
begin
result := -1 ;
exit ;
end ;
if length (info) <> 19 then exit ;
if info [14] <> ':' then exit ;
if info [17] <> ':' then exit ;
hh := Str2Word (copy (info, 12, 2)) ;
nn := Str2Word (copy (info, 15, 2)) ;
ss := Str2Word (copy (info, 18, 2)) ;
if NOT TryEncodeTime (hh, nn, ss, 0, timeDT) then exit ; // D6 only
result := result + timeDT ;
end ;
function PackedISO2UKStr (info: string): string ;
// yyyy-mm-ddThh:nn:ss (ISODateTimeMask), might be NULL, to dd/mm/yyyy hh:mm:ss
// or just yyyy-mm-dd
// 1234567890123456789
// null returns blank, zero seconds left blank
begin
result := '' ;
info := trim (info) ;
if length (info) < 10 then exit ;
if info [5] <> '-' then exit ;
result := copy (info, 9, 2) + '/' + copy (info, 6, 2) + '/' + copy (info, 1, 4) ;
if length (info) <> 19 then exit ;
if info [14] <> ':' then exit ;
result := result + ' ' + copy (info, 12, 2) + ':' + copy (info, 15, 2) ;
if copy (info, 18, 2) <> '00' then
result := result + ':' + copy (info, 18, 2) ;
end ;
function Packed2Secs (info: string): integer ;
// hh:nn:ss - but with leading characters blank, 12:40 3:50 - timer!!
// 12345678
var
len: integer ;
begin
result := 0 ;
info := trim (info) ;
len := length (info) ;
if len < 4 then exit ;
while length (info) < 8 do info := '0' + info ; // add leading zeros
if info [6] <> MyFormatSettings.TimeSeparator then exit ;
result := AscToInt (copy (info, 1, 2)) * 60 ;
result := (result + AscToInt (copy (info, 4, 2))) * 60 ;
result := result + AscToInt (copy (info, 7, 2)) ;
end ;
function ConvLongDate (info: string): TDateTime ;
// yyyy/mm/dd
var
yy, mm, dd: word ;
begin
result := 0 ;
info := trim (info) ;
if length (info) <> 10 then exit ;
yy := Str2Word (copy (info, 1, 4)) ;
mm := Str2Word (copy (info, 6, 2)) ;
dd := Str2Word (copy (info, 9, 2)) ;
if NOT TryEncodeDate (yy, mm, dd, result) then // D6 only
begin
result := -1 ;
exit ;
end ;
end ;
function ConvUSADate (info: string): TDateTime ;
// mm/dd/yyyy
var
yy, mm, dd: word ;
begin
result := 0 ;
info := trim (info) ;
if length (info) <> 10 then exit ;
yy := Str2Word (copy (info, 7, 4)) ;
mm := Str2Word (copy (info, 1, 2)) ;
dd := Str2Word (copy (info, 4, 2)) ;
if NOT TryEncodeDate (yy, mm, dd, result) then result := 0 ; // D6 only
end ;
function ConvUKDate (info: string): TDateTime ;
// dd/mm/yyyy hh:mm:ss or dd/mm/yyyy or dd/mm/yyyy hh:mm
// 1234567890123456789
var
yy, mm, dd: word ;
hh, nn, ss: word ;
timeDT: TDateTime ;
begin
result := 0 ;
info := trim (info) ;
if length (info) < 10 then exit ;
if info [3] <> '/' then exit ;
yy := Str2Word (copy (info, 7, 4)) ;
mm := Str2Word (copy (info, 4, 2)) ;
dd := Str2Word (copy (info, 1, 2)) ;
if NOT TryEncodeDate (yy, mm, dd, result) then
begin
result := 0 ; // D6 only
exit ;
end ;
if length (info) < 16 then exit ;
if info [14] <> ':' then exit ;
hh := Str2Word (copy (info, 12, 2)) ;
nn := Str2Word (copy (info, 15, 2)) ;
ss := 0 ;
if length (info) >= 19 then ss := Str2Word (copy (info, 18, 2)) ;
if NOT TryEncodeTime (hh, nn, ss, 0, timeDT) then exit ; // D6 only
result := result + timeDT ;
end ;
// yyyymmdd and hhnnss to 'yyyy-mm-ddThh:nn:ss'
function AlphaDTtoISODT (sdate, stime: string): string ;
begin
result := SQUOTE + CopyLeft (sdate, 4) + '-' + Copy (sdate, 5, 2) +
'-' + Copy (sdate, 7, 2) + 'T' + CopyLeft (stime, 2) + ':' +
Copy (stime, 3, 2) + ':' + Copy (stime, 5, 2) + SQUOTE ;
end ;
// yyyymmdd-hhnnss or yyyymmdd to 'yyyy-mm-ddThh:nn:ss'
function PackedDTtoISODT (info: string): string ;
begin
result := 'NULL' ;
if length (info) = 8 then info := info + '-000000' ;
if length (info) <> 15 then exit ;
result := AlphaDTtoISODT (copy (info, 1, 8), copy (info, 10, 6)) ;
end ;
// TDateTime to dd-mmm-yyyy
function DTtoAlpha (D: TDateTime): string ;
begin
result := FormatDateTime (DateAlphaMask, D) ;
end ;
// TDateTime to 1st January 2010
function DTtoLongAlpha (D: TDateTime): string ;
var
day, month, year: word ;
begin
SysUtils.DecodeDate (D, Year, Month, Day) ;
case day of
1,21,31: result := 'st' ;
2,22: result := 'nd' ;
3,23: result := 'rd' ;
else
result := 'th' ;
end;
if (month < 1) or (month > 12) then month := 1 ;
result := IntToStr (day) + result + space +
MyFormatSettings.LongMonthNames [month] + space + IntToStr (year) ;
end ;
// TDateTime to dd-mmm-yyyy hh:mm
function DTTtoAlpha (D: TDateTime): string ;
begin
result := FormatDateTime (DateAlphaMask + ' ' + ShortTimeMask, D) ;
end ;
// yyyy-mm-ddThh:nn:ss to yyyymmdd-hhnnss
function ISODTtoPacked (ISO: string): string ;
var
L: integer ;
begin
result := '' ;
L := Length (ISO) ;
if L < 10 then exit ;
if ISO [5] <> '-' then exit ;
result := CopyLeft (ISO, 4) + Copy (ISO, 6, 2) + Copy (ISO, 9, 2) ;
if L < 19 then exit ;
if ISO [11] <> 'T' then exit ;
result := result + '-' + Copy (ISO, 12, 2) + Copy (ISO, 15, 2) + Copy (ISO, 18, 2) ;
end ;
// fuzzy date/time comparison, within one second
// Warning - does not work with file time stamps, need at least two secs
function EqualDateTime(const A, B: TDateTime): boolean;
begin
result := (Abs (A - B) < OneSecond) ;
end;
// date/time difference in seconds, max one day
function DiffDateTime(const A, B: TDateTime): integer ;
begin
result := SecsPerDay ;
if Abs (A - B) >= 1 then exit ;
result := Trunc ((Abs (A - B)) * SecsPerDay) ;
end;
// quote string, unless blank when NULL (for SQL)
function QuoteNull (S: string): string ;
begin
if S = '' then
result := 'NULL'
else
result := QuotedStr (S) ;
end ;
// convert date/time to quoted SQL ISO date or NULL
function QuoteSQLDate (D: TDateTime): string ;
begin
if D <= 100 then
result := 'NULL'
else
result := QuotedStr (FormatDateTime (ISODateTimeMask, D)) ;
end ;
// return boolean in English
function GetYN (value: boolean): Char ;
begin
if value then
result := 'Y'
else
result := 'N' ;
end ;
function GetYesNo (value: boolean): string ;
begin
if value then
result := 'YES'
else
result := 'NO' ;
end ;
// check boolean from English - 25 March 2009
function CheckYesNo (const value: string): boolean ;
begin
result := (LowerCaseAnsi (AnsiString (Copy (value, 1, 1))) = 'y') OR (value = '1') ; // 7 Aug 2010
end;
// return boolean as true or false literals - 25 March 2009
function GetTrueFalse (opt: boolean): string ;
begin
if opt then
result := 'True'
else
result := 'False' ;
end ;
// check boolean from true or false - 25 March 2009
function CheckTrueFalse (const value: string): boolean ;
begin
result := (LowerCaseAnsi (AnsiString (Copy (value, 1, 1))) = 't') OR (value = '1') ; // 7 Aug 2010
end;
// TDateTime to to yyyy-mm-ddThh:nn:ss - no quotes
function DT2ISODT (D: TDateTime): string ;
begin
result := FormatDateTime (ISODateTimeMask, D) ;
end ;
// TDateTime to to 'yyyy-mm-ddThh:nn:ss'
function DTtoISODT (D: TDateTime): string ;
begin
result := QuotedStr (DT2ISODT (D)) ;
end ;
// convert time to quote SQL ISO date
function QuoteSQLTime (T: TDateTime): string ;
begin
result := QuotedStr (TimeToNStr (T)) ;
end ;
{ time functions }
function DateTimeToAStr(const DateTime: TDateTime): string; // always alpha month and numeric hh:mm:ss
begin
DateTimeToString(Result, DateTimeAlphaMask, DateTime);
end;
function DateToAStr(const DateTime: TDateTime): string; // always alpha month
begin
DateTimeToString(Result, DateAlphaMask, DateTime);
end;
function TimeToNStr(const DateTime: TDateTime): string; // always numeric hh:mm:ss
begin
DateTimeToString(Result, ISOTimeMask, DateTime);
end;
function TimeToZStr(const DateTime: TDateTime): string; // always numeric hh:mm:ss:zzz
begin
DateTimeToString(Result, LongTimeMask, DateTime);
end;
function timeHour(T: TDateTime): Integer;
var
Hour,Minute,Sec,Sec100: Word;
begin
DecodeTime(T,Hour,Minute,Sec,Sec100);
Result:=Hour;
end;
function timeMin(T: TDateTime): Integer;
var
Hour,Minute,Sec,Sec100: Word;
begin
DecodeTime(T,Hour,Minute,Sec,Sec100);
Result:=Minute;
end;
function timeSec(T: TDateTime): Integer;
var
Hour,Minute,Sec,Sec100: Word;
begin
DecodeTime(T,Hour,Minute,Sec,Sec100);
Result:=Sec;
end;
function TimeToInt(T: TDateTime): Integer; // returns seconds
begin
Result := -1 ;
if T > 20000 then exit ; // too many days for integer
try
Result := Trunc ((MSecsPerday * Frac (T)) / 1000); // time
Result := Result + (Trunc (T) * SecsPerDay) ; // date
except
Result := 0 ;
end ;
end;
function HoursToTime (hours: integer): TDateTime ;
begin
if hours = 0 then
result := 0
else
result := hours / (SecsPerDay / (60 * 60) ) ;
end ;
function MinsToTime (mins: integer): TDateTime ;
begin
if mins = 0 then
result := 0
else
result := mins / (SecsPerDay / 60) ;
end ;
function SecsToTime (secs: integer): TDateTime ;
begin
if secs = 0 then
result := 0
else
result := secs / SecsPerDay ;
end ;
function TimerToStr (duration: TDateTime): string ;
var
hours: integer ;
info: string ; // 7 Aug 2010
begin
info := copy (FormatDateTime ('hh:mm:ss', frac (duration)), 4, 5) ;
hours := trunc (duration * 24) ;
if hours = 0 then
begin
if (Length (info) > 0) and (info [1] = '0') then // 7 Aug 2010
result := copy (info, 2, 9)
else
result := info ;
exit ;
end ;
result := IntToStr (hours) + string (MyFormatSettings.TimeSeparator) + info ; // 7 Aug 2010
end ;
function SecsToMinStr (secs: integer): string ;
begin
result := '0' ;
if secs = 0 then exit ;
result := IntToStr (secs div 60) + ':' + LInt2ZStr (secs mod 60, 2) ;
end ;
function SecsToHourStr (secs: integer): string ;
begin
result := TimeToNStr (secs / SecsPerDay) ;
end ;
function sysTempPath: String;
var
Buffer: array [0..MAX_PATH] of WideChar ;
begin
SetString (Result, Buffer, GetTempPathW (Length (Buffer) - 1, Buffer)) ;
end;
function sysTempPathWide: UnicodeString;
var
Buffer: array [0..MAX_PATH] of WideChar ;
begin
SetString (Result, Buffer, GetTempPathW (Length (Buffer) - 1, Buffer)) ;
end;
function sysWindowsDir: String;
begin
Result := GetWinDir ; // Unicode, duplicate
end;
procedure sysBeep;
begin
messageBeep($FFFF);
end;
function strLastCh(const S: string): Char ;
begin
result := nulll ;
if length (S) <> 0 then result := S [Length (S)] ;
end;
procedure strStripLast (var S: string);
begin
if Length (S) > 0 then Delete (S, Length(S), 1) ;
end;
function strAddSlash(const S: string): string ;
begin
result := S ;
if strLastCh (result) <> SLASH then result := result + SLASH ;
end;
function strDelSlash(const S: string): string;
begin
result := S ;
if strLastCh (result) = SLASH then Delete (result, Length (result), 1) ;
end;
function ExtractUNIXPath(const FileName: string): string;
var
I: Integer;
begin
I := LastDelimiter('/', FileName);
Result := Copy(FileName, 1, I);
end;
function ExtractUNIXName(const FileName: string): string;
var
I: Integer;
begin
I := LastDelimiter('/', FileName);
Result := Copy(FileName, I + 1, MaxInt);
end;
{$IFNDEF CPUX64}
function CharPos (TheChar: AnsiChar; const Str: AnsiString): Integer;
// Find a char in a string - faster than Pos
asm
push edi // save needed regs
or edx, edx // got an empty string?
jz @@1 // if yes, get out now
mov edi, edx // EDI = source string
mov ecx, [edi-4] // get length of string
cld // specify auto-inc
repnz scasb // find the char
mov eax,0 // assume failure
jnz @@2 // yup -- char wasn't found
sub edi, edx // calculate index
xchg edi, edx // need result in edx
@@1: mov eax, edx // copy into EAX
@@2: pop edi // restore EDI
end;
{$ELSE}
function CharPos (TheChar: AnsiChar; const Str: AnsiString): Integer;
begin
result := Pos (TheChar, Str) ;
end;
{$ENDIF}
// 10 Nov 2011 - reverse Pos, similar to Pos but backwards
function PosRev (const SubStr: string; const S: string): Integer;
var
I, J: integer ;
begin
result := 0 ;
for I := Length (S) downto 1 do
begin
J := PosEx (SubStr, S, I) ;
if J > 0 then
begin
result := J ;
break ;
end;
end;
end;
{$IFNDEF CPUX64}
function DownCase( ch : AnsiChar ) : AnsiChar;
asm
{ -> AL Character }
{ <- AL Result }
CMP AL,'A'
JB @@exit
CMP AL,'Z'
JA @@exit
ADD AL,'a' - 'A'
@@exit:
end;
{$ELSE}
function DownCase( ch : AnsiChar ) : AnsiChar;
begin
if (ch >= 'A') and (ch <= 'Z') then
result := AnsiChar (Ord (ch) + (Ord ('a') - Ord ('A')))
else
result := ch ;
end;
{$ENDIF}
// convert string to hex quads
function ConvHexQuads (S: string): string ;
var
I, J: integer ;
begin
J := 0 ;
result := '' ;
if Length (S) = 0 then exit ;
for I := 1 to Length (S) do
begin
result := result + IntToHex (Ord (S [I]), 2) ;
inc (J) ;
if J = 4 then
begin
J := 0 ;
result := result + space ;
end ;
end ;
end ;
// get performance counter frequency, Win95 and NT3.1 and later
// PC09=3,579,545, might be processor frequency 2 gig
function GetPerfCountsPerSec: int64 ;
begin
if PerfFreqCountsPerSec = 0 then
QueryPerformanceFrequency (PerfFreqCountsPerSec) ;
result := PerfFreqCountsPerSec ;
end ;
function PerfCountCurrent: int64 ;
begin
QueryPerformanceCounter (result) ;
end ;
function PerfCountToMilli (LI: int64): integer ;
begin
result := (LI * 1000) div GetPerfCountsPerSec ;
end ;
function PerfCountToSecs (LI: int64): integer ;
begin
result := LI div GetPerfCountsPerSec ;
end ;
function PerfCountGetMilli (startLI: int64): integer ;
var
curLI: int64 ;
begin
QueryPerformanceCounter (curLI) ;
result := PerfCountToMilli (curLI - startLI) ;
end ;
function PerfCountGetSecs (startLI: int64): integer ;
var
curLI: int64 ;
begin
QueryPerformanceCounter (curLI) ;
result := PerfCountToSecs (curLI - startLI) ;
end ;
function PerfCountGetMillStr (startLI: int64): string ;
begin
result := LInt2CEStr (PerfCountGetMilli (startLI)) + 'ms' ;
end ;
// 'NowPC' function that returns the current time and date to a resolution of 200ns....
// Like Now, but returns a value to the performance counter resolution }
// WARNING - need set PerfFreqAligned to false if time is corrected
// check WM_TIMECHANGE message
function NowPC: TDateTime ;
var
f_Now : comp;
LI : int64;
f_ElapsedSinceStart : extended;
begin
// first access, aligns the performance counter and date / time
if NOT PerfFreqAligned then
begin
f_TDStartValue := Now ;
QueryPerformanceCounter (LI) ;
f_PCStartValue := LI ;
f_PCCountsPerDay := GetPerfCountsPerSec * SecsPerDay ;
PerfFreqAligned := True ;
end;
QueryPerformanceCounter (LI) ;
f_Now := LI ;
f_ElapsedSinceStart := f_Now - f_PCStartValue ;
If f_ElapsedSinceStart < 0.0 then
f_ElapsedSinceStart := f_ElapsedSinceStart - 1 ; // Rolled over
// scale to get a TDateTime
NowPC := f_TDStartValue + (f_ElapsedSinceStart / f_PCCountsPerDay) ;
end;
// date parsing borrowed from HttpApp but adapted to allow time hh:mm without seconds
// and for two digit W2K years, and with fewer exceptions
const
// These strings are NOT to be resourced
Months: array[1..13] of string = (
'Jan', 'Feb', 'Mar', 'Apr',
'May', 'Jun', 'Jul', 'Aug',
'Sep', 'Oct', 'Nov', 'Dec', '');
DaysOfWeek: array[1..7] of string = (
'Sun', 'Mon', 'Tue', 'Wed',
'Thu', 'Fri', 'Sat');
function InetParseDate(const DateStr: string): TDateTime;
var
Month, Day, Year, Hour, Minute, Sec: Integer;
Parser: TParser;
StringStream: TStringStream;
temptime: TDateTime ;
function GetMonth: Boolean;
begin
Month := 1;
while not Parser.TokenSymbolIs(Months[Month]) and (Month < 13) do Inc(Month);
Result := Month < 13;
end;
procedure GetTime;
begin
with Parser do
begin
Hour := TokenInt;
NextToken;
if Token = ':' then NextToken;
Minute := TokenInt;
NextToken;
if Token = ':' then // angus, allow missing seconds
begin
NextToken;
Sec := TokenInt;
NextToken;
end ;
end;
end;
begin
Sec := 0 ;
result := 0 ;
if DateStr = '' then exit ; // angus, ignore blank
StringStream := TStringStream.Create(DateStr);
try
Parser := TParser.Create(StringStream);
with Parser do
try
NextToken;
if Token = ':' then NextToken;
NextToken; // get day of week, might not exixt...
if Token = ',' then NextToken;
if GetMonth then
begin
NextToken;
Day := TokenInt;
NextToken;
GetTime;
Year := TokenInt;
end else
begin
Day := TokenInt;
NextToken;
if Token = '-' then NextToken;
GetMonth;
NextToken;
if Token = '-' then NextToken;
Year := TokenInt;
if Year < 50 then Inc(Year, 2000); // Y2K pivot
if Year < 100 then Inc(Year, 1900);
NextToken;
GetTime;
end;
// avoid exceptions
if TryEncodeDate (Year, Month, Day, Result) then
begin
if TryEncodeTime (Hour, Minute, Sec, 0, temptime) then
result := result + temptime ;
end ;
finally
Free;
end;
finally
StringStream.Free;
end;
end;
function MagURLEncode(const psSrc: AnsiString): AnsiString; // Dec 2020
const
UnsafeChars = ' *#%<>+'; {do not localize}
var
i: Integer;
begin
Result := ''; { do not localize }
for i := 1 to Length(psSrc) do
begin
if psSrc[i] = space then
Result := Result + '+'
else if (psSrc[i] in [CR, LF,'*','#','%','<','>','+','&','''','"']) or (psSrc[i] >= #$80) then
begin
Result := Result + '%' + IntToHexAnsi(Ord(psSrc[i]), 2); {do not localize}
end
else
begin
Result := Result + psSrc[i];
end;
end;
end;
function MagURLDecode(const AStr: AnsiString): AnsiString; // borrowed from httpapp.pas // Dec 2020
var
Sp, Rp, Cp: PAnsiChar;
S: AnsiString;
begin
SetLength(Result, Length(AStr));
Sp := PAnsiChar(AStr);
Rp := PAnsiChar(Result);
Cp := Sp;
try
while Sp^ <> #0 do
begin
case Sp^ of
'+': Rp^ := ' ';
'%': begin
// Look for an escaped % (%%) or % encoded character
Inc(Sp);
if Sp^ = '%' then
Rp^ := '%'
else
begin
Cp := Sp;
Inc(Sp);
if (Cp^ <> #0) and (Sp^ <> #0) then
begin
S := AnsiChar('$') + AnsiChar(Cp^) + AnsiChar(Sp^);
Rp^ := AnsiChar(AscToIntAnsi(S));
end
else
exit ;
// raise EWebBrokerException.CreateFmt(sErrorDecodingURLText, [Cp - PChar(AStr)]);
end;
end;
else
Rp^ := Sp^;
end;
Inc(Rp);
Inc(Sp);
end;
except
on E:EConvertError do
raise EConvertError.CreateFmt('Invalid URL Encoded Char',
[AnsiChar('%') + AnsiChar(Cp^) + AnsiChar(Sp^), Cp - PAnsiChar(AStr)])
end;
SetLength(Result, Rp - PAnsiChar(Result));
end;
function FormatLastError: string ;
begin
result := SysErrorMessage (GetLastError) + ' [' + IntToCStr (GetLastError) + ']' ;
end ;
// display kilobytes
function Int2Kbytes (value: integer): string ;
begin
if value > 999 then
result := LInt2CStr ((value + 500) div 1000, 7) + 'K'
else
if value = 0 then result := ' 0'
else
result := ' < 1K' ;
end ;
// display megabytes, no max!
function Int2Mbytes (value: int64): string ;
begin
if value > 999999 then
begin
value := value div 10 ;
result := LInt2CStr ((value + 50000) div 100000, 7) + 'M'
end
else
if value = 0 then result := ' 0'
else
result := ' < 1M' ;
end ;
{function IntToKbyte (Value: Int64): String;
var
float: double ;
begin
float := value ;
if (float / 100) >= GBYTE then
FmtStr (result, '%5.0fG', [float / GBYTE]) // 134G
else if (float / 10) >= GBYTE then
FmtStr (result, '%5.1fG', [float / GBYTE]) // 13.4G
else if float >= GBYTE then
FmtStr (result, '%5.2fG', [float / GBYTE]) // 3.44G
else if float >= (MBYTE * 100) then
FmtStr (result, '%5.0fM', [float / MBYTE]) // 234M
else if float >= (MBYTE * 10) then
FmtStr (result, '%5.1fM', [float / MBYTE]) // 12.4M
else if float >= MBYTE then
FmtStr (result, '%5.2fM', [float / MBYTE]) // 5.67M
else if float >= (KBYTE * 100) then
FmtStr (result, '%5.0fK', [float / KBYTE]) // 678K
else if float >= (KBYTE * 10) then
FmtStr (result, '%5.1fK', [float / KBYTE]) // 76.5K
else if float >= KBYTE then
FmtStr (result, '%5.2fK', [float / KBYTE]) // 4.78K
else
FmtStr (result, '%5.0f ', [float]) ; // 123
result := Trim (result) ;
end ; }
function IntToKbyte (Value: Int64; Bytes: boolean = false): String;
var
float, float2: double ;
mask, suffix: string ;
begin
float := value ;
if (float / 100) >= GBYTE then
begin
mask := '%5.0f' ;
suffix := 'G';
float2 := float / GBYTE ; // 134G
end
else if (float / 10) >= GBYTE then
begin
mask := '%5.1f' ;
suffix := 'G';
float2 := float / GBYTE ; // 13.4G
end
else if float >= GBYTE then
begin
mask := '%5.2f' ;
suffix := 'G';
float2 := float / GBYTE ; // 3.44G
end
else if float >= (MBYTE * 100) then
begin
mask := '%5.0f' ;
suffix := 'M';
float2 := float / MBYTE ; // 234M
end
else if float >= (MBYTE * 10) then
begin
mask := '%5.1f' ;
suffix := 'M';
float2 := float / MBYTE ; // 12.4M
end
else if float >= MBYTE then
begin
mask := '%5.2f' ;
suffix := 'M';
float2 := float / MBYTE ; // 12.4M
end
else if float >= (KBYTE * 100) then
begin
mask := '%5.0f' ;
suffix := 'K';
float2 := float / KBYTE ; // 678K
end
else if float >= (KBYTE * 10) then
begin
mask := '%5.1f' ;
suffix := 'K';
float2 := float / KBYTE ; // 76.5K
end
else if float >= KBYTE then
begin
mask := '%5.2f' ;
suffix := 'K';
float2 := float / KBYTE ; // 4.78K
end
else
begin
mask := '%5.0f' ;
suffix := '';
float2 := float ; // 123
end ;
if Bytes then // 20 Oct 2011 improve result a little
result := Trim (Format (mask, [float2])) + space + suffix + 'bytes'
else
result := Trim (Format (mask, [float2])) + suffix ;
end ;
procedure EmptyRecycleBin (const fname: WideString) ;
begin
SHEmptyRecycleBin (0, PWideChar (fname),
SHERB_NOCONFIRMATION + SHERB_NOPROGRESSUI) ;
end;
// effectively pages a program out of main memory
procedure TrimWorkingSetMemory ;
var
MainHandle: THandle;
begin
MainHandle := OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessID) ;
SetProcessWorkingSetSize (MainHandle, $FFFFFFFF, $FFFFFFFF);
CloseHandle(MainHandle);
end;
// helper functions for timers and triggers using GetTickCount - which wraps after 49 days
// note: Vista/2008 and later have GetTickCount64 which returns 64-bits
function GetTickCountX: longword ;
var
newtick: Int64 ;
begin
result := GetTickCount ;
// ensure special trigger values never returned - 18 Feb 2009
if (result = TriggerDisabled) or (result = TriggerImmediate) then result := 1 ;
if TicksTestOffset = 0 then exit ; // no testing, byebye
// TicksTestOffset is set in initialization so that the counter wraps five mins after startup
newtick := Int64 (result) + Int64 (TicksTestOffset) ;
if newtick >= MaxLongWrd then
result := newtick - MaxLongWrd
else
result := newtick ;
end ;
function DiffTicks (const StartTick, EndTick: longword): longword ;
begin
if (StartTick = TriggerImmediate) or (StartTick = TriggerDisabled) then // 25 May 2006
result := 0
else
begin
if EndTick >= StartTick then // 19 Oct 2005, was > but allow for zero
Result := EndTick - StartTick
else
Result := (MaxLongWord - StartTick) + EndTick ;
end ;
end ;
function ElapsedMSecs (const StartTick: longword): longword ;
begin
result := DiffTicks (StartTick, GetTickCountX) ;
end ;
function ElapsedTicks (const StartTick: longword): longword ;
begin
result := DiffTicks (StartTick, GetTickCountX) ;
end ;
function ElapsedSecs (const StartTick: longword): integer ;
begin
result := (DiffTicks (StartTick, GetTickCountX)) div TicksPerSecond ;
end ;
function WaitingSecs (const EndTick: longword): integer ;
begin
if (EndTick = TriggerImmediate) or (EndTick = TriggerDisabled) then
result := 0
else
result := (DiffTicks (GetTickCountX, EndTick)) div TicksPerSecond ;
end ;
function ElapsedMins (const StartTick: longword): integer ;
begin
result := (DiffTicks (StartTick, GetTickCountX)) div TicksPerMinute ;
end ;
function AddTrgMsecs (const TickCount, MilliSecs: longword): longword ;
begin
result := MilliSecs ;
if result > (MaxLongWord - TickCount) then
result := (MaxLongWord - TickCount) + result
else
result := result + TickCount ;
end ;
function AddTrgSecs (const TickCount, DurSecs: integer): longword ;
begin
result := TickCount ;
if DurSecs < 0 then exit ; // 22 June 2007
result := AddTrgMsecs (TickCount, longword (DurSecs) * TicksPerSecond) ;
end ;
function GetTrgMsecs (const MilliSecs: integer): longword ;
begin
result := TriggerImmediate ;
if MilliSecs < 0 then exit ; // 22 June 2007
result := AddTrgMsecs (GetTickCountX, MilliSecs) ;
end ;
function GetTrgSecs (const DurSecs: integer): longword ;
begin
result := TriggerImmediate ;
if DurSecs < 0 then exit ; // 22 June 2007
result := AddTrgMsecs (GetTickCountX, longword (DurSecs) * TicksPerSecond) ;
end ;
function GetTrgMins (const DurMins: integer): longword ;
begin
result := TriggerImmediate ;
if DurMins < 0 then exit ; // 22 June 2007
result := AddTrgMsecs (GetTickCountX, longword (DurMins) * TicksPerMinute) ;
end ;
function TestTrgTick (const TrgTick: longword): boolean ;
var
curtick: longword ;
begin
result := false ;
if TrgTick = TriggerDisabled then exit ; // special case for trigger disabled
if TrgTick = TriggerImmediate then
begin
result := true ; // special case for now
exit ;
end ;
curtick := GetTickCountX ;
if curtick <= MaxInteger then // less than 25 days, keep it simple
begin
if curtick >= TrgTick then result := true ;
exit ;
end ;
if TrgTick <= MaxInteger then exit ; // trigger was wrapped, can not have been reached
if curtick >= TrgTick then result := true ;
end ;
procedure FreeAndNilEx(var Obj);
var
Temp: TObject;
begin
if Pointer(Obj) = Nil then exit ;
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
// does program have administrator access
// useful on Vista since some things no longer work where admin access is assumed
function IsProgAdmin: Boolean;
var
psidAdmin: Pointer;
Token: THandle;
Count: DWORD;
TokenInfo: PTokenGroups;
HaveToken: Boolean;
I: Integer;
const
SE_GROUP_USE_FOR_DENY_ONLY = $00000010;
SECURITY_NT_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
SECURITY_BUILTIN_DOMAIN_RID = ($00000020);
DOMAIN_ALIAS_RID_ADMINS = ($00000220);
begin
Result := False;
if Win32Platform <> VER_PLATFORM_WIN32_NT then
begin
result := true ;
exit ;
end ;
psidAdmin := nil;
TokenInfo := nil;
HaveToken := False;
try
HaveToken := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token);
if (not HaveToken) and (GetLastError = ERROR_NO_TOKEN) then
HaveToken := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token);
if HaveToken then
begin
Win32Check(AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
psidAdmin));
if GetTokenInformation(Token, TokenGroups, nil, 0, Count) or
(GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
RaiseLastOSError;
TokenInfo := PTokenGroups(AllocMem(Count));
Win32Check(GetTokenInformation(Token, TokenGroups, TokenInfo, Count, Count));
for I := 0 to TokenInfo^.GroupCount - 1 do
begin
{$RANGECHECKS OFF} // Groups is an array [0..0] of TSIDAndAttributes, ignore ERangeError
Result := EqualSid(psidAdmin, TokenInfo^.Groups[I].Sid) and
(TokenInfo^.Groups[I].Attributes and SE_GROUP_USE_FOR_DENY_ONLY = 0); //Vista??
{$IFDEF RANGECHECKS_ON}
{$RANGECHECKS ON}
{$ENDIF RANGECHECKS_ON}
if Result then
Break;
end;
end;
finally
if TokenInfo <> nil then
FreeMem(TokenInfo);
if HaveToken then
CloseHandle(Token);
if psidAdmin <> nil then
FreeSid(psidAdmin);
end;
end;
// retrieves information about a locale specified by LCTYPE LOCALE_xxx identifiers
function GetLcTypeInfo (Id: integer): UnicodeString ;
var
Buffer: array [0..255] of WideChar ;
begin
result := '' ;
if GetLocaleInfoW (LOCALE_SYSTEM_DEFAULT, Id, Buffer, 254) > 0 then result := Buffer ;
end ;
// format an IPv6 address with []
function FormatIpAddr (const Addr: string): string ;
begin
if (Pos ('.', Addr) = 0) and (Pos ('[', Addr) = 0) and (Pos (':', Addr) > 0) then
result := '[' + Addr + ']'
else
result := Addr ;
end;
// format an IPv6 address with [] and port
function FormatIpAddrPort (const Addr, Port: string): string ;
begin
result := FormatIpAddr (Addr) + ':' + Port ;
end;
// strip [] off IPv6 addresses
function StripIpAddr (const Addr: string): string ;
begin
if (Pos ('[', Addr) = 1) and (Addr [Length (Addr)] = ']') then
result := Copy (Addr, 2, Length (Addr) - 2)
else
result := Addr ;
end;
procedure GetMyFormatSettings ; // 3 Sept 2012
begin
{$IF CompilerVersion >= 23.0} // XE2 and later
MyFormatSettings := TFormatSettings.Create (GetThreadLocale) ;
{$ELSE}
GetLocaleFormatSettings (GetThreadLocale, MyFormatSettings) ;
{$IFEND}
end;
initialization
SensapiModule := 0 ;
TicksTestOffset := 0 ;
@GetProductInfo := Nil ; // 10 Aug 2010
// force GetTickCount wrap in 5 mins - next line normally commented out
// TicksTestOffset := MaxLongWord - GetTickCount - (5 * 60 * 1000) ;
// keep OS version
MagRasOSVersion := OSW9x ;
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
GetOSInfo ; // 3 Oct 2014 - works correctly with Windows 8.1 and later
if OsInfo.dwMajorVersion = 5 then
begin
MagRasOSVersion := OSW2K ;
if OsInfo.dwMinorVersion > 0 then MagRasOSVersion := OSWXP ; // and 2003
end
else if OsInfo.dwMajorVersion >= 6 then
begin
MagRasOSVersion := OSVista ; // and 2008 and 7
if OsInfo.dwMinorVersion = 1 then MagRasOSVersion := OS7 ; // and 2008 R2
if OsInfo.dwMinorVersion >= 2 then MagRasOSVersion := OS8 ; // and 2012
if OsInfo.dwMinorVersion >= 4 then MagRasOSVersion := OS10 ; // and 2016
if OsInfo.dwMajorVersion >= 10 then MagRasOSVersion := OS10 ; // and 2016, Jan 2018
end
else
MagRasOSVersion := OSNT4 ;
end ;
GetMyFormatSettings ; // 3 Sept 2012
finalization
if SensapiModule <> 0 then
begin
FreeLibrary (SensapiModule) ;
SensapiModule := 0 ;
end ;
end.