Quick.Config.Provider.Registry.pas 15 KB

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