Quick.Config.Provider.Registry.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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 : 12/09/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. Quick.Json.Serializer,
  30. {$IFDEF DELPHIRX102_UP}
  31. System.Json,
  32. System.JSON.Types,
  33. {$ELSE}
  34. {$IFDEF FPC}
  35. fpjson,
  36. //jsonparser,
  37. //fpjsonrtti,
  38. Quick.Json.fpc.Compatibility,
  39. {$ELSE}
  40. Rest.Json.Types,
  41. System.JSON,
  42. Rest.Json,
  43. {$ENDIF}
  44. {$ENDIF}
  45. Quick.Commons,
  46. Quick.Config;
  47. type
  48. TAppConfigRegistryProvider<T : class> = class(TAppConfigProviderBase<T>)
  49. private
  50. fRootKey : HKEY;
  51. fMainKey : string;
  52. fRegConfig : TRegistry;
  53. function JsonToRegistry(const StrJson : string) : Boolean;
  54. function RegistryToJson(out StrJson : string) : Boolean;
  55. class function IsSimpleJsonValue(v: TJSONValue): Boolean;
  56. function IsRegKeyObject(const cCurrentKey : string = '') : Boolean;
  57. function IsRegKeyArray(const cCurrentKey : string = '') : Boolean;
  58. function ProcessPairRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  59. function ProcessElementRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  60. procedure ProcessPairWrite(const cCurrentKey: string; obj: TJSONObject; aIndex: integer);
  61. procedure ProcessElementWrite(const cCurrentKey: string; arr: TJSONArray; aIndex, aMax : integer);
  62. function AddRegKey(const cCurrentKey, NewKey : string) : Boolean;
  63. function ReadRegValue(const cCurrentKey, cName : string) : TJSONValue;
  64. procedure AddRegValue(const cCurrentKey, cName : string; cValue : TJSONValue);
  65. public
  66. constructor Create(var cConfig : T); override;
  67. destructor Destroy; override;
  68. property HRoot : HKEY read fRootKey write fRootKey;
  69. property MainKey : string read fMainKey write fMainKey;
  70. procedure Load(var cConfig : T); override;
  71. procedure Save(var cConfig : T); override;
  72. end;
  73. EAppConfig = Exception;
  74. implementation
  75. { TAppConfigRegistryProvider }
  76. constructor TAppConfigRegistryProvider<T>.Create(var cConfig : T);
  77. begin
  78. inherited Create(cConfig);
  79. fRootKey := HKEY_CURRENT_USER;
  80. fMainKey := '_AppConfig';
  81. fRegConfig := TRegistry.Create(KEY_READ or KEY_WRITE);
  82. end;
  83. destructor TAppConfigRegistryProvider<T>.Destroy;
  84. begin
  85. if Assigned(fRegConfig) then fRegConfig.Free;
  86. inherited;
  87. end;
  88. procedure TAppConfigRegistryProvider<T>.Load(var cConfig : T);
  89. var
  90. Serializer: TJsonSerializer;
  91. json : string;
  92. newObj : T;
  93. begin
  94. fRegConfig.Access := KEY_READ;
  95. fRegConfig.RootKey := fRootKey;
  96. if not fRegConfig.KeyExists('\Software\' + fMainKey) then
  97. begin
  98. if CreateIfNotExists then Save(cConfig)
  99. else
  100. begin
  101. cConfig := InitObject;
  102. Exit;
  103. end;
  104. end;
  105. RegistryToJson(json);
  106. serializer := TJsonSerializer.Create(slPublishedProperty);
  107. try
  108. //Streamer.Options := Streamer.Options + [jsoDateTimeAsString ,jsoUseFormatString];
  109. //Streamer.DateTimeFormat := 'yyyy-mm-dd"T"hh:mm:ss.zz';
  110. serializer.JsonToObject(cConfig,json);
  111. Exit;
  112. finally
  113. serializer.Free;
  114. end;
  115. if Assigned(cConfig) then cConfig.Free;
  116. cConfig := newObj;
  117. end;
  118. procedure TAppConfigRegistryProvider<T>.Save(var cConfig : T);
  119. begin
  120. //create object with rtti if nil
  121. if not Assigned(cConfig) then cConfig := InitObject;
  122. JsonToRegistry(TAppConfig(cConfig).ToJSON);
  123. end;
  124. function TAppConfigRegistryProvider<T>.JsonToRegistry(const StrJson : string) : Boolean;
  125. var
  126. jValue : TJSONValue;
  127. aCount : Integer;
  128. i : Integer;
  129. aCurrentKey : string;
  130. begin
  131. Result := False;
  132. if fMainKey = '' then raise EAppConfig.Create('MainKey not defined!');
  133. fRegConfig.Access := KEY_READ or KEY_WRITE;
  134. fRegConfig.RootKey := fRootKey;
  135. aCurrentKey := '\Software\' + fMainKey;
  136. if fRegConfig.KeyExists(aCurrentKey) then
  137. begin
  138. try
  139. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  140. fRegConfig.MoveKey(aCurrentKey,aCurrentKey + '_bak',True);
  141. except
  142. raise EAppConfig.Create('Can''t write Config Registry');
  143. end;
  144. end;
  145. try
  146. if not AddRegKey('\Software',fMainKey) then
  147. begin
  148. raise EAppConfig.Create('Can''t create key');
  149. end;
  150. {$IFNDEF FPC}
  151. jValue := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONValue;
  152. {$ELSE}
  153. jValue := TJSONObject.ParseJSONValue(StrJson) as TJSONValue;
  154. {$ENDIF}
  155. try
  156. if IsSimpleJsonValue(jValue) then
  157. begin
  158. {$IFNDEF FPC}
  159. AddRegValue(aCurrentKey,TJSONPair(jValue).JsonString.ToString.DeQuotedString('"'),TJSONPair(jValue).JsonValue);
  160. {$ELSE}
  161. AddRegValue(aCurrentKey,TJSONPair(jValue).JsonString.DeQuotedString('"'),TJSONPair(jValue).JsonValue);
  162. {$ENDIF}
  163. end
  164. {$IFNDEF FPC}
  165. else if jValue is TJSONObject then
  166. {$ELSE}
  167. else if jvalue.JSONType = jtObject then
  168. {$ENDIF}
  169. begin
  170. aCount := TJSONObject(jValue).Count;
  171. for i := 0 to aCount - 1 do
  172. ProcessPairWrite(aCurrentKey,TJSONObject(jValue),i);
  173. end
  174. {$IFNDEF FPC}
  175. else if jValue is TJSONArray then
  176. {$ELSE}
  177. else if jValue.JSONType = jtArray then
  178. {$ENDIF}
  179. begin
  180. aCount := TJSONArray(jValue).Count;
  181. for i := 0 to aCount - 1 do
  182. ProcessElementWrite(aCurrentKey,TJSONArray(jValue),i,aCount);
  183. end
  184. else raise EAppConfig.Create('Error Saving config to Registry');
  185. Result := True;
  186. finally
  187. jValue.Free;
  188. end;
  189. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  190. except
  191. fRegConfig.DeleteKey(aCurrentKey);
  192. fRegConfig.MoveKey(aCurrentKey+'_bak',aCurrentKey,True);
  193. end;
  194. end;
  195. function TAppConfigRegistryProvider<T>.RegistryToJson(out StrJson : string) : Boolean;
  196. var
  197. jValue : TJSONValue;
  198. jPair : TJSONPair;
  199. jArray : TJSONArray;
  200. a, b : string;
  201. aCount : Integer;
  202. i : Integer;
  203. aName : string;
  204. aValue : TJSONValue;
  205. aCurrentKey : string;
  206. newObj : TJSONObject;
  207. RegKeyList : TStringList;
  208. RegValueList : TStringList;
  209. RegKey : string;
  210. RegValue : string;
  211. RegKeyInfo : TRegKeyInfo;
  212. begin
  213. Result := False;
  214. //check if exists root key
  215. fRegConfig.Access := KEY_READ;
  216. fRegConfig.RootKey := fRootKey;
  217. if fRegConfig.KeyExists('\Software\' + fMainKey) then
  218. begin
  219. fRegConfig.OpenKeyReadOnly('\Software\' + fMainKey);
  220. aCurrentKey := '\Software\' + fMainKey;
  221. end
  222. else raise EAppConfig.Create('Can''t read key');
  223. newObj := TJSONObject.Create;
  224. try
  225. //read root values
  226. RegValueList := TStringList.Create;
  227. try
  228. fRegConfig.GetValueNames(RegValueList);
  229. for RegValue in RegValueList do
  230. begin
  231. newObj.AddPair(RegValue,ReadRegValue(aCurrentKey,RegValue));
  232. end;
  233. finally
  234. RegValueList.Free;
  235. end;
  236. //read root keys
  237. RegKeyList := TStringList.Create;
  238. try
  239. fRegConfig.GetKeyNames(RegKeyList);
  240. for RegKey in RegKeyList do
  241. begin
  242. fRegConfig.OpenKeyReadOnly(aCurrentKey + '\' + RegKey);
  243. if IsRegKeyObject then
  244. begin
  245. jValue := ProcessPairRead(aCurrentKey + '\' + RegKey,Regkey,i);
  246. newObj.AddPair(RegKey,jValue);
  247. end
  248. else if IsRegKeyArray then
  249. begin
  250. jValue := ProcessElementRead(aCurrentKey + '\' + RegKey,Regkey,i);
  251. newObj.AddPair(RegKey,jValue);
  252. end
  253. else raise EAppConfig.Create('Unknow value reading Config Registry');
  254. end;
  255. finally
  256. RegKeyList.Free;
  257. end;
  258. StrJson := newObj.ToJSON;
  259. finally
  260. newObj.Free;
  261. end;
  262. end;
  263. function TAppConfigRegistryProvider<T>.IsRegKeyObject(const cCurrentKey : string = '') : Boolean;
  264. begin
  265. Result := not IsRegKeyArray(cCurrentKey);
  266. end;
  267. function TAppConfigRegistryProvider<T>.IsRegKeyArray(const cCurrentKey : string = '') : Boolean;
  268. var
  269. RegValue : string;
  270. RegValueList : TStrings;
  271. RegKey : string;
  272. RegKeyList : TStrings;
  273. n : Integer;
  274. begin
  275. Result := False;
  276. if cCurrentKey <> '' then fRegConfig.OpenKeyReadOnly(cCurrentKey);
  277. //check if exists RegKey numeric (indicates is a Array)
  278. RegKeyList := TStringList.Create;
  279. try
  280. fRegConfig.GetKeyNames(RegKeyList);
  281. for RegKey in RegKeyList do
  282. if TryStrToInt(RegKey,n) then
  283. begin
  284. Result := True;
  285. Break;
  286. end;
  287. finally
  288. RegKeyList.Free;
  289. end;
  290. //check if exists RegValue numeric (indicates is a Array)
  291. RegValueList := TStringList.Create;
  292. try
  293. fRegConfig.GetValueNames(RegValueList);
  294. for RegValue in RegValueList do
  295. if TryStrToInt(RegValue,n) then
  296. begin
  297. Result := True;
  298. Break;
  299. end;
  300. finally
  301. RegValueList.Free;
  302. end;
  303. end;
  304. class function TAppConfigRegistryProvider<T>.IsSimpleJsonValue(v: TJSONValue): Boolean;
  305. begin
  306. Result := (v is TJSONNumber)
  307. or (v is TJSONString)
  308. {$IFNDEF FPC}
  309. or (v is TJSONTrue)
  310. or (v is TJSONFalse)
  311. {$ELSE}
  312. or (v is TJsonBool)
  313. {$ENDIF}
  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{$IFNDEF FPC}.ToString{$ENDIF},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{$IFNDEF FPC}.ToString{$ENDIF}.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{$IFNDEF FPC}.ToString{$ENDIF}.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.