Quick.Config.Registry.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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.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.Base;
  47. type
  48. TAppConfigRegistryProvider = class(TAppConfigProviderBase)
  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. protected
  66. procedure Load(cConfig : TAppConfig); override;
  67. procedure Save(cConfig : TAppConfig); override;
  68. public
  69. constructor Create; override;
  70. destructor Destroy; override;
  71. property HRoot : HKEY read fRootKey write fRootKey;
  72. property MainKey : string read fMainKey write fMainKey;
  73. end;
  74. EAppConfig = Exception;
  75. TAppConfigRegistry = class(TAppConfig)
  76. private
  77. fProvider : TAppConfigRegistryProvider;
  78. function GetProvider : IAppConfigProvider; override;
  79. public
  80. constructor Create; override;
  81. destructor Destroy; override;
  82. property Provider : TAppConfigRegistryProvider read fProvider write fProvider;
  83. end;
  84. implementation
  85. { TAppConfigRegistryProvider }
  86. constructor TAppConfigRegistryProvider.Create;
  87. begin
  88. inherited Create;
  89. fRootKey := HKEY_CURRENT_USER;
  90. fMainKey := '_AppConfig';
  91. fRegConfig := TRegistry.Create(KEY_READ or KEY_WRITE);
  92. end;
  93. destructor TAppConfigRegistryProvider.Destroy;
  94. begin
  95. if Assigned(fRegConfig) then fRegConfig.Free;
  96. inherited;
  97. end;
  98. procedure TAppConfigRegistryProvider.Load(cConfig : TAppConfig);
  99. var
  100. Serializer: TJsonSerializer;
  101. json : string;
  102. begin
  103. fRegConfig.Access := KEY_READ;
  104. fRegConfig.RootKey := fRootKey;
  105. if not fRegConfig.KeyExists('\Software\' + fMainKey) then
  106. begin
  107. if not CreateIfNotExists then raise EAppConfig.Create('Not exists MainKey in registry!');
  108. Save(cConfig);
  109. end;
  110. RegistryToJson(json);
  111. serializer := TJsonSerializer.Create(slPublishedProperty);
  112. try
  113. serializer.JsonToObject(cConfig,json);
  114. finally
  115. serializer.Free;
  116. end;
  117. end;
  118. procedure TAppConfigRegistryProvider.Save(cConfig : TAppConfig);
  119. begin
  120. JsonToRegistry(cConfig.ToJSON);
  121. end;
  122. function TAppConfigRegistryProvider.JsonToRegistry(const StrJson : string) : Boolean;
  123. var
  124. jValue : TJSONValue;
  125. aCount : Integer;
  126. i : Integer;
  127. aCurrentKey : string;
  128. begin
  129. Result := False;
  130. if fMainKey = '' then raise EAppConfig.Create('MainKey not defined!');
  131. fRegConfig.Access := KEY_READ or KEY_WRITE;
  132. fRegConfig.RootKey := fRootKey;
  133. aCurrentKey := '\Software\' + fMainKey;
  134. if fRegConfig.KeyExists(aCurrentKey) then
  135. begin
  136. try
  137. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  138. fRegConfig.MoveKey(aCurrentKey,aCurrentKey + '_bak',True);
  139. except
  140. raise EAppConfig.Create('Can''t write Config Registry');
  141. end;
  142. end;
  143. try
  144. if not AddRegKey('\Software',fMainKey) then
  145. begin
  146. raise EAppConfig.Create('Can''t create key');
  147. end;
  148. {$IFNDEF FPC}
  149. jValue := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONValue;
  150. {$ELSE}
  151. jValue := TJSONObject.ParseJSONValue(StrJson) as TJSONValue;
  152. {$ENDIF}
  153. try
  154. if IsSimpleJsonValue(jValue) then
  155. begin
  156. {$IFNDEF FPC}
  157. AddRegValue(aCurrentKey,TJSONPair(jValue).JsonString.ToString.DeQuotedString('"'),TJSONPair(jValue).JsonValue);
  158. {$ELSE}
  159. AddRegValue(aCurrentKey,TJSONPair(jValue).JsonString.DeQuotedString('"'),TJSONPair(jValue).JsonValue);
  160. {$ENDIF}
  161. end
  162. {$IFNDEF FPC}
  163. else if jValue is TJSONObject then
  164. {$ELSE}
  165. else if jvalue.JSONType = jtObject then
  166. {$ENDIF}
  167. begin
  168. aCount := TJSONObject(jValue).Count;
  169. for i := 0 to aCount - 1 do
  170. ProcessPairWrite(aCurrentKey,TJSONObject(jValue),i);
  171. end
  172. {$IFNDEF FPC}
  173. else if jValue is TJSONArray then
  174. {$ELSE}
  175. else if jValue.JSONType = jtArray then
  176. {$ENDIF}
  177. begin
  178. aCount := TJSONArray(jValue).Count;
  179. for i := 0 to aCount - 1 do
  180. ProcessElementWrite(aCurrentKey,TJSONArray(jValue),i,aCount);
  181. end
  182. else raise EAppConfig.Create('Error Saving config to Registry');
  183. Result := True;
  184. finally
  185. jValue.Free;
  186. end;
  187. if fRegConfig.KeyExists(aCurrentKey + '_bak') then fRegConfig.DeleteKey(aCurrentKey + '_bak');
  188. except
  189. fRegConfig.DeleteKey(aCurrentKey);
  190. fRegConfig.MoveKey(aCurrentKey+'_bak',aCurrentKey,True);
  191. end;
  192. end;
  193. function TAppConfigRegistryProvider.RegistryToJson(out StrJson : string) : Boolean;
  194. var
  195. jValue : TJSONValue;
  196. jPair : TJSONPair;
  197. jArray : TJSONArray;
  198. a, b : string;
  199. aCount : Integer;
  200. i : Integer;
  201. aName : string;
  202. aValue : TJSONValue;
  203. aCurrentKey : string;
  204. newObj : TJSONObject;
  205. RegKeyList : TStringList;
  206. RegValueList : TStringList;
  207. RegKey : string;
  208. RegValue : string;
  209. RegKeyInfo : TRegKeyInfo;
  210. begin
  211. Result := False;
  212. //check if exists root key
  213. fRegConfig.Access := KEY_READ;
  214. fRegConfig.RootKey := fRootKey;
  215. if fRegConfig.KeyExists('\Software\' + fMainKey) then
  216. begin
  217. fRegConfig.OpenKeyReadOnly('\Software\' + fMainKey);
  218. aCurrentKey := '\Software\' + fMainKey;
  219. end
  220. else raise EAppConfig.Create('Can''t read key');
  221. newObj := TJSONObject.Create;
  222. try
  223. //read root values
  224. RegValueList := TStringList.Create;
  225. try
  226. fRegConfig.GetValueNames(RegValueList);
  227. for RegValue in RegValueList do
  228. begin
  229. newObj.AddPair(RegValue,ReadRegValue(aCurrentKey,RegValue));
  230. end;
  231. finally
  232. RegValueList.Free;
  233. end;
  234. //read root keys
  235. RegKeyList := TStringList.Create;
  236. try
  237. fRegConfig.GetKeyNames(RegKeyList);
  238. for RegKey in RegKeyList do
  239. begin
  240. fRegConfig.OpenKeyReadOnly(aCurrentKey + '\' + RegKey);
  241. if IsRegKeyObject then
  242. begin
  243. jValue := ProcessPairRead(aCurrentKey + '\' + RegKey,Regkey,i);
  244. newObj.AddPair(RegKey,jValue);
  245. end
  246. else if IsRegKeyArray then
  247. begin
  248. jValue := ProcessElementRead(aCurrentKey + '\' + RegKey,Regkey,i);
  249. newObj.AddPair(RegKey,jValue);
  250. end
  251. else raise EAppConfig.Create('Unknow value reading Config Registry');
  252. end;
  253. finally
  254. RegKeyList.Free;
  255. end;
  256. StrJson := newObj.ToJSON;
  257. finally
  258. newObj.Free;
  259. end;
  260. end;
  261. function TAppConfigRegistryProvider.IsRegKeyObject(const cCurrentKey : string = '') : Boolean;
  262. begin
  263. Result := not IsRegKeyArray(cCurrentKey);
  264. end;
  265. function TAppConfigRegistryProvider.IsRegKeyArray(const cCurrentKey : string = '') : Boolean;
  266. var
  267. RegValue : string;
  268. RegValueList : TStrings;
  269. RegKey : string;
  270. RegKeyList : TStrings;
  271. n : Integer;
  272. begin
  273. Result := False;
  274. if cCurrentKey <> '' then fRegConfig.OpenKeyReadOnly(cCurrentKey);
  275. //check if exists RegKey numeric (indicates is a Array)
  276. RegKeyList := TStringList.Create;
  277. try
  278. fRegConfig.GetKeyNames(RegKeyList);
  279. for RegKey in RegKeyList do
  280. if TryStrToInt(RegKey,n) then
  281. begin
  282. Result := True;
  283. Break;
  284. end;
  285. finally
  286. RegKeyList.Free;
  287. end;
  288. //check if exists RegValue numeric (indicates is a Array)
  289. RegValueList := TStringList.Create;
  290. try
  291. fRegConfig.GetValueNames(RegValueList);
  292. for RegValue in RegValueList do
  293. if TryStrToInt(RegValue,n) then
  294. begin
  295. Result := True;
  296. Break;
  297. end;
  298. finally
  299. RegValueList.Free;
  300. end;
  301. end;
  302. class function TAppConfigRegistryProvider.IsSimpleJsonValue(v: TJSONValue): Boolean;
  303. begin
  304. Result := (v is TJSONNumber)
  305. or (v is TJSONString)
  306. {$IFNDEF FPC}
  307. or (v is TJSONTrue)
  308. or (v is TJSONFalse)
  309. {$ELSE}
  310. or (v is TJsonBool)
  311. {$ENDIF}
  312. or (v is TJSONNull);
  313. end;
  314. function TAppConfigRegistryProvider.ReadRegValue(const cCurrentKey, cName : string) : TJSONValue;
  315. var
  316. aValue : string;
  317. RegInfo : TRegDataInfo;
  318. begin
  319. if fRegConfig.OpenKeyReadOnly(cCurrentKey) then
  320. begin
  321. if fRegConfig.GetDataInfo(cName,RegInfo) then
  322. case RegInfo.RegData of
  323. rdInteger : Result := TJSONNumber.Create(fRegConfig.ReadInteger(cName));
  324. rdString :
  325. begin
  326. aValue := fRegConfig.ReadString(cName);
  327. if aValue.ToLower = 'true' then Result := TJSONBool.Create(True)
  328. else if aValue.ToLower = 'false' then Result := TJSONBool.Create(False)
  329. else Result := TJSONString.Create(aValue);
  330. end;
  331. else Result := TJSONNull.Create;
  332. end;
  333. end;
  334. end;
  335. function TAppConfigRegistryProvider.AddRegKey(const cCurrentKey, NewKey : string) : Boolean;
  336. begin
  337. Result := fRegConfig.CreateKey(Format('%s\%s',[cCurrentKey,NewKey]));
  338. end;
  339. procedure TAppConfigRegistryProvider.AddRegValue(const cCurrentKey, cName : string; cValue : TJSONValue);
  340. var
  341. aName : string;
  342. aValue : string;
  343. begin
  344. aName := cName.DeQuotedString('"');
  345. aValue := cValue.ToString.DeQuotedString('"');
  346. fRegConfig.OpenKey(cCurrentKey,True);
  347. if cValue is TJSONNumber then fRegConfig.WriteInteger(aName,StrToInt64(aValue))
  348. else if cValue is TJSONString then fRegConfig.WriteString(aName,aValue)
  349. else if cValue is TJSONBool then fRegConfig.WriteString(aName,aValue);
  350. //else if cValue is TJSONNull then fRegConfig.WriteString(aName,'');
  351. end;
  352. function TAppConfigRegistryProvider.ProcessPairRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  353. var
  354. i : Integer;
  355. jValue : TJSONValue;
  356. RegValue : string;
  357. RegValueList : TStrings;
  358. RegKey : string;
  359. RegKeyList : TStrings;
  360. newObj : TJSONObject;
  361. begin
  362. newObj := TJSONObject.Create;
  363. //read root values
  364. RegValueList := TStringList.Create;
  365. try
  366. fRegConfig.GetValueNames(RegValueList);
  367. for RegValue in RegValueList do
  368. begin
  369. newObj.AddPair(RegValue,ReadRegValue(cCurrentKey,RegValue));
  370. end;
  371. finally
  372. RegValueList.Free;
  373. end;
  374. //read root keys
  375. RegKeyList := TStringList.Create;
  376. try
  377. fRegConfig.GetKeyNames(RegKeyList);
  378. for RegKey in RegKeyList do
  379. begin
  380. fRegConfig.OpenKeyReadOnly(cCurrentKey + '\' + RegKey);
  381. if IsRegKeyObject then
  382. begin
  383. jValue := ProcessPairRead(cCurrentKey + '\' + RegKey,Regkey,i);
  384. newObj.AddPair(RegKey,jValue);
  385. end
  386. else if IsRegKeyArray then
  387. begin
  388. jValue := ProcessElementRead(cCurrentKey + '\' + RegKey,Regkey,i);
  389. newObj.AddPair(RegKey,jValue);
  390. end
  391. else raise EAppConfig.Create('Unknow value reading Config Registry');
  392. end;
  393. finally
  394. RegKeyList.Free;
  395. end;
  396. Result := TJsonValue(newObj);
  397. end;
  398. function TAppConfigRegistryProvider.ProcessElementRead(const cCurrentKey, cRegKey : string; aIndex : Integer) : TJSONValue;
  399. var
  400. i : Integer;
  401. jValue : TJSONValue;
  402. RegValue : string;
  403. RegValueList : TStrings;
  404. RegKey : string;
  405. RegKeyList : TStrings;
  406. newObj : TJSONArray;
  407. begin
  408. newObj := TJSONArray.Create;
  409. //read root values
  410. RegValueList := TStringList.Create;
  411. try
  412. fRegConfig.GetValueNames(RegValueList);
  413. for RegValue in RegValueList do
  414. begin
  415. newObj.AddElement(ReadRegValue(cCurrentKey,RegValue));
  416. end;
  417. finally
  418. RegValueList.Free;
  419. end;
  420. //read root keys
  421. RegKeyList := TStringList.Create;
  422. try
  423. fRegConfig.GetKeyNames(RegKeyList);
  424. for RegKey in RegKeyList do
  425. begin
  426. fRegConfig.OpenKeyReadOnly(cCurrentKey + '\' + RegKey);
  427. if IsRegKeyObject then
  428. begin
  429. jValue := ProcessPairRead(cCurrentKey + '\' + RegKey,Regkey,i);
  430. newObj.AddElement(jValue);
  431. end
  432. else if IsRegKeyArray then
  433. begin
  434. jValue := ProcessElementRead(cCurrentKey + '\' + RegKey,Regkey,i);
  435. newObj.AddElement(jValue);
  436. end
  437. else raise EAppConfig.Create('Unknow value reading Config Registry');
  438. end;
  439. finally
  440. RegKeyList.Free;
  441. end;
  442. Result := TJsonValue(newObj);
  443. end;
  444. procedure TAppConfigRegistryProvider.ProcessPairWrite(const cCurrentKey: string; obj: TJSONObject; aIndex: integer);
  445. var
  446. jPair: TJSONPair;
  447. i : Integer;
  448. aCount: integer;
  449. begin
  450. jPair := obj.Pairs[aIndex];
  451. if IsSimpleJsonValue(jPair.JsonValue) then
  452. begin
  453. AddRegValue(cCurrentKey,jPair.JsonString{$IFNDEF FPC}.ToString{$ENDIF},jPair.JsonValue);
  454. Exit;
  455. end;
  456. if jPair.JsonValue is TJSONObject then
  457. begin
  458. aCount := TJSONObject(jPair.JsonValue).Count;
  459. for i := 0 to aCount - 1 do
  460. ProcessPairWrite(cCurrentKey + '\' + jPair.JsonString{$IFNDEF FPC}.ToString{$ENDIF}.DeQuotedString('"'), TJSONObject(jPair.JsonValue),i);
  461. end
  462. else if jPair.JsonValue is TJSONArray then
  463. begin
  464. aCount := TJSONArray(jPair.JsonValue).Count;
  465. for i := 0 to aCount - 1 do
  466. ProcessElementWrite(cCurrentKey + '\' + jPair.JsonString{$IFNDEF FPC}.ToString{$ENDIF}.DeQuotedString('"'), TJSONArray(jPair.JsonValue),i,aCount);
  467. end
  468. else raise EAppConfig.Create('Error Saving config to Registry');
  469. end;
  470. procedure TAppConfigRegistryProvider.ProcessElementWrite(const cCurrentKey: string; arr: TJSONArray; aIndex, aMax: integer);
  471. var
  472. jValue: TJSONValue;
  473. i : Integer;
  474. aCount: integer;
  475. dig : Integer;
  476. begin
  477. jValue := arr.Items[aIndex];
  478. dig := CountDigits(aMax);
  479. if IsSimpleJsonValue(jValue) then
  480. begin
  481. AddRegValue(cCurrentKey,Zeroes(aIndex,dig),jValue);
  482. Exit;
  483. end;
  484. if jValue is TJSONObject then
  485. begin
  486. aCount := TJSONObject(jValue).Count;
  487. for i := 0 to aCount - 1 do
  488. ProcessPairWrite(cCurrentKey + '\' + Zeroes(aIndex,dig),TJSONObject(jValue),i);
  489. end
  490. else if jValue is TJSONArray then
  491. begin
  492. aCount := TJSONArray(jValue).Count;
  493. for i := 0 to aCount - 1 do
  494. ProcessElementWrite(cCurrentKey + '\' + Zeroes(i,dig),TJSONArray(jValue),i,aCount);
  495. end
  496. else raise EAppConfig.Create('Error Saving config to Registry');
  497. end;
  498. { TAppConfigRegistry }
  499. constructor TAppConfigRegistry.Create;
  500. begin
  501. inherited;
  502. fProvider := TAppConfigRegistryProvider.Create;
  503. end;
  504. destructor TAppConfigRegistry.Destroy;
  505. begin
  506. if Assigned(fProvider) then fProvider.Free;
  507. inherited;
  508. end;
  509. function TAppConfigRegistry.GetProvider: IAppConfigProvider;
  510. begin
  511. Result := fProvider;
  512. end;
  513. end.