6175 lines
202 KiB
Plaintext
6175 lines
202 KiB
Plaintext
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, 5th November 2018
|
|
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)
|
|
|
|
|
|
}
|
|
(* example manifest file that displays OS versions correctly with GetVersionExW
|
|
|
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="MyApp"
|
|
type="win32" />
|
|
<description>My application description</description>
|
|
<!-- Visual Styles -->
|
|
<dependency>
|
|
<dependentAssembly>
|
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
|
|
version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df"
|
|
language="*" />
|
|
</dependentAssembly>
|
|
</dependency>
|
|
<!-- UAC elevation -->
|
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
<security>
|
|
<requestedPrivileges>
|
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
|
<!-- or level="requireAdministrator" -->
|
|
</requestedPrivileges>
|
|
</security>
|
|
</trustInfo>
|
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
<application>
|
|
<!-- Windows Vista -->
|
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
|
<!-- Windows 7 -->
|
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
|
<!-- Windows 8 -->
|
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
|
<!-- Windows 8.1 -->
|
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
|
<!-- Windows 10 -->
|
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
|
</application>
|
|
</compatibility>
|
|
</assembly>
|
|
*)
|
|
|
|
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 (<start,>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 URLEncode(const psSrc: AnsiString): AnsiString;
|
|
function URLDecode(const AStr: AnsiString): AnsiString;
|
|
|
|
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 ; // <desktop>
|
|
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 ; // <user name>\Favorites
|
|
CSIDL_STARTUP = $0007 ; // Start Menu\Programs\Startup
|
|
CSIDL_RECENT = $0008 ; // <user name>\Recent
|
|
CSIDL_SENDTO = $0009 ; // <user name>\SendTo
|
|
CSIDL_BITBUCKET = $000a ; // <desktop>\Recycle Bin
|
|
CSIDL_STARTMENU = $000b ; // <user name>\Start Menu
|
|
CSIDL_MYDOCUMENTS = $000c ; // the user's My Documents folder
|
|
CSIDL_MYMUSIC = $000d ;
|
|
CSIDL_MYVIDEO = $000e ;
|
|
CSIDL_DESKTOPDIRECTORY = $0010 ; // <user name>\Desktop 16
|
|
CSIDL_DRIVES = $0011 ; // My Computer
|
|
CSIDL_NETWORK = $0012 ; // Network Neighborhood
|
|
CSIDL_NETHOOD = $0013 ; // <user name>\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 ; // <user name>\Application Data
|
|
CSIDL_PRINTHOOD = $001b ; // <user name>\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 ; // <user name>\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' ;
|
|
// 10 March 2014 see if tablet
|
|
if IsTouchTablet then info := info + ' Tablet' ;
|
|
end
|
|
else if OsInfo.dwMajorVersion = 10 then // 16 Apr 2015
|
|
begin
|
|
// Nov 2017 added version, ie 1709
|
|
inf3 := inf3 + space + MagGetRegHlmStr (CVNTKey, 'ReleaseId') ;
|
|
if (OsInfo.dwMinorVersion = 0) then
|
|
begin
|
|
if OsInfo.wProductType <= VER_NT_WORKSTATION then
|
|
info := 'Windows 10' + inf3 // 3 Oct 2014
|
|
else begin
|
|
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' ;
|
|
// 10 March 2014 see if tablet
|
|
if IsTouchTablet then info := info + ' Tablet' ;
|
|
end
|
|
else info := 'Unknown Windows Major version' ;
|
|
end
|
|
else
|
|
info := 'Unknown Windows platform' ;
|
|
end;
|
|
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 URLEncode(const psSrc: AnsiString): AnsiString;
|
|
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 URLDecode(const AStr: AnsiString): AnsiString; // borrowed from httpapp.pas
|
|
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 %<hex> 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.
|
|
|
|
|
|
|