Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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