unit VCLEx;
{$MODE Delphi}
{$codepage UTF8}
interface
uses ShellApi, windows, sysutils, strutils, LazFileUtils, LazUTF8;
type IntEx = {$IFDEF Win64}Int64{$ELSE}Integer{$ENDIF};
     TWaitEvent = procedure;
     TOSPlatform = (ospUnknown, ospWin32, ospWin64);
     TPrivilegeState = (psError, psLimitedUser, psAdmin);
     Percent = 0..100;
     TRandomRange = (rrUpperCasesLetters, rrLowerCasesLetters, rrNumbers, rrStandartSymbols);
     TRandomRanges = set of TRandomRange;
//stdcalls
function IntToBool (const AInt: IntEx): Boolean; STDCALL;
function BoolToInt (const ABool: Boolean): IntEx; STDCALL;
function CopyDirectory (const AFromDir, AToDir: String; const AFlags: Word = FOF_FILESONLY): Boolean; STDCALL;
function MoveDirectory (const AFromDir, AToDir: String; const AFlags: Word = FOF_FILESONLY): Boolean; STDCALL;
function DelDirectory (const ADir: String; const AFlags: Word = FOF_SILENT or FOF_NOCONFIRMATION): Boolean; STDCALL;
procedure WaitEx (const AMs: Int64; OnWait: TWaitEvent); STDCALL;
procedure Wait (const AMs: Int64); STDCALL;
function GetBuildPlatform: TOSPlatform; STDCALL;
function GetWindowsUserPrivilege: TPrivilegeState; STDCALL;
function ExtractUpDir(const ADir: String; var VSuccess: Boolean): String; STDCALL;
function GetAnyFileType (const AFileName: UTF8String): UTF8String; STDCALL;
function FileSizeToStr (const AFS: Int64; const AScaleCaptions: array of String): String; OVERLOAD; STDCALL;
function FileSizeToStr (const AFS: Int64): String; OVERLOAD; STDCALL;
function GetRandomString (const sLength: Integer; const ARange: TRandomRanges = [rrUpperCasesLetters, rrLowerCasesLetters]; const AIncludedSymbols: String = ''; const AExcludedSymbols: String = ''): String; STDCALL;
implementation
function IntToBool (const AInt: IntEx): Boolean;
begin
if AInt >= 0 then
 Result:= True
 else
 Result:= False;
end;
function BoolToInt (const ABool: Boolean): IntEx;
begin
if ABool then
 Result:= 1
 else
 Result:= -1;
end;
//from http://www.delphiworld.narod.ru/base/copy_del_move_dir.html
function CopyDirectory (const AFromDir, AToDir: String; const AFlags: Word = FOF_FILESONLY): Boolean;
var fos: TSHFileOpStruct;
begin
with fos do
 begin
 wFunc:= FO_COPY;
 fFlags:= AFlags;
 pFrom:= PChar(AFromDir + #0);
 pTo:= PChar(AToDir);
 end;
Result:= (0 = SHFileOperation(fos));
end;
function MoveDirectory (const AFromDir, AToDir: String; const AFlags: Word = FOF_FILESONLY): Boolean;
var fos: TSHFileOpStruct;
begin
with fos do
 begin
 wFunc:= FO_MOVE;
 fFlags:= AFlags;
 pFrom:= PChar(AFromDir + #0);
 pTo:= PChar(AToDir);
 end;
Result:= (0 = SHFileOperation(fos));
end;
function DelDirectory (const ADir: String; const AFlags: Word = FOF_SILENT or FOF_NOCONFIRMATION): Boolean;
var
  fos: TSHFileOpStruct;
begin
with fos do
 begin
 wFunc:= FO_DELETE;
 fFlags:= AFlags;
 pFrom:= PChar(ADir + #0);
 end;
Result:= (0 = SHFileOperation(fos));
end;
//---
procedure WaitEx (const AMs: Int64; OnWait: TWaitEvent);
var STime: Int64;
begin
STime:= GetTickCount64;
repeat
  OnWait;
  until (GetTickCount64 - STime) = AMs;
end;
procedure Wait (const AMs: Int64);
 procedure MyWait;
 begin
 end;
begin
WaitEx(AMs, @MyWait);
end;
//GetBuildPlatform
function GetBuildPlatform: TOSPlatform;
begin
Result:= ospUnknown;
if LowerCase({$I %FPCTARGETOS%}) = 'win32' then
 Result:= ospWin32;
if LowerCase({$I %FPCTARGETOS%}) = 'win64' then
 Result:= ospWin64;
end;
//GetWindowsUserPrivilege
function GetWindowsUserPrivilege: TPrivilegeState;
const SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
      SECURITY_BUILTIN_DOMAIN_RID = $00000020;
      DOMAIN_ALIAS_RID_ADMINS = $00000220;
      SECURITY_MANDATORY_HIGH_RID = $00003000;
      TokenIntegrityLevel = 25;
var hAccessToken: THandle;
    ptgGroups: PTokenGroups;
    dwInfoBufferSize: DWORD;
    psidAdministrators: PSID;
    I: Integer;
    SubAuthority: DWORD;
begin
Result:= psError;
if Win32Platform <> VER_PLATFORM_WIN32_NT then
 Exit;
if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, hAccessToken) then
 if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hAccessToken) then
  Exit;
try
 GetMem(ptgGroups, 1024);
 try
  if Win32MajorVersion < 6 then
   begin
   if not GetTokenInformation(hAccessToken, TokenGroups, ptgGroups, 1024, dwInfoBufferSize) then
    Exit;
   AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdministrators);
   try
    Result:= psLimitedUser;
    for I:= 0 to ptgGroups.GroupCount - 1 do
     if EqualSid(psidAdministrators, ptgGroups.Groups[I].Sid) then
      begin
      Result:= psAdmin;
      Break;
      end;
    finally
    FreeSid(psidAdministrators);
    end;
   end
   else
   begin
   if GetTokenInformation(hAccessToken, TTokenInformationClass(TokenIntegrityLevel), ptgGroups, 1024, dwInfoBufferSize) and IsValidSid(PSIDAndAttributes(ptgGroups)^.Sid) then
    begin
    Result:= psLimitedUser;
    SubAuthority:= GetSidSubAuthorityCount(PSIDAndAttributes(ptgGroups)^.Sid)^ - 1;
    if GetSidSubAuthority(PSIDAndAttributes(ptgGroups)^.Sid, SubAuthority)^ >= SECURITY_MANDATORY_HIGH_RID then
     Result:= psAdmin;
    end;
   end;
  finally
  FreeMem(ptgGroups);
  end;
 finally
 CloseHandle(hAccessToken);
 end;
end;
function ExtractUpDir (const ADir: String; var VSuccess: Boolean): String;
var CurrDelim, NextDelim: Integer;
    s, Str: String;
begin
Str:= ExcludeTrailingBackslash(ADir);
if Length(ADir) < 4 then
 begin
 VSuccess:= false;
 Result:= ADir;
 Exit;
 end;
s:= '';
CurrDelim:= 1;
repeat
 NextDelim:= PosEx('\', Str, CurrDelim);
 if NextDelim = 0 then
  NextDelim:= Length(Str) + 1;
 if NextDelim < Length(Str) then
  s:= s + Copy(Str, CurrDelim, NextDelim - CurrDelim) + '\';
 CurrDelim:= NextDelim + 1;
 until (CurrDelim > Length(Str));
VSuccess:= DirectoryExistsUTF8(s);
Result:= s;
end;
function GetAnyFileType (const AFileName: UTF8String): UTF8String;
var FileInfo: TSHFILEINFO;
begin
Result:= '';
FillChar(FileInfo, SizeOf(FileInfo), 0);
if (SHGetFileInfo(PChar(ExtractFileExt(AFileName)), FILE_ATTRIBUTE_NORMAL, FileInfo, SizeOf(FileInfo), SHGFI_TYPENAME or SHGFI_USEFILEATTRIBUTES) <> 0) then
 Result:= AnsiToUtf8(FileInfo.szTypeName);
end;
function FileSizeToStr (const AFS: Int64; const AScaleCaptions: array of String): String;
var ARSize: Real;
begin
if AFS < 1024 then
 begin
 Result:= Format('%d ' + AScaleCaptions[0], [AFS]);
 Exit;
 end;
if AFS < 1048576 then
 begin
 ARSize:= Round((AFS / 1024)*100)/100;
 Result:= FloatToStr(ARSize) + ' ' + AScaleCaptions[1];
 Exit;
 end;
if AFS < 1073741824 then
 begin
 ARSize:= Round((AFS / 1048576)*100)/100;
 Result:= FloatToStr(ARSize) + ' ' + AScaleCaptions[2];
 Exit;
 end;
ARSize:= Round((AFS / 1073741824)*100)/100;
Result:= FloatToStr(ARSize) + ' ' + AScaleCaptions[3];
end;
function FileSizeToStr (const AFS: Int64): String;
begin
Result:= FileSizeToStr(AFS, ['Byte', 'KB', 'MB', 'GB']);
end;
function GetRandomString (const sLength: Integer; const ARange: TRandomRanges = [rrUpperCasesLetters, rrLowerCasesLetters]; const AIncludedSymbols: String = ''; const AExcludedSymbols: String = ''): String;
const UpperCaseChars = 'ABCDEFGHIKLMNOPQRSTUVWXYZ';
      LowerCaseChars = 'abcdefghiklmnopqrstuvwxyz';
      NumbersChars = '0123456789';
      SymbolsChars = '!"#$%&''()*+,-.:;<=>?@\]^_`{|}~';
var i, j: Integer;
    Chars: String;
begin
SetLength (Result, sLength);
Chars:= AIncludedSymbols;
if rrUpperCasesLetters in ARange then
 Chars:= Chars + UpperCaseChars;
if rrLowerCasesLetters in ARange then
 Chars:= Chars + LowerCaseChars;
if rrNumbers in ARange then
 Chars:= Chars + NumbersChars;
if rrStandartSymbols in ARange then
 Chars:= Chars + SymbolsChars;
if Length(AExcludedSymbols) > 0 then
 for i:= 1 to Length(AExcludedSymbols) do
  begin
  j:= Pos(AExcludedSymbols[i], Chars);
  if j > 0 then
   Delete(Chars, j, 1);
  end;
for i:= 1 to sLength do
 Result[i]:= Chars[Random(Length(Chars))+1];
end;
end.
