unit uutlCommon; {$mode objfpc}{$H+} interface uses Classes, SysUtils, versionresource, versiontypes, typinfo {$IFDEF UNIX}, unixtype, pthreads {$ENDIF}; type //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlInterfaceNoRefCount = class(TObject, IUnknown) protected fRefCount: longint; { implement methods of IUnknown } function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; function _AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; function _Release: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; public property RefCount: LongInt read fRefCount; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlCSVList = class(TStringList) private fSkipDelims: boolean; function GetStrictDelText: string; procedure SetStrictDelText(const Value: string); public property StrictDelimitedText: string read GetStrictDelText write SetStrictDelText; // Skip repeated delims instead of reading empty lines? property SkipDelims: Boolean read fSkipDelims write fSkipDelims; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlVersionInfo = class(TObject) private fVersionRes: TVersionResource; function GetFixedInfo: TVersionFixedInfo; function GetStringFileInfo: TVersionStringFileInfo; function GetVarFileInfo: TVersionVarFileInfo; public property FixedInfo: TVersionFixedInfo read GetFixedInfo; property StringFileInfo: TVersionStringFileInfo read GetStringFileInfo; property VarFileInfo: TVersionVarFileInfo read GetVarFileInfo; function Load(const aInstance: THandle): Boolean; constructor Create; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EOutOfRangeException = class(Exception) private fMin: Integer; fMax: Integer; fIndex: Integer; public property Min: Integer read fMin; property Max: Integer read fMax; property Index: Integer read fIndex; constructor Create(const aIndex, aMin, aMax: Integer); constructor Create(const aMsg: String; const aIndex, aMin, aMax: Integer); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// IutlFilterBuilder = interface['{BC5039C7-42E7-428F-A3E7-DDF7757B1907}'] function Add(aDescr, aMask: string; const aAppendFilterToDesc: boolean = true): IutlFilterBuilder; function AddFilter(aFilter: string): IutlFilterBuilder; function Compose(const aIncludeAllSupported: String = ''; const aIncludeAllFiles: String = ''): string; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function Supports (const aInstance: TObject; const aClass: TClass; out aObj): Boolean; function GetTickCount64 (): QWord; function GetMicroTime (): QWord; function GetPlatformIdentitfier(): String; function utlRateLimited (const Reference: QWord; const Interval: QWord): boolean; procedure utlFinalizeObject (var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean); function utlFilterBuilder (): IutlFilterBuilder; implementation uses {$IFDEF WINDOWS} Windows, {$ELSE} Unix, BaseUnix, {$ENDIF} uutlGenerics; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// type TFilterBuilderImpl = class( TInterfacedObject, IutlFilterBuilder) private type TFilterEntry = class Descr, Filter: String; end; TFilterList = specialize TutlList; private fFilters: TFilterList; public function Add (aDescr, aMask: string; const aAppendFilterToDesc: boolean): IutlFilterBuilder; function AddFilter(aFilter: string): IutlFilterBuilder; function Compose (const aIncludeAllSupported: String = ''; const aIncludeAllFiles: String = ''): string; constructor Create; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Helper Methods//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function Supports(const aInstance: TObject; const aClass: TClass; out aObj): Boolean; begin result := Assigned(aInstance) and aInstance.InheritsFrom(aClass); if result then TObject(aObj) := aInstance else TObject(aObj) := nil; end; {$IF DEFINED(WINDOWS)} var PERF_FREQ: Int64; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetTickCount64: QWord; begin // GetTickCount64 is better, but we need to check the Windows version to use it Result := Windows.GetTickCount(); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetMicroTime: QWord; var pc: Int64; begin pc := 0; QueryPerformanceCounter(pc); result := (pc * 1000*1000) div PERF_FREQ; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {$ELSEIF DEFINED(UNIX)} function GetTickCount64: QWord; var tp: TTimeVal; begin fpgettimeofday(@tp, nil); Result := (Int64(tp.tv_sec) * 1000) + (tp.tv_usec div 1000); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetMicroTime: QWord; var tp: TTimeVal; begin fpgettimeofday(@tp, nil); Result := (Int64(tp.tv_sec) * 1000*1000) + tp.tv_usec; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {$ELSE} function GetTickCount64: QWord; begin Result := Trunc(Now * 24 * 60 * 60 * 1000); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetMicroTime: QWord; begin Result := Trunc(Now * 24 * 60 * 60 * 1000*1000); end; {$ENDIF} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetPlatformIdentitfier: String; {$IFDEF WINDOWS} function GetWindowsVersionStr(const aDefault: String): string; var osv: TOSVERSIONINFO; ver: cardinal; begin result := aDefault; osv.dwOSVersionInfoSize := SizeOf(osv); if GetVersionEx(osv) then begin ver := MAKELONG(osv.dwMinorVersion, osv.dwMajorVersion); // positive overflow: if system is newer, always detect as newest we knew instead of failing if ver >= $000A0000 then result := '10' else if ver >= $00060003 then result := '8_1' else if ver >= $00060002 then result := '8' else if ver >= $00060001 then result := '7' else if ver >= $00060000 then result := 'Vista' else if ver >= $00050002 then result := '2003' else if ver >= $00050001 then result := 'XP' else if ver >= $00050000 then result := '2000' else if ver >= $00040000 then result := 'NT4'; // ignore NT3, hmkay?; end; end; {$ENDIF} var os,ver,arch: string; begin result := ''; os := ''; ver := 'generic'; arch := ''; {$IF DEFINED(WINDOWS)} os := 'mswin'; ver := GetWindowsVersionStr(ver); {$ELSEIF DEFINED(LINUX)} os := 'linux'; {$Warning System Version String missing!} {$ENDIF} {$IF DEFINED(CPUX86)} arch := 'x86'; {$ELSEIF DEFINED(cpux86_64)} arch := 'x64'; {$ELSE} {$ERROR Unknown Architecture!} {$ENDIF} result := Format('%s-%s-%s', [os, ver, arch]); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function utlRateLimited(const Reference: QWord; const Interval: QWord): boolean; begin Result := GetMicroTime - Reference > Interval; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure utlFinalizeObject(var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean); var o: TObject; begin case aTypeInfo^.Kind of tkClass: begin if (aFreeObject) then begin o := TObject(obj); Pointer(obj) := nil; if Assigned(o) then o.Free; end; end; tkInterface: begin IUnknown(obj) := nil; end; tkAString: begin AnsiString(Obj) := ''; end; tkUString: begin UnicodeString(Obj) := ''; end; tkString: begin String(Obj) := ''; end; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function utlFilterBuilder: IutlFilterBuilder; begin result := TFilterBuilderImpl.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlInterfaceNoRefCount/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlInterfaceNoRefCount.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; begin if getinterface(iid,obj) then result:=S_OK else result:=longint(E_NOINTERFACE); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlInterfaceNoRefCount._AddRef: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; begin result := InterLockedIncrement(fRefCount); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlInterfaceNoRefCount._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; begin result := InterLockedDecrement(fRefCount); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlCSVList/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlCSVList.GetStrictDelText: string; var S: string; I, J, Cnt: Integer; q: boolean; LDelimiters: TSysCharSet; begin Cnt := GetCount; if (Cnt = 1) and (Get(0) = '') then Result := QuoteChar + QuoteChar else begin Result := ''; LDelimiters := [QuoteChar, Delimiter]; for I := 0 to Cnt - 1 do begin S := Get(I); q:= false; if S>'' then begin for J:= 1 to length(S) do if S[J] in LDelimiters then begin q:= true; break; end; if q then S := AnsiQuotedStr(S, QuoteChar); end else S := AnsiQuotedStr(S, QuoteChar); Result := Result + S + Delimiter; end; System.Delete(Result, Length(Result), 1); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlCSVList.SetStrictDelText(const Value: string); var S: String; P, P1: PChar; begin BeginUpdate; try Clear; P:= PChar(Value); if fSkipDelims then begin while (P^<>#0) and (P^=Delimiter) do begin P:= CharNext(P); end; end; while (P^<>#0) do begin if (P^ = QuoteChar) then begin S:= AnsiExtractQuotedStr(P, QuoteChar); end else begin P1:= P; while (P^<>#0) and (P^<>Delimiter) do begin P:= CharNext(P); end; SetString(S, P1, P - P1); end; Add(S); while (P^<>#0) and (P^<>Delimiter) do begin P:= CharNext(P); end; if (P^<>#0) then P:= CharNext(P); if fSkipDelims then begin while (P^<>#0) and (P^=Delimiter) do begin P:= CharNext(P); end; end; end; finally EndUpdate; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlVersionInfo/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlVersionInfo.GetFixedInfo: TVersionFixedInfo; begin result := fVersionRes.FixedInfo; end; function TutlVersionInfo.GetStringFileInfo: TVersionStringFileInfo; begin result := fVersionRes.StringFileInfo; end; function TutlVersionInfo.GetVarFileInfo: TVersionVarFileInfo; begin result := fVersionRes.VarFileInfo; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlVersionInfo.Load(const aInstance: THandle): Boolean; var Stream: TResourceStream; begin result := false; if (FindResource(aInstance, PChar(PtrInt(1)), PChar(RT_VERSION)) = 0) then exit; Stream := TResourceStream.CreateFromID(aInstance, 1, PChar(RT_VERSION)); try fVersionRes.SetCustomRawDataStream(Stream); fVersionRes.FixedInfo;// access some property to force load from the stream fVersionRes.SetCustomRawDataStream(nil); finally Stream.Free; end; result := true; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlVersionInfo.Create; begin inherited Create; fVersionRes := TVersionResource.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor TutlVersionInfo.Destroy; begin FreeAndNil(fVersionRes); inherited Destroy; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //EOutOfRange/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor EOutOfRangeException.Create(const aIndex, aMin, aMax: Integer); begin Create('', aIndex, aMin, aMax); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor EOutOfRangeException.Create(const aMsg: String; const aIndex, aMin, aMax: Integer); var s: String; begin fIndex := aIndex; fMin := aMin; fMax := aMax; s := Format('index (%d) out of range (%d:%d)', [fIndex, fMin, fMax]); if (aMsg <> '') then s := s + ': ' + aMsg; inherited Create(s); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlFilterBuilder/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TFilterBuilderImpl.Compose(const aIncludeAllSupported: String; const aIncludeAllFiles: String): string; var s: String; e: TFilterEntry; begin result := ''; if (aIncludeAllSupported>'') and (fFilters.Count > 0) then begin s:= ''; for e in fFilters do begin if s>'' then s += ';'; s += e.Filter; end; Result+= Format('%s|%s', [aIncludeAllSupported, s, s]); end; for e in fFilters do begin if Result>'' then Result += '|'; Result+= Format('%s|%s', [e.Descr, e.Filter]); end; if aIncludeAllFiles > '' then begin if Result>'' then Result += '|'; Result+= Format('%s|%s', [aIncludeAllFiles, '*.*']); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TFilterBuilderImpl.Add(aDescr, aMask: string; const aAppendFilterToDesc: boolean): IutlFilterBuilder; var e: TFilterEntry; begin result := Self; e:= TFilterEntry.Create; if aAppendFilterToDesc then e.Descr:= Format('%s (%s)', [aDescr, aMask]) else e.Descr:= aDescr; e.Filter:= aMask; fFilters.Add(e); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TFilterBuilderImpl.AddFilter(aFilter: string): IutlFilterBuilder; var c: integer; begin c:= Pos('|', aFilter); if c > 0 then result := (Self as IutlFilterBuilder).Add(Copy(aFilter, 1, c-1), Copy(aFilter, c+1, Maxint)) else result := (Self as IutlFilterBuilder).Add(aFilter, aFilter, false); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TFilterBuilderImpl.Create; begin inherited Create; fFilters:= TFilterList.Create(true); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor TFilterBuilderImpl.Destroy; begin FreeAndNil(fFilters); inherited Destroy; end; initialization {$IF DEFINED(WINDOWS)} PERF_FREQ := 0; QueryPerformanceFrequency(PERF_FREQ); {$ENDIF} end.