/trunk/matreshka/source/league/os/windows/matreshka-internals-settings-registry.adb

http://github.com/landgraf/matreshka · Ada · 606 lines · 384 code · 113 blank · 109 comment · 21 complexity · 54bab179956173868dac41a56713a922 MD5 · raw file

  1. ------------------------------------------------------------------------------
  2. -- --
  3. -- Matreshka Project --
  4. -- --
  5. -- Localization, Internationalization, Globalization for Ada --
  6. -- --
  7. -- Runtime Library Component --
  8. -- --
  9. ------------------------------------------------------------------------------
  10. -- --
  11. -- Copyright Š 2011, Vadim Godunko <vgodunko@gmail.com> --
  12. -- All rights reserved. --
  13. -- --
  14. -- Redistribution and use in source and binary forms, with or without --
  15. -- modification, are permitted provided that the following conditions --
  16. -- are met: --
  17. -- --
  18. -- * Redistributions of source code must retain the above copyright --
  19. -- notice, this list of conditions and the following disclaimer. --
  20. -- --
  21. -- * Redistributions in binary form must reproduce the above copyright --
  22. -- notice, this list of conditions and the following disclaimer in the --
  23. -- documentation and/or other materials provided with the distribution. --
  24. -- --
  25. -- * Neither the name of the Vadim Godunko, IE nor the names of its --
  26. -- contributors may be used to endorse or promote products derived from --
  27. -- this software without specific prior written permission. --
  28. -- --
  29. -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS --
  30. -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT --
  31. -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR --
  32. -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT --
  33. -- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, --
  34. -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED --
  35. -- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR --
  36. -- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF --
  37. -- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING --
  38. -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --
  39. -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --
  40. -- --
  41. ------------------------------------------------------------------------------
  42. -- $Revision$ $Date$
  43. ------------------------------------------------------------------------------
  44. with Ada.Unchecked_Conversion;
  45. with Interfaces.C;
  46. with League.Characters;
  47. with League.Strings.Internals;
  48. with Matreshka.Internals.Strings.C;
  49. with Matreshka.Internals.Utf16;
  50. with Matreshka.Internals.Windows;
  51. package body Matreshka.Internals.Settings.Registry is
  52. use type League.Characters.Universal_Character;
  53. -----------------
  54. -- Windows API --
  55. -----------------
  56. type ACCESS_MASK is new Interfaces.C.unsigned_long;
  57. type REGSAM is new ACCESS_MASK;
  58. type PHKEY is access all HKEY;
  59. type SECURITY_ATTRIBUTES is null record;
  60. type LPSECURITY_ATTRIBUTES is access all SECURITY_ATTRIBUTES;
  61. pragma Convention (C, LPSECURITY_ATTRIBUTES);
  62. KEY_WRITE : constant REGSAM := 16#20006#;
  63. KEY_READ : constant REGSAM := 16#20019#;
  64. subtype LONG is Interfaces.C.long;
  65. subtype DWORD is interfaces.C.unsigned_long;
  66. use type LONG;
  67. type LPDWORD is access all DWORD;
  68. REG_OPTION_NON_VOLATILE : constant DWORD := 0;
  69. REG_SZ : constant DWORD := 1;
  70. function To_HKEY is
  71. new Ada.Unchecked_Conversion (Interfaces.C.unsigned, HKEY);
  72. No_HKEY : constant HKEY := HKEY (System.Null_Address);
  73. HKEY_CLASSES_ROOT : constant HKEY := To_HKEY (16#8000_0000#);
  74. HKEY_CURRENT_USER : constant HKEY := To_HKEY (16#8000_0001#);
  75. HKEY_LOCAL_MACHINE : constant HKEY := To_HKEY (16#8000_0002#);
  76. HKEY_USERS : constant HKEY := To_HKEY (16#8000_0003#);
  77. function RegOpenKeyEx
  78. (hKey : Registry.HKEY;
  79. lpSubKey : Windows.LPCWSTR;
  80. ulOptions : Interfaces.C.unsigned_long;
  81. samDesired : REGSAM;
  82. phkResult : PHKEY) return LONG;
  83. pragma Import (Stdcall, RegOpenKeyEx, "RegOpenKeyExW");
  84. function RegCreateKeyEx
  85. (hKey : Registry.HKEY;
  86. lpSubKey : Windows.LPCWSTR;
  87. Reserved : Interfaces.C.unsigned_long;
  88. lpClass : Windows.LPWSTR;
  89. dwOptions : DWORD;
  90. samDesired : REGSAM;
  91. lpSecurityAttributes : LPSECURITY_ATTRIBUTES;
  92. phkResult : PHKEY;
  93. lpdwDisposition : LPDWORD) return LONG;
  94. pragma Import (Stdcall, RegCreateKeyEx, "RegCreateKeyExW");
  95. -- function RegCloseKey (hKey : Registry.HKEY) return LONG;
  96. procedure RegCloseKey (hKey : Registry.HKEY);
  97. pragma Import (Stdcall, RegCloseKey, "RegCloseKey");
  98. -- function RegFlushKey (hKey : Registry.HKEY) return LONG;
  99. procedure RegFlushKey (hKey : Registry.HKEY);
  100. pragma Import (Stdcall, RegFlushKey, "RegFlushKey");
  101. function RegSetValueEx
  102. (hKey : Registry.HKEY;
  103. lpSubKey : Windows.LPCWSTR;
  104. Reserved : DWORD;
  105. dwType : DWORD;
  106. lpData : System.Address;
  107. cbData : DWORD) return LONG;
  108. pragma Import (Stdcall, RegSetValueEx, "RegSetValueExW");
  109. function RegQueryValueEx
  110. (hKey : Registry.HKEY;
  111. lpSubKey : Windows.LPCWSTR;
  112. Reserved : LPDWORD;
  113. lpType : LPDWORD;
  114. lpData : System.Address;
  115. lpcbData : LPDWORD) return LONG;
  116. pragma Import (Stdcall, RegQueryValueEx, "RegQueryValueExW");
  117. function Create
  118. (Manager : not null access Abstract_Manager'Class;
  119. Name : League.Strings.Universal_String;
  120. Root : HKEY;
  121. Key : League.Strings.Universal_String;
  122. Read_Only : Boolean) return not null Settings_Access;
  123. -- Creates storage pointing to specified root and key. Read_Only means
  124. -- that subtree is opened for reading only.
  125. procedure Split_Path_Name
  126. (Key : League.Strings.Universal_String;
  127. Path : out League.Strings.Universal_String;
  128. Name : out League.Strings.Universal_String);
  129. -- Split key into path and name parts.
  130. function Open_Or_Create
  131. (Parent : HKEY;
  132. Path : League.Strings.Universal_String) return HKEY;
  133. -- Opens existing path or create new path and returns its handler.
  134. function Open
  135. (Parent : HKEY;
  136. Path : League.Strings.Universal_String) return HKEY;
  137. -- Opens existing path in read-only mode and returns its handler.
  138. HKEY_CURRENT_USER_Name : constant League.Strings.Universal_String
  139. := League.Strings.To_Universal_String ("HKEY_CURRENT_USER");
  140. HKEY_LOCAL_MACHINE_Name : constant League.Strings.Universal_String
  141. := League.Strings.To_Universal_String ("HKEY_LOCAL_MACHINE");
  142. HKEY_CLASSES_ROOT_Name : constant League.Strings.Universal_String
  143. := League.Strings.To_Universal_String ("HKEY_CLASSES_ROOT");
  144. HKEY_USERS_Name : constant League.Strings.Universal_String
  145. := League.Strings.To_Universal_String ("HKEY_USERS");
  146. --------------
  147. -- Contains --
  148. --------------
  149. overriding function Contains
  150. (Self : Registry_Settings;
  151. Key : League.Strings.Universal_String) return Boolean
  152. is
  153. Handler : HKEY;
  154. Path : League.Strings.Universal_String;
  155. Name : League.Strings.Universal_String;
  156. Found : Boolean := True;
  157. begin
  158. -- Compute path to open
  159. Split_Path_Name (Key, Path, Name);
  160. -- Try to open path
  161. Handler := Open (Self.Handler, Path);
  162. if Handler /= No_HKEY then
  163. -- Try to retrieve value
  164. if RegQueryValueEx
  165. (Handler,
  166. League.Strings.Internals.Internal (Name).Value (0)'Access,
  167. null,
  168. null,
  169. System.Null_Address,
  170. null) /= 0
  171. then
  172. Found := False;
  173. end if;
  174. RegCloseKey (Handler);
  175. else
  176. Found := False;
  177. end if;
  178. return Found;
  179. end Contains;
  180. ------------
  181. -- Create --
  182. ------------
  183. function Create
  184. (Manager : not null access Abstract_Manager'Class;
  185. Key : League.Strings.Universal_String;
  186. Read_Only : Boolean) return not null Settings_Access
  187. is
  188. use type League.Strings.Universal_String;
  189. Path : League.Strings.Universal_String := Key;
  190. Separator : Natural;
  191. begin
  192. -- Remove leading backslash if any.
  193. if Path.Element (1) = '\' then
  194. Path := Path.Slice (2, Path.Length);
  195. end if;
  196. Separator := Path.Index ('\');
  197. if Separator = 0 then
  198. Separator := Path.Length + 1;
  199. end if;
  200. if Path.Slice (1, Separator - 1) = HKEY_CURRENT_USER_Name then
  201. return
  202. Create
  203. (Manager,
  204. '\' & Path,
  205. HKEY_CURRENT_USER,
  206. Path.Slice (Separator + 1, Path.Length),
  207. Read_Only);
  208. elsif Path.Slice (1, Separator - 1) = HKEY_LOCAL_MACHINE_Name then
  209. return
  210. Create
  211. (Manager,
  212. '\' & Path,
  213. HKEY_LOCAL_MACHINE,
  214. Path.Slice (Separator + 1, Path.Length),
  215. Read_Only);
  216. elsif Path.Slice (1, Separator - 1) = HKEY_CLASSES_ROOT_Name then
  217. return
  218. Create
  219. (Manager,
  220. '\' & Path,
  221. HKEY_CLASSES_ROOT,
  222. Path.Slice (Separator + 1, Path.Length),
  223. Read_Only);
  224. elsif Path.Slice (1, Separator - 1) = HKEY_USERS_Name then
  225. return
  226. Create
  227. (Manager,
  228. '\' & Path,
  229. HKEY_USERS,
  230. Path.Slice (Separator + 1, Path.Length),
  231. Read_Only);
  232. else
  233. return
  234. Create
  235. (Manager,
  236. '\' & HKEY_LOCAL_MACHINE_Name & '\' & Path,
  237. HKEY_LOCAL_MACHINE,
  238. Path,
  239. Read_Only);
  240. end if;
  241. end Create;
  242. ------------
  243. -- Create --
  244. ------------
  245. function Create
  246. (Manager : not null access Abstract_Manager'Class;
  247. Name : League.Strings.Universal_String;
  248. Root : HKEY;
  249. Key : League.Strings.Universal_String;
  250. Read_Only : Boolean) return not null Settings_Access is
  251. begin
  252. return Aux : constant not null Settings_Access
  253. := new Registry_Settings'
  254. (Counter => <>,
  255. Manager => Manager,
  256. Name => Name,
  257. Handler => <>,
  258. Read_Only => Read_Only)
  259. do
  260. declare
  261. use type LONG;
  262. Self : Registry_Settings'Class
  263. renames Registry_Settings'Class (Aux.all);
  264. begin
  265. if Self.Read_Only then
  266. -- Open registry to read when Read_Only mode is specified.
  267. Self.Handler := Open (Root, Key);
  268. else
  269. -- In Read_Write mode, try to open first.
  270. Self.Handler := Open_Or_Create (Root, Key);
  271. if Self.Handler = No_HKEY then
  272. -- Fallback to read-only mode and try to open it to read.
  273. Self.Read_Only := True;
  274. Self.Handler := Open (Root, Key);
  275. end if;
  276. end if;
  277. end;
  278. end return;
  279. end Create;
  280. --------------
  281. -- Finalize --
  282. --------------
  283. overriding procedure Finalize
  284. (Self : not null access Registry_Settings) is
  285. begin
  286. if Self.Handler /= No_HKEY then
  287. RegCloseKey (Self.Handler);
  288. Self.Handler := No_HKEY;
  289. end if;
  290. end Finalize;
  291. ----------
  292. -- Name --
  293. ----------
  294. overriding function Name
  295. (Self : not null access Registry_Settings)
  296. return League.Strings.Universal_String is
  297. begin
  298. return Self.Name;
  299. end Name;
  300. ----------
  301. -- Open --
  302. ----------
  303. function Open
  304. (Parent : HKEY;
  305. Path : League.Strings.Universal_String) return HKEY
  306. is
  307. Handler : aliased HKEY;
  308. begin
  309. if RegOpenKeyEx
  310. (Parent,
  311. League.Strings.Internals.Internal (Path).Value (0)'Access,
  312. 0,
  313. KEY_READ,
  314. Handler'Unchecked_Access) /= 0
  315. then
  316. Handler := No_HKEY;
  317. end if;
  318. return Handler;
  319. end Open;
  320. --------------------
  321. -- Open_Or_Create --
  322. --------------------
  323. function Open_Or_Create
  324. (Parent : HKEY;
  325. Path : League.Strings.Universal_String) return HKEY
  326. is
  327. Handler : aliased HKEY;
  328. begin
  329. if RegOpenKeyEx
  330. (Parent,
  331. League.Strings.Internals.Internal (Path).Value (0)'Access,
  332. 0,
  333. KEY_READ or KEY_WRITE,
  334. Handler'Unchecked_Access) /= 0
  335. then
  336. -- Try to create path
  337. if RegCreateKeyEx
  338. (Parent,
  339. League.Strings.Internals.Internal (Path).Value (0)'Access,
  340. 0,
  341. null,
  342. REG_OPTION_NON_VOLATILE,
  343. KEY_READ or KEY_WRITE,
  344. null,
  345. Handler'Unchecked_Access,
  346. null) /= 0
  347. then
  348. -- Operation failed.
  349. Handler := No_HKEY;
  350. end if;
  351. end if;
  352. return Handler;
  353. end Open_Or_Create;
  354. ------------
  355. -- Remove --
  356. ------------
  357. overriding procedure Remove
  358. (Self : in out Registry_Settings;
  359. Key : League.Strings.Universal_String) is
  360. begin
  361. null;
  362. end Remove;
  363. ---------------
  364. -- Set_Value --
  365. ---------------
  366. overriding procedure Set_Value
  367. (Self : in out Registry_Settings;
  368. Key : League.Strings.Universal_String;
  369. Value : League.Holders.Holder)
  370. is
  371. use type Matreshka.Internals.Utf16.Utf16_String_Index;
  372. Handler : aliased HKEY;
  373. Path : League.Strings.Universal_String;
  374. Name : League.Strings.Universal_String;
  375. V : League.Strings.Universal_String;
  376. begin
  377. if Self.Handler = No_HKEY or Self.Read_Only then
  378. -- Registry can't be modified in read-only mode.
  379. return;
  380. end if;
  381. -- Compute path to open
  382. Split_Path_Name (Key, Path, Name);
  383. -- Try to open path
  384. Handler := Open_Or_Create (Self.Handler, Path);
  385. if Handler = No_HKEY then
  386. -- Operation failed, return.
  387. return;
  388. end if;
  389. -- Extract value.
  390. V := League.Holders.Element (Value);
  391. -- Store string.
  392. if RegSetValueEx
  393. (Handler,
  394. League.Strings.Internals.Internal (Name).Value (0)'Access,
  395. 0,
  396. REG_SZ,
  397. League.Strings.Internals.Internal (V).Value (0)'Address,
  398. DWORD ((League.Strings.Internals.Internal (V).Unused + 1) * 2)) /= 0
  399. then
  400. null;
  401. end if;
  402. RegCloseKey (Handler);
  403. end Set_Value;
  404. ---------------------
  405. -- Split_Path_Name --
  406. ---------------------
  407. procedure Split_Path_Name
  408. (Key : League.Strings.Universal_String;
  409. Path : out League.Strings.Universal_String;
  410. Name : out League.Strings.Universal_String) is
  411. begin
  412. Path := League.Strings.Empty_Universal_String;
  413. Name := Key;
  414. for J in 1 .. Key.Length loop
  415. if Key.Element (J) = '\' then
  416. Path := Key.Slice (1, J - 1);
  417. Name := Key.Slice (J + 1, Key.Length);
  418. exit;
  419. end if;
  420. end loop;
  421. end Split_Path_Name;
  422. ----------
  423. -- Sync --
  424. ----------
  425. overriding procedure Sync (Self : in out Registry_Settings) is
  426. begin
  427. if Self.Handler /= No_HKEY and not Self.Read_Only then
  428. -- RegFlushKey requires KEY_QUERY_VALUE access right, this right is
  429. -- part of KEY_READ.
  430. RegFlushKey (Self.Handler);
  431. end if;
  432. end Sync;
  433. -----------
  434. -- Value --
  435. -----------
  436. overriding function Value
  437. (Self : Registry_Settings;
  438. Key : League.Strings.Universal_String)
  439. return League.Holders.Holder
  440. is
  441. use Matreshka.Internals.Utf16;
  442. use type DWORD;
  443. Handler : HKEY;
  444. Path : League.Strings.Universal_String;
  445. Name : League.Strings.Universal_String;
  446. V_Type : aliased DWORD;
  447. V_Size : aliased DWORD;
  448. Value : League.Holders.Holder;
  449. begin
  450. -- Compute path to open
  451. Split_Path_Name (Key, Path, Name);
  452. -- Try to open path
  453. Handler := Open (Self.Handler, Path);
  454. if Handler = No_HKEY then
  455. return Value;
  456. end if;
  457. -- Try to retrieve value
  458. if RegQueryValueEx
  459. (Handler,
  460. League.Strings.Internals.Internal (Name).Value (0)'Access,
  461. null,
  462. V_Type'Unchecked_Access,
  463. System.Null_Address,
  464. V_Size'Unchecked_Access) = 0
  465. then
  466. if V_Type = REG_SZ then
  467. declare
  468. V : Matreshka.Internals.Utf16.Utf16_String
  469. (0
  470. .. Matreshka.Internals.Utf16.Utf16_String_Index
  471. (V_Size / 2));
  472. begin
  473. if RegQueryValueEx
  474. (Handler,
  475. League.Strings.Internals.Internal (Name).Value (0)'Access,
  476. null,
  477. V_Type'Unchecked_Access,
  478. V'Address,
  479. V_Size'Unchecked_Access) = 0
  480. then
  481. V (V'Last) := 0;
  482. League.Holders.Replace_Element
  483. (Value,
  484. Matreshka.Internals.Strings.C.To_Valid_Universal_String
  485. (V (0)'Unchecked_Access));
  486. end if;
  487. end;
  488. end if;
  489. end if;
  490. RegCloseKey (Handler);
  491. return Value;
  492. end Value;
  493. end Matreshka.Internals.Settings.Registry;