You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

602 lines
21 KiB

  1. unit uutlCommon;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, versionresource, versiontypes, typinfo
  6. {$IFDEF UNIX}, unixtype, pthreads {$ENDIF};
  7. type
  8. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. TutlInterfacedObject = class(TObject, IUnknown)
  10. protected
  11. fRefCount: longint;
  12. fAutoFree: Boolean;
  13. { implement methods of IUnknown }
  14. function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  15. function _AddRef: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual;
  16. function _Release: longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual;
  17. public
  18. property AutoFree: Boolean read fAutoFree write fAutoFree;
  19. property RefCount: LongInt read fRefCount;
  20. procedure AfterConstruction; override;
  21. constructor Create;
  22. public
  23. class function NewInstance: TObject; override;
  24. end;
  25. TutlInterfaceNoRefCount = TutlInterfacedObject;
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. TutlCSVList = class(TStringList)
  28. private
  29. fSkipDelims: boolean;
  30. function GetStrictDelText: string;
  31. procedure SetStrictDelText(const Value: string);
  32. public
  33. property StrictDelimitedText: string read GetStrictDelText write SetStrictDelText;
  34. // Skip repeated delims instead of reading empty lines?
  35. property SkipDelims: Boolean read fSkipDelims write fSkipDelims;
  36. end;
  37. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  38. TutlVersionInfo = class(TObject)
  39. private
  40. fVersionRes: TVersionResource;
  41. function GetFixedInfo: TVersionFixedInfo;
  42. function GetStringFileInfo: TVersionStringFileInfo;
  43. function GetVarFileInfo: TVersionVarFileInfo;
  44. public
  45. property FixedInfo: TVersionFixedInfo read GetFixedInfo;
  46. property StringFileInfo: TVersionStringFileInfo read GetStringFileInfo;
  47. property VarFileInfo: TVersionVarFileInfo read GetVarFileInfo;
  48. function Load(const aInstance: THandle): Boolean;
  49. constructor Create;
  50. destructor Destroy; override;
  51. end;
  52. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  53. EInternal = class(Exception);
  54. EOutOfRangeException = class(Exception)
  55. private
  56. fMin: Integer;
  57. fMax: Integer;
  58. fIndex: Integer;
  59. public
  60. property Min: Integer read fMin;
  61. property Max: Integer read fMax;
  62. property Index: Integer read fIndex;
  63. constructor Create(const aIndex, aMin, aMax: Integer);
  64. constructor Create(const aMsg: String; const aIndex, aMin, aMax: Integer);
  65. end;
  66. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  67. IutlFilterBuilder = interface['{BC5039C7-42E7-428F-A3E7-DDF7757B1907}']
  68. function Add(aDescr, aMask: string; const aAppendFilterToDesc: boolean = true): IutlFilterBuilder;
  69. function AddFilter(aFilter: string): IutlFilterBuilder;
  70. function Compose(const aIncludeAllSupported: String = ''; const aIncludeAllFiles: String = ''): string;
  71. end;
  72. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  73. function Supports (const aInstance: TObject; const aClass: TClass; out aObj): Boolean; overload;
  74. function GetTickCount64 (): QWord;
  75. function GetMicroTime (): QWord;
  76. function GetPlatformIdentitfier(): String;
  77. function utlRateLimited (const Reference: QWord; const Interval: QWord): boolean;
  78. function utlFinalizeObject (var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean): Boolean;
  79. function utlFilterBuilder (): IutlFilterBuilder;
  80. function utlBitCount (const aValue: DWord): Integer;
  81. implementation
  82. uses
  83. {$IFDEF WINDOWS}
  84. Windows,
  85. {$ELSE}
  86. Unix, BaseUnix,
  87. {$ENDIF}
  88. uutlGenerics;
  89. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  90. type
  91. TFilterBuilderImpl = class(
  92. TInterfacedObject,
  93. IutlFilterBuilder)
  94. private type
  95. TFilterEntry = class
  96. Descr,
  97. Filter: String;
  98. end;
  99. TFilterList = specialize TutlList<TFilterEntry>;
  100. private
  101. fFilters: TFilterList;
  102. public
  103. function Add (aDescr, aMask: string; const aAppendFilterToDesc: boolean): IutlFilterBuilder;
  104. function AddFilter(aFilter: string): IutlFilterBuilder;
  105. function Compose (const aIncludeAllSupported: String = ''; const aIncludeAllFiles: String = ''): string;
  106. constructor Create;
  107. destructor Destroy; override;
  108. end;
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. //Helper Methods////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. function Supports(const aInstance: TObject; const aClass: TClass; out aObj): Boolean;
  113. begin
  114. result := Assigned(aInstance) and aInstance.InheritsFrom(aClass);
  115. if result
  116. then TObject(aObj) := aInstance
  117. else TObject(aObj) := nil;
  118. end;
  119. {$IF DEFINED(WINDOWS)}
  120. var
  121. PERF_FREQ: Int64;
  122. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  123. function GetTickCount64: QWord;
  124. begin
  125. // GetTickCount64 is better, but we need to check the Windows version to use it
  126. Result := Windows.GetTickCount();
  127. end;
  128. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  129. function GetMicroTime: QWord;
  130. var
  131. pc: Int64;
  132. begin
  133. pc := 0;
  134. QueryPerformanceCounter(pc);
  135. result := (pc * 1000*1000) div PERF_FREQ;
  136. end;
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. {$ELSEIF DEFINED(UNIX)}
  139. function GetTickCount64: QWord;
  140. var
  141. tp: TTimeVal;
  142. begin
  143. fpgettimeofday(@tp, nil);
  144. Result := (Int64(tp.tv_sec) * 1000) + (tp.tv_usec div 1000);
  145. end;
  146. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147. function GetMicroTime: QWord;
  148. var
  149. tp: TTimeVal;
  150. begin
  151. fpgettimeofday(@tp, nil);
  152. Result := (Int64(tp.tv_sec) * 1000*1000) + tp.tv_usec;
  153. end;
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  155. {$ELSE}
  156. function GetTickCount64: QWord;
  157. begin
  158. Result := Trunc(Now * 24 * 60 * 60 * 1000);
  159. end;
  160. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. function GetMicroTime: QWord;
  162. begin
  163. Result := Trunc(Now * 24 * 60 * 60 * 1000*1000);
  164. end;
  165. {$ENDIF}
  166. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  167. function GetPlatformIdentitfier: String;
  168. {$IFDEF WINDOWS}
  169. function GetWindowsVersionStr(const aDefault: String): string;
  170. var
  171. osv: TOSVERSIONINFO;
  172. ver: cardinal;
  173. begin
  174. result := aDefault;
  175. osv.dwOSVersionInfoSize := SizeOf(osv);
  176. if GetVersionEx(osv) then begin
  177. ver := MAKELONG(osv.dwMinorVersion, osv.dwMajorVersion);
  178. // positive overflow: if system is newer, always detect as newest we knew instead of failing
  179. if ver >= $000A0000 then
  180. result := '10'
  181. else if ver >= $00060003 then
  182. result := '8_1'
  183. else if ver >= $00060002 then
  184. result := '8'
  185. else if ver >= $00060001 then
  186. result := '7'
  187. else if ver >= $00060000 then
  188. result := 'Vista'
  189. else if ver >= $00050002 then
  190. result := '2003'
  191. else if ver >= $00050001 then
  192. result := 'XP'
  193. else if ver >= $00050000 then
  194. result := '2000'
  195. else if ver >= $00040000 then
  196. result := 'NT4';
  197. // ignore NT3, hmkay?;
  198. end;
  199. end;
  200. {$ENDIF}
  201. var
  202. os,ver,arch: string;
  203. begin
  204. result := '';
  205. os := '';
  206. ver := 'generic';
  207. arch := '';
  208. {$IF DEFINED(WINDOWS)}
  209. os := 'mswin';
  210. ver := GetWindowsVersionStr(ver);
  211. {$ELSEIF DEFINED(LINUX)}
  212. os := 'linux';
  213. {$Warning System Version String missing!}
  214. {$ENDIF}
  215. {$IF DEFINED(CPUX86)}
  216. arch := 'x86';
  217. {$ELSEIF DEFINED(cpux86_64)}
  218. arch := 'x64';
  219. {$ELSE}
  220. {$ERROR Unknown Architecture!}
  221. {$ENDIF}
  222. result := Format('%s-%s-%s', [os, ver, arch]);
  223. end;
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. function utlRateLimited(const Reference: QWord; const Interval: QWord): boolean;
  226. begin
  227. Result := GetMicroTime - Reference > Interval;
  228. end;
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  230. function utlFinalizeObject(var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean): Boolean;
  231. var
  232. o: TObject;
  233. begin
  234. result := true;
  235. case aTypeInfo^.Kind of
  236. tkClass: begin
  237. if (aFreeObject) then begin
  238. o := TObject(obj);
  239. Pointer(obj) := nil;
  240. if Assigned(o) then
  241. o.Free;
  242. end;
  243. end;
  244. tkInterface: begin
  245. IUnknown(obj) := nil;
  246. end;
  247. tkAString: begin
  248. AnsiString(Obj) := '';
  249. end;
  250. tkUString: begin
  251. UnicodeString(Obj) := '';
  252. end;
  253. tkString: begin
  254. String(Obj) := '';
  255. end;
  256. else
  257. result := false;
  258. end;
  259. end;
  260. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  261. function utlFilterBuilder: IutlFilterBuilder;
  262. begin
  263. result := TFilterBuilderImpl.Create;
  264. end;
  265. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  266. function utlBitCount(const aValue: DWord): Integer;
  267. var
  268. t: DWord;
  269. begin
  270. t := aValue - ((aValue shr 1) and &33333333333)
  271. - ((aValue shr 2) and &11111111111);
  272. result := ((t + (t shr 3)) and &30707070707) mod 63;
  273. end;
  274. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  275. //TutlInterfacedObject///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  276. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  277. function TutlInterfacedObject.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  278. begin
  279. if getinterface(iid,obj) then
  280. result:=S_OK
  281. else
  282. result:=longint(E_NOINTERFACE);
  283. end;
  284. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  285. function TutlInterfacedObject._AddRef: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  286. begin
  287. _AddRef := interlockedincrement(fRefCount);
  288. end;
  289. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  290. function TutlInterfacedObject._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  291. begin
  292. _Release := InterLockedDecrement(fRefCount);
  293. if (_Release = 0) and fAutoFree then
  294. Destroy;
  295. end;
  296. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  297. procedure TutlInterfacedObject.AfterConstruction;
  298. begin
  299. inherited AfterConstruction;
  300. InterLockedDecrement(fRefCount);
  301. end;
  302. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  303. constructor TutlInterfacedObject.Create;
  304. begin
  305. inherited Create;
  306. fAutoFree := false;
  307. end;
  308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  309. class function TutlInterfacedObject.NewInstance: TObject;
  310. begin
  311. result := inherited NewInstance;
  312. if Assigned(result) then
  313. TutlInterfacedObject(result).fRefCount := 1;
  314. end;
  315. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  316. //TutlCSVList///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  317. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  318. function TutlCSVList.GetStrictDelText: string;
  319. var
  320. S: string;
  321. I, J, Cnt: Integer;
  322. q: boolean;
  323. LDelimiters: TSysCharSet;
  324. begin
  325. Cnt := GetCount;
  326. if (Cnt = 1) and (Get(0) = '') then
  327. Result := QuoteChar + QuoteChar
  328. else
  329. begin
  330. Result := '';
  331. LDelimiters := [QuoteChar, Delimiter];
  332. for I := 0 to Cnt - 1 do
  333. begin
  334. S := Get(I);
  335. q:= false;
  336. if S>'' then begin
  337. for J:= 1 to length(S) do
  338. if S[J] in LDelimiters then begin
  339. q:= true;
  340. break;
  341. end;
  342. if q then S := AnsiQuotedStr(S, QuoteChar);
  343. end else
  344. S := AnsiQuotedStr(S, QuoteChar);
  345. Result := Result + S + Delimiter;
  346. end;
  347. System.Delete(Result, Length(Result), 1);
  348. end;
  349. end;
  350. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  351. procedure TutlCSVList.SetStrictDelText(const Value: string);
  352. var
  353. S: String;
  354. P, P1: PChar;
  355. begin
  356. BeginUpdate;
  357. try
  358. Clear;
  359. P:= PChar(Value);
  360. if fSkipDelims then begin
  361. while (P^<>#0) and (P^=Delimiter) do begin
  362. {$IFDEF LINUX}inc(P){$ELSE}P:= CharNext(P){$ENDIF};
  363. end;
  364. end;
  365. while (P^<>#0) do begin
  366. if (P^ = QuoteChar) then begin
  367. S:= AnsiExtractQuotedStr(P, QuoteChar);
  368. end else begin
  369. P1:= P;
  370. while (P^<>#0) and (P^<>Delimiter) do begin
  371. {$IFDEF LINUX}inc(P){$ELSE}P:= CharNext(P){$ENDIF};
  372. end;
  373. SetString(S, P1, P - P1);
  374. end;
  375. Add(S);
  376. while (P^<>#0) and (P^<>Delimiter) do begin
  377. {$IFDEF LINUX}inc(P){$ELSE}P:= CharNext(P){$ENDIF};
  378. end;
  379. if (P^<>#0) then
  380. {$IFDEF LINUX}inc(P){$ELSE}P:= CharNext(P){$ENDIF};
  381. if fSkipDelims then begin
  382. while (P^<>#0) and (P^=Delimiter) do begin
  383. {$IFDEF LINUX}inc(P){$ELSE}P:= CharNext(P){$ENDIF};
  384. end;
  385. end;
  386. end;
  387. finally
  388. EndUpdate;
  389. end;
  390. end;
  391. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  392. //TutlVersionInfo///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  393. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  394. function TutlVersionInfo.GetFixedInfo: TVersionFixedInfo;
  395. begin
  396. result := fVersionRes.FixedInfo;
  397. end;
  398. function TutlVersionInfo.GetStringFileInfo: TVersionStringFileInfo;
  399. begin
  400. result := fVersionRes.StringFileInfo;
  401. end;
  402. function TutlVersionInfo.GetVarFileInfo: TVersionVarFileInfo;
  403. begin
  404. result := fVersionRes.VarFileInfo;
  405. end;
  406. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  407. function TutlVersionInfo.Load(const aInstance: THandle): Boolean;
  408. var
  409. Stream: TResourceStream;
  410. begin
  411. result := false;
  412. if (FindResource(aInstance, PChar(PtrInt(1)), PChar(RT_VERSION)) = 0) then
  413. exit;
  414. Stream := TResourceStream.CreateFromID(aInstance, 1, PChar(RT_VERSION));
  415. try
  416. fVersionRes.SetCustomRawDataStream(Stream);
  417. fVersionRes.FixedInfo;// access some property to force load from the stream
  418. fVersionRes.SetCustomRawDataStream(nil);
  419. finally
  420. Stream.Free;
  421. end;
  422. result := true;
  423. end;
  424. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  425. constructor TutlVersionInfo.Create;
  426. begin
  427. inherited Create;
  428. fVersionRes := TVersionResource.Create;
  429. end;
  430. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  431. destructor TutlVersionInfo.Destroy;
  432. begin
  433. FreeAndNil(fVersionRes);
  434. inherited Destroy;
  435. end;
  436. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  437. //EOutOfRange///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  438. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  439. constructor EOutOfRangeException.Create(const aIndex, aMin, aMax: Integer);
  440. begin
  441. Create('', aIndex, aMin, aMax);
  442. end;
  443. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  444. constructor EOutOfRangeException.Create(const aMsg: String; const aIndex, aMin, aMax: Integer);
  445. var
  446. s: String;
  447. begin
  448. fIndex := aIndex;
  449. fMin := aMin;
  450. fMax := aMax;
  451. s := Format('index (%d) out of range (%d:%d)', [fIndex, fMin, fMax]);
  452. if (aMsg <> '') then
  453. s := s + ': ' + aMsg;
  454. inherited Create(s);
  455. end;
  456. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  457. //TutlFilterBuilder///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  458. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  459. function TFilterBuilderImpl.Compose(const aIncludeAllSupported: String; const aIncludeAllFiles: String): string;
  460. var
  461. s: String;
  462. e: TFilterEntry;
  463. begin
  464. result := '';
  465. if (aIncludeAllSupported>'') and (fFilters.Count > 0) then begin
  466. s:= '';
  467. for e in fFilters do begin
  468. if s>'' then
  469. s += ';';
  470. s += e.Filter;
  471. end;
  472. Result+= Format('%s|%s', [aIncludeAllSupported, s, s]);
  473. end;
  474. for e in fFilters do begin
  475. if Result>'' then
  476. Result += '|';
  477. Result+= Format('%s|%s', [e.Descr, e.Filter]);
  478. end;
  479. if aIncludeAllFiles > '' then begin
  480. if Result>'' then
  481. Result += '|';
  482. Result+= Format('%s|%s', [aIncludeAllFiles, '*.*']);
  483. end;
  484. end;
  485. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  486. function TFilterBuilderImpl.Add(aDescr, aMask: string; const aAppendFilterToDesc: boolean): IutlFilterBuilder;
  487. var
  488. e: TFilterEntry;
  489. begin
  490. result := Self;
  491. e:= TFilterEntry.Create;
  492. if aAppendFilterToDesc then
  493. e.Descr:= Format('%s (%s)', [aDescr, aMask])
  494. else
  495. e.Descr:= aDescr;
  496. e.Filter:= aMask;
  497. fFilters.Add(e);
  498. end;
  499. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  500. function TFilterBuilderImpl.AddFilter(aFilter: string): IutlFilterBuilder;
  501. var
  502. c: integer;
  503. begin
  504. c:= Pos('|', aFilter);
  505. if c > 0 then
  506. result := (Self as IutlFilterBuilder).Add(Copy(aFilter, 1, c-1), Copy(aFilter, c+1, Maxint))
  507. else
  508. result := (Self as IutlFilterBuilder).Add(aFilter, aFilter, false);
  509. end;
  510. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  511. constructor TFilterBuilderImpl.Create;
  512. begin
  513. inherited Create;
  514. fFilters:= TFilterList.Create(true);
  515. end;
  516. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  517. destructor TFilterBuilderImpl.Destroy;
  518. begin
  519. FreeAndNil(fFilters);
  520. inherited Destroy;
  521. end;
  522. initialization
  523. {$IF DEFINED(WINDOWS)}
  524. PERF_FREQ := 0;
  525. QueryPerformanceFrequency(PERF_FREQ);
  526. {$ENDIF}
  527. end.