Quick.Config.Registry.pas 17 KB

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