選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

571 行
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. function utlFinalizeObject (var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean): 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. function utlFinalizeObject(var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean): Boolean;
  226. var
  227. o: TObject;
  228. begin
  229. result := true;
  230. case aTypeInfo^.Kind of
  231. tkClass: begin
  232. if (aFreeObject) then begin
  233. o := TObject(obj);
  234. Pointer(obj) := nil;
  235. if Assigned(o) then
  236. o.Free;
  237. end;
  238. end;
  239. tkInterface: begin
  240. IUnknown(obj) := nil;
  241. end;
  242. tkAString: begin
  243. AnsiString(Obj) := '';
  244. end;
  245. tkUString: begin
  246. UnicodeString(Obj) := '';
  247. end;
  248. tkString: begin
  249. String(Obj) := '';
  250. end;
  251. else
  252. result := false;
  253. end;
  254. end;
  255. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  256. function utlFilterBuilder: IutlFilterBuilder;
  257. begin
  258. result := TFilterBuilderImpl.Create;
  259. end;
  260. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  261. //TutlInterfacedObject///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  262. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  263. function TutlInterfacedObject.QueryInterface(constref iid: tguid; out obj): longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  264. begin
  265. if getinterface(iid,obj) then
  266. result:=S_OK
  267. else
  268. result:=longint(E_NOINTERFACE);
  269. end;
  270. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  271. function TutlInterfacedObject._AddRef: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  272. begin
  273. result := InterLockedIncrement(fRefCount);
  274. end;
  275. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  276. function TutlInterfacedObject._Release: longint; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
  277. begin
  278. result := InterLockedDecrement(fRefCount);
  279. if (result = 0) and fAutoFree then
  280. Destroy;
  281. end;
  282. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  283. constructor TutlInterfacedObject.Create;
  284. begin
  285. inherited Create;
  286. fAutoFree := false;
  287. fRefCount := 0;
  288. end;
  289. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  290. //TutlCSVList///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  291. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  292. function TutlCSVList.GetStrictDelText: string;
  293. var
  294. S: string;
  295. I, J, Cnt: Integer;
  296. q: boolean;
  297. LDelimiters: TSysCharSet;
  298. begin
  299. Cnt := GetCount;
  300. if (Cnt = 1) and (Get(0) = '') then
  301. Result := QuoteChar + QuoteChar
  302. else
  303. begin
  304. Result := '';
  305. LDelimiters := [QuoteChar, Delimiter];
  306. for I := 0 to Cnt - 1 do
  307. begin
  308. S := Get(I);
  309. q:= false;
  310. if S>'' then begin
  311. for J:= 1 to length(S) do
  312. if S[J] in LDelimiters then begin
  313. q:= true;
  314. break;
  315. end;
  316. if q then S := AnsiQuotedStr(S, QuoteChar);
  317. end else
  318. S := AnsiQuotedStr(S, QuoteChar);
  319. Result := Result + S + Delimiter;
  320. end;
  321. System.Delete(Result, Length(Result), 1);
  322. end;
  323. end;
  324. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  325. procedure TutlCSVList.SetStrictDelText(const Value: string);
  326. var
  327. S: String;
  328. P, P1: PChar;
  329. begin
  330. BeginUpdate;
  331. try
  332. Clear;
  333. P:= PChar(Value);
  334. if fSkipDelims then begin
  335. while (P^<>#0) and (P^=Delimiter) do begin
  336. P:= CharNext(P);
  337. end;
  338. end;
  339. while (P^<>#0) do begin
  340. if (P^ = QuoteChar) then begin
  341. S:= AnsiExtractQuotedStr(P, QuoteChar);
  342. end else begin
  343. P1:= P;
  344. while (P^<>#0) and (P^<>Delimiter) do begin
  345. P:= CharNext(P);
  346. end;
  347. SetString(S, P1, P - P1);
  348. end;
  349. Add(S);
  350. while (P^<>#0) and (P^<>Delimiter) do begin
  351. P:= CharNext(P);
  352. end;
  353. if (P^<>#0) then
  354. P:= CharNext(P);
  355. if fSkipDelims then begin
  356. while (P^<>#0) and (P^=Delimiter) do begin
  357. P:= CharNext(P);
  358. end;
  359. end;
  360. end;
  361. finally
  362. EndUpdate;
  363. end;
  364. end;
  365. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  366. //TutlVersionInfo///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  367. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  368. function TutlVersionInfo.GetFixedInfo: TVersionFixedInfo;
  369. begin
  370. result := fVersionRes.FixedInfo;
  371. end;
  372. function TutlVersionInfo.GetStringFileInfo: TVersionStringFileInfo;
  373. begin
  374. result := fVersionRes.StringFileInfo;
  375. end;
  376. function TutlVersionInfo.GetVarFileInfo: TVersionVarFileInfo;
  377. begin
  378. result := fVersionRes.VarFileInfo;
  379. end;
  380. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  381. function TutlVersionInfo.Load(const aInstance: THandle): Boolean;
  382. var
  383. Stream: TResourceStream;
  384. begin
  385. result := false;
  386. if (FindResource(aInstance, PChar(PtrInt(1)), PChar(RT_VERSION)) = 0) then
  387. exit;
  388. Stream := TResourceStream.CreateFromID(aInstance, 1, PChar(RT_VERSION));
  389. try
  390. fVersionRes.SetCustomRawDataStream(Stream);
  391. fVersionRes.FixedInfo;// access some property to force load from the stream
  392. fVersionRes.SetCustomRawDataStream(nil);
  393. finally
  394. Stream.Free;
  395. end;
  396. result := true;
  397. end;
  398. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  399. constructor TutlVersionInfo.Create;
  400. begin
  401. inherited Create;
  402. fVersionRes := TVersionResource.Create;
  403. end;
  404. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  405. destructor TutlVersionInfo.Destroy;
  406. begin
  407. FreeAndNil(fVersionRes);
  408. inherited Destroy;
  409. end;
  410. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  411. //EOutOfRange///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  412. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  413. constructor EOutOfRangeException.Create(const aIndex, aMin, aMax: Integer);
  414. begin
  415. Create('', aIndex, aMin, aMax);
  416. end;
  417. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  418. constructor EOutOfRangeException.Create(const aMsg: String; const aIndex, aMin, aMax: Integer);
  419. var
  420. s: String;
  421. begin
  422. fIndex := aIndex;
  423. fMin := aMin;
  424. fMax := aMax;
  425. s := Format('index (%d) out of range (%d:%d)', [fIndex, fMin, fMax]);
  426. if (aMsg <> '') then
  427. s := s + ': ' + aMsg;
  428. inherited Create(s);
  429. end;
  430. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  431. //TutlFilterBuilder///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  432. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  433. function TFilterBuilderImpl.Compose(const aIncludeAllSupported: String; const aIncludeAllFiles: String): string;
  434. var
  435. s: String;
  436. e: TFilterEntry;
  437. begin
  438. result := '';
  439. if (aIncludeAllSupported>'') and (fFilters.Count > 0) then begin
  440. s:= '';
  441. for e in fFilters do begin
  442. if s>'' then
  443. s += ';';
  444. s += e.Filter;
  445. end;
  446. Result+= Format('%s|%s', [aIncludeAllSupported, s, s]);
  447. end;
  448. for e in fFilters do begin
  449. if Result>'' then
  450. Result += '|';
  451. Result+= Format('%s|%s', [e.Descr, e.Filter]);
  452. end;
  453. if aIncludeAllFiles > '' then begin
  454. if Result>'' then
  455. Result += '|';
  456. Result+= Format('%s|%s', [aIncludeAllFiles, '*.*']);
  457. end;
  458. end;
  459. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  460. function TFilterBuilderImpl.Add(aDescr, aMask: string; const aAppendFilterToDesc: boolean): IutlFilterBuilder;
  461. var
  462. e: TFilterEntry;
  463. begin
  464. result := Self;
  465. e:= TFilterEntry.Create;
  466. if aAppendFilterToDesc then
  467. e.Descr:= Format('%s (%s)', [aDescr, aMask])
  468. else
  469. e.Descr:= aDescr;
  470. e.Filter:= aMask;
  471. fFilters.Add(e);
  472. end;
  473. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  474. function TFilterBuilderImpl.AddFilter(aFilter: string): IutlFilterBuilder;
  475. var
  476. c: integer;
  477. begin
  478. c:= Pos('|', aFilter);
  479. if c > 0 then
  480. result := (Self as IutlFilterBuilder).Add(Copy(aFilter, 1, c-1), Copy(aFilter, c+1, Maxint))
  481. else
  482. result := (Self as IutlFilterBuilder).Add(aFilter, aFilter, false);
  483. end;
  484. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  485. constructor TFilterBuilderImpl.Create;
  486. begin
  487. inherited Create;
  488. fFilters:= TFilterList.Create(true);
  489. end;
  490. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  491. destructor TFilterBuilderImpl.Destroy;
  492. begin
  493. FreeAndNil(fFilters);
  494. inherited Destroy;
  495. end;
  496. initialization
  497. {$IF DEFINED(WINDOWS)}
  498. PERF_FREQ := 0;
  499. QueryPerformanceFrequency(PERF_FREQ);
  500. {$ENDIF}
  501. end.