Quick.Config.Provider.Registry.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. { ***************************************************************************
  2. Copyright (c) 2015-2018 Kike Pérez
  3. Unit : Quick.Config.Provider.Registry
  4. Description : Save config to Windows Registry
  5. Author : Kike Pérez
  6. Version : 1.2
  7. Created : 21/10/2017
  8. Modified : 07/04/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Config.Provider.Registry;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. Windows,
  27. SysUtils,
  28. Registry,
  29. {$IFDEF DELPHIRX102_UP}
  30. System.Json,
  31. System.JSON.Types,
  32. System.JSON.Serializers,
  33. {$ELSE}
  34. {$IFDEF FPC}
  35. fpjson, jsonparser,
  36. fpjsonrtti,
  37. {$ELSE}
  38. Rest.Json.Types,
  39. System.JSON,
  40. Rest.Json,
  41. {$ENDIF}
  42. {$ENDIF}
  43. Quick.Commons,
  44. Quick.Config;
  45. type
  46. TJSONValue = TJSONData;
  47. TAppConfigRegistryProvider<T : class> = class(TAppConfigProviderBase<T>)
  48. private
  49. fRootKey : HKEY;
  50. fMainKey : string;
  51. fRegConfig : TRegistry;
  52. function JsonToRegistry(const StrJson : string) : Boolean;
  53. function RegistryToJson(out StrJson : string) : Boolean;
  54. class function IsSimpleJsonValue(v: TJSONValue): Boolean;
  55. function IsRegKeyObject(const cCurrentKey : string = '') : Boolean;
  56. function IsRegKeyArray(const cCurrentKey : string = '') : Boolean;
  57. function ProcessPairRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  58. function ProcessElementRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  59. procedure ProcessPairWrite(const cCurrentKey: string; obj: TJSONObject; aIndex: integer);
  60. procedure ProcessElementWrite(const cCurrentKey: string; arr: TJSONArray; aIndex, aMax : integer);
  61. function AddRegKey(const cCurrentKey, NewKey : string) : Boolean;
  62. function ReadRegValue(const cCurrentKey, cName : string) : TJSONValue;
  63. procedure AddRegValue(const cCurrentKey, cName : string; cValue : TJSONValue);
  64. public
  65. constructor Create(var cConfig : T); override;
  66. destructor Destroy; override;
  67. property HRoot : HKEY read fRootKey write fRootKey;
  68. property MainKey : string read fMainKey write fMainKey;
  69. procedure Load(var cConfig : T); override;
  70. procedure Save(var cConfig : T); override;
  71. end;
  72. EAppConfig = Exception;
  73. implementation
  74. { TAppConfigRegistryProvider }
  75. constructor TAppConfigRegistryProvider<T>.Create(var cConfig : T);
  76. begin
  77. inherited Create(cConfig);
  78. fRootKey := HKEY_CURRENT_USER;
  79. fMainKey := '_AppConfig';
  80. fRegConfig := TRegistry.Create(KEY_READ or KEY_WRITE);
  81. end;
  82. destructor TAppConfigRegistryProvider<T>.Destroy;
  83. begin
  84. if Assigned(fRegConfig) then fRegConfig.Free;
  85. inherited;
  86. end;
  87. procedure TAppConfigRegistryProvider<T>.Load(var cConfig : T);
  88. var
  89. {$IFDEF DELPHIRX102_UP}
  90. Serializer: TJsonSerializer;
  91. {$ENDIF}
  92. {$IFDEF FPC}
  93. streamer : TJSONDeStreamer;
  94. {$ENDIF}
  95. json : string;
  96. newObj : T;
  97. begin
  98. fRegConfig.Access := KEY_READ;
  99. fRegConfig.RootKey := fRootKey;
  100. if (not fRegConfig.KeyExists('\Software\' + fMainKey))
  101. and (CreateIfNotExists) then
  102. begin
  103. Save(cConfig);
  104. end;
  105. RegistryToJson(json);
  106. {$IFDEF DELPHIRX102_UP}
  107. Serializer := TJsonSerializer.Create;
  108. try
  109. Serializer.Formatting := TJsonFormatting.Indented;
  110. if TAppConfig(cConfig).DateTimeZone = TDateTimeZone.tzLocal then
  111. begin
  112. Serializer.DateTimeZoneHandling := TJsonDateTimeZoneHandling.Local;
  113. Serializer.DateFormatHandling := TJsonDateFormatHandling.FormatSettings;
  114. end
  115. else Serializer.DateTimeZoneHandling := TJsonDateTimeZoneHandling.Utc;
  116. newObj := Serializer.Deserialize<T>(json);
  117. finally
  118. Serializer.Free;
  119. end;
  120. {$ELSE}
  121. {$IFDEF FPC}
  122. streamer := TJSONDeStreamer.Create(nil);
  123. try
  124. //Streamer.Options := Streamer. .Options + [jsoDateTimeAsString ,jsoUseFormatString];
  125. Streamer.DateTimeFormat := 'yyyy-mm-dd"T"hh:mm:ss.zz';
  126. Streamer.JsonToObject(json,NewObj);
  127. finally
  128. Streamer.Free;
  129. end;
  130. {$ELSE}
  131. TJson.JsonToObject(newObj,TJSONObject(TJSONObject.ParseJSONValue(json)));
  132. {$ENDIF}
  133. {$ENDIF}
  134. if Assigned(cConfig) then cConfig.Free;
  135. cConfig := newObj;
  136. end;
  137. procedure TAppConfigRegistryProvider<T>.Save(var cConfig : T);
  138. begin
  139. //create object with rtti if nil
  140. if not Assigned(cConfig) then cConfig := InitObject;
  141. JsonToRegistry(TAppConfig(cConfig).ToJSON);
  142. end;
  143. function TAppConfigRegistryProvider<T>.JsonToRegistry(const StrJson : string) : Boolean;
  144. var
  145. jValue : TJSONValue;
  146. aCount : Integer;
  147. i : Integer;
  148. aCurrentKey : string;
  149. begin
  150. Result := False;
  151. if fMainKey = '' then raise EAppConfig.Create('MainKey not defined!');
  152. fRegConfig.Access := KEY_READ or KEY_WRITE;
  153. fRegConfig.RootKey := fRootKey;
  154. aCurrentKey := '\Software\' + fMainKey;
  155. if fRegConfig.KeyExists(aCurrentKey) then
  156. begin
  157. try
  158. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  159. fRegConfig.MoveKey(aCurrentKey,aCurrentKey + '_bak',True);
  160. except
  161. raise EAppConfig.Create('Can''t write Config Registry');
  162. end;
  163. end;
  164. try
  165. if not AddRegKey('\Software',fMainKey) then
  166. begin
  167. raise EAppConfig.Create('Can''t create key');
  168. end;
  169. jValue := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONValue;
  170. try
  171. if IsSimpleJsonValue(jValue) then
  172. begin
  173. AddRegValue(aCurrentKey,TJSONPair(jValue).JsonString.ToString.DeQuotedString('"'),TJSONPair(jValue).JsonValue);
  174. end
  175. else if jValue is TJSONObject then
  176. begin
  177. aCount := TJSONObject(jValue).Count;
  178. for i := 0 to aCount - 1 do
  179. ProcessPairWrite(aCurrentKey,TJSONObject(jValue),i);
  180. end
  181. else if jValue is TJSONArray then
  182. begin
  183. aCount := TJSONArray(jValue).Count;
  184. for i := 0 to aCount - 1 do
  185. ProcessElementWrite(aCurrentKey,TJSONArray(jValue),i,aCount);
  186. end
  187. else
  188. raise EAppConfig.Create('Error Saving config to Registry');
  189. Result := True;
  190. finally
  191. jValue.Free;
  192. end;
  193. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  194. except
  195. fRegConfig.DeleteKey(aCurrentKey);
  196. fRegConfig.MoveKey(aCurrentKey+'_bak',aCurrentKey,True);
  197. end;
  198. end;
  199. function TAppConfigRegistryProvider<T>.RegistryToJson(out StrJson : string) : Boolean;
  200. var
  201. jValue : TJSONValue;
  202. jPair : TJSONPair;
  203. jArray : TJSONArray;
  204. a, b : string;
  205. aCount : Integer;
  206. i : Integer;
  207. aName : string;
  208. aValue : TJSONValue;
  209. aCurrentKey : string;
  210. newObj : TJSONObject;
  211. RegKeyList : TStringList;
  212. RegValueList : TStringList;
  213. RegKey : string;
  214. RegValue : string;
  215. RegKeyInfo : TRegKeyInfo;
  216. begin
  217. Result := False;
  218. //check if exists root key
  219. fRegConfig.Access := KEY_READ;
  220. fRegConfig.RootKey := fRootKey;
  221. if fRegConfig.KeyExists('\Software\' + fMainKey) then
  222. begin
  223. fRegConfig.OpenKeyReadOnly('\Software\' + fMainKey);
  224. aCurrentKey := '\Software\' + fMainKey;
  225. end
  226. else raise EAppConfig.Create('Can''t read key');
  227. newObj := TJSONObject.Create;
  228. try
  229. //read root values
  230. RegValueList := TStringList.Create;
  231. try
  232. fRegConfig.GetValueNames(RegValueList);
  233. for RegValue in RegValueList do
  234. begin
  235. newObj.AddPair(RegValue,ReadRegValue(aCurrentKey,RegValue));
  236. end;
  237. finally
  238. RegValueList.Free;
  239. end;
  240. //read root keys
  241. RegKeyList := TStringList.Create;
  242. try
  243. fRegConfig.GetKeyNames(RegKeyList);
  244. for RegKey in RegKeyList do
  245. begin
  246. fRegConfig.OpenKeyReadOnly(aCurrentKey + '\' + RegKey);
  247. if IsRegKeyObject then
  248. begin
  249. jValue := ProcessPairRead(aCurrentKey + '\' + RegKey,Regkey,i);
  250. newObj.AddPair(RegKey,jValue);
  251. end
  252. else if IsRegKeyArray then
  253. begin
  254. jValue := ProcessElementRead(aCurrentKey + '\' + RegKey,Regkey,i);
  255. newObj.AddPair(RegKey,jValue);
  256. end
  257. else raise EAppConfig.Create('Unknow value reading Config Registry');
  258. end;
  259. finally
  260. RegKeyList.Free;
  261. end;
  262. StrJson := newObj.ToJSON;
  263. finally
  264. newObj.Free;
  265. end;
  266. end;
  267. function TAppConfigRegistryProvider<T>.IsRegKeyObject(const cCurrentKey : string = '') : Boolean;
  268. begin
  269. Result := not IsRegKeyArray(cCurrentKey);
  270. end;
  271. function TAppConfigRegistryProvider<T>.IsRegKeyArray(const cCurrentKey : string = '') : Boolean;
  272. var
  273. RegValue : string;
  274. RegValueList : TStrings;
  275. RegKey : string;
  276. RegKeyList : TStrings;
  277. n : Integer;
  278. begin
  279. Result := False;
  280. if cCurrentKey <> '' then fRegConfig.OpenKeyReadOnly(cCurrentKey);
  281. //check if exists RegKey numeric (indicates is a Array)
  282. RegKeyList := TStringList.Create;
  283. try
  284. fRegConfig.GetKeyNames(RegKeyList);
  285. for RegKey in RegKeyList do
  286. if TryStrToInt(RegKey,n) then
  287. begin
  288. Result := True;
  289. Break;
  290. end;
  291. finally
  292. RegKeyList.Free;
  293. end;
  294. //check if exists RegValue numeric (indicates is a Array)
  295. RegValueList := TStringList.Create;
  296. try
  297. fRegConfig.GetValueNames(RegValueList);
  298. for RegValue in RegValueList do
  299. if TryStrToInt(RegValue,n) then
  300. begin
  301. Result := True;
  302. Break;
  303. end;
  304. finally
  305. RegValueList.Free;
  306. end;
  307. end;
  308. class function TAppConfigRegistryProvider<T>.IsSimpleJsonValue(v: TJSONValue): Boolean;
  309. begin
  310. Result := (v is TJSONNumber)
  311. or (v is TJSONString)
  312. or (v is TJSONTrue)
  313. or (v is TJSONFalse)
  314. or (v is TJSONNull);
  315. end;
  316. function TAppConfigRegistryProvider<T>.ReadRegValue(const cCurrentKey, cName : string) : TJSONValue;
  317. var
  318. aValue : string;
  319. RegInfo : TRegDataInfo;
  320. begin
  321. if fRegConfig.OpenKeyReadOnly(cCurrentKey) then
  322. begin
  323. if fRegConfig.GetDataInfo(cName,RegInfo) then
  324. case RegInfo.RegData of
  325. rdInteger : Result := TJSONNumber.Create(fRegConfig.ReadInteger(cName));
  326. rdString :
  327. begin
  328. aValue := fRegConfig.ReadString(cName);
  329. if aValue.ToLower = 'true' then Result := TJSONBool.Create(True)
  330. else if aValue.ToLower = 'false' then Result := TJSONBool.Create(False)
  331. else Result := TJSONString.Create(aValue);
  332. end;
  333. else Result := TJSONNull.Create;
  334. end;
  335. end;
  336. end;
  337. function TAppConfigRegistryProvider<T>.AddRegKey(const cCurrentKey, NewKey : string) : Boolean;
  338. begin
  339. Result := fRegConfig.CreateKey(Format('%s\%s',[cCurrentKey,NewKey]));
  340. end;
  341. procedure TAppConfigRegistryProvider<T>.AddRegValue(const cCurrentKey, cName : string; cValue : TJSONValue);
  342. var
  343. aName : string;
  344. aValue : string;
  345. begin
  346. aName := cName.DeQuotedString('"');
  347. aValue := cValue.ToString.DeQuotedString('"');
  348. fRegConfig.OpenKey(cCurrentKey,True);
  349. if cValue is TJSONNumber then fRegConfig.WriteInteger(aName,StrToInt64(aValue))
  350. else if cValue is TJSONString then fRegConfig.WriteString(aName,aValue)
  351. else if cValue is TJSONBool then fRegConfig.WriteString(aName,aValue);
  352. //else if cValue is TJSONNull then fRegConfig.WriteString(aName,'');
  353. end;
  354. function TAppConfigRegistryProvider<T>.ProcessPairRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  355. var
  356. i : Integer;
  357. jValue : TJSONValue;
  358. RegValue : string;
  359. RegValueList : TStrings;
  360. RegKey : string;
  361. RegKeyList : TStrings;
  362. newObj : TJSONObject;
  363. begin
  364. newObj := TJSONObject.Create;
  365. //read root values
  366. RegValueList := TStringList.Create;
  367. try
  368. fRegConfig.GetValueNames(RegValueList);
  369. for RegValue in RegValueList do
  370. begin
  371. newObj.AddPair(RegValue,ReadRegValue(cCurrentKey,RegValue));
  372. end;
  373. finally
  374. RegValueList.Free;
  375. end;
  376. //read root keys
  377. RegKeyList := TStringList.Create;
  378. try
  379. fRegConfig.GetKeyNames(RegKeyList);
  380. for RegKey in RegKeyList do
  381. begin
  382. fRegConfig.OpenKeyReadOnly(cCurrentKey + '\' + RegKey);
  383. if IsRegKeyObject then
  384. begin
  385. jValue := ProcessPairRead(cCurrentKey + '\' + RegKey,Regkey,i);
  386. newObj.AddPair(RegKey,jValue);
  387. end
  388. else if IsRegKeyArray then
  389. begin
  390. jValue := ProcessElementRead(cCurrentKey + '\' + RegKey,Regkey,i);
  391. newObj.AddPair(RegKey,jValue);
  392. end
  393. else raise EAppConfig.Create('Unknow value reading Config Registry');
  394. end;
  395. finally
  396. RegKeyList.Free;
  397. end;
  398. Result := TJsonValue(newObj);
  399. end;
  400. function TAppConfigRegistryProvider<T>.ProcessElementRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  401. var
  402. i : Integer;
  403. jValue : TJSONValue;
  404. RegValue : string;
  405. RegValueList : TStrings;
  406. RegKey : string;
  407. RegKeyList : TStrings;
  408. newObj : TJSONArray;
  409. begin
  410. newObj := TJSONArray.Create;
  411. //read root values
  412. RegValueList := TStringList.Create;
  413. try
  414. fRegConfig.GetValueNames(RegValueList);
  415. for RegValue in RegValueList do
  416. begin
  417. newObj.AddElement(ReadRegValue(cCurrentKey,RegValue));
  418. end;
  419. finally
  420. RegValueList.Free;
  421. end;
  422. //read root keys
  423. RegKeyList := TStringList.Create;
  424. try
  425. fRegConfig.GetKeyNames(RegKeyList);
  426. for RegKey in RegKeyList do
  427. begin
  428. fRegConfig.OpenKeyReadOnly(cCurrentKey + '\' + RegKey);
  429. if IsRegKeyObject then
  430. begin
  431. jValue := ProcessPairRead(cCurrentKey + '\' + RegKey,Regkey,i);
  432. newObj.AddElement(jValue);
  433. end
  434. else if IsRegKeyArray then
  435. begin
  436. jValue := ProcessElementRead(cCurrentKey + '\' + RegKey,Regkey,i);
  437. newObj.AddElement(jValue);
  438. end
  439. else raise EAppConfig.Create('Unknow value reading Config Registry');
  440. end;
  441. finally
  442. RegKeyList.Free;
  443. end;
  444. Result := TJsonValue(newObj);
  445. end;
  446. procedure TAppConfigRegistryProvider<T>.ProcessPairWrite(const cCurrentKey: string; obj: TJSONObject; aIndex: integer);
  447. var
  448. jPair: TJSONPair;
  449. i : Integer;
  450. aCount: integer;
  451. begin
  452. jPair := obj.Pairs[aIndex];
  453. if IsSimpleJsonValue(jPair.JsonValue) then
  454. begin
  455. AddRegValue(cCurrentKey,jPair.JsonString.ToString,jPair.JsonValue);
  456. Exit;
  457. end;
  458. if jPair.JsonValue is TJSONObject then
  459. begin
  460. aCount := TJSONObject(jPair.JsonValue).Count;
  461. for i := 0 to aCount - 1 do
  462. ProcessPairWrite(cCurrentKey + '\' + jPair.JsonString.ToString.DeQuotedString('"'), TJSONObject(jPair.JsonValue),i);
  463. end
  464. else if jPair.JsonValue is TJSONArray then
  465. begin
  466. aCount := TJSONArray(jPair.JsonValue).Count;
  467. for i := 0 to aCount - 1 do
  468. ProcessElementWrite(cCurrentKey + '\' + jPair.JsonString.ToString.DeQuotedString('"'), TJSONArray(jPair.JsonValue),i,aCount);
  469. end
  470. else raise EAppConfig.Create('Error Saving config to Registry');
  471. end;
  472. procedure TAppConfigRegistryProvider<T>.ProcessElementWrite(const cCurrentKey: string; arr: TJSONArray; aIndex, aMax: integer);
  473. var
  474. jValue: TJSONValue;
  475. i : Integer;
  476. aCount: integer;
  477. dig : Integer;
  478. begin
  479. jValue := arr.Items[aIndex];
  480. dig := CountDigits(aMax);
  481. if IsSimpleJsonValue(jValue) then
  482. begin
  483. AddRegValue(cCurrentKey,Zeroes(aIndex,dig),jValue);
  484. Exit;
  485. end;
  486. if jValue is TJSONObject then
  487. begin
  488. aCount := TJSONObject(jValue).Count;
  489. for i := 0 to aCount - 1 do
  490. ProcessPairWrite(cCurrentKey + '\' + Zeroes(aIndex,dig),TJSONObject(jValue),i);
  491. end
  492. else if jValue is TJSONArray then
  493. begin
  494. aCount := TJSONArray(jValue).Count;
  495. for i := 0 to aCount - 1 do
  496. ProcessElementWrite(cCurrentKey + '\' + Zeroes(i,dig),TJSONArray(jValue),i,aCount);
  497. end
  498. else raise EAppConfig.Create('Error Saving config to Registry');
  499. end;
  500. end.