| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T . R E G I S T R Y -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2001-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Interfaces.C; |
| with System; |
| with GNAT.Directory_Operations; |
| |
| package body GNAT.Registry is |
| |
| use System; |
| |
| ------------------------------ |
| -- Binding to the Win32 API -- |
| ------------------------------ |
| |
| subtype LONG is Interfaces.C.long; |
| subtype ULONG is Interfaces.C.unsigned_long; |
| subtype DWORD is ULONG; |
| |
| type PULONG is access all ULONG; |
| subtype PDWORD is PULONG; |
| subtype LPDWORD is PDWORD; |
| |
| subtype Error_Code is LONG; |
| |
| subtype REGSAM is LONG; |
| |
| type PHKEY is access all HKEY; |
| |
| ERROR_SUCCESS : constant Error_Code := 0; |
| |
| REG_SZ : constant := 1; |
| REG_EXPAND_SZ : constant := 2; |
| |
| function RegCloseKey (Key : HKEY) return LONG; |
| pragma Import (Stdcall, RegCloseKey, "RegCloseKey"); |
| |
| function RegCreateKeyEx |
| (Key : HKEY; |
| lpSubKey : Address; |
| Reserved : DWORD; |
| lpClass : Address; |
| dwOptions : DWORD; |
| samDesired : REGSAM; |
| lpSecurityAttributes : Address; |
| phkResult : PHKEY; |
| lpdwDisposition : LPDWORD) |
| return LONG; |
| pragma Import (Stdcall, RegCreateKeyEx, "RegCreateKeyExA"); |
| |
| function RegDeleteKey |
| (Key : HKEY; |
| lpSubKey : Address) return LONG; |
| pragma Import (Stdcall, RegDeleteKey, "RegDeleteKeyA"); |
| |
| function RegDeleteValue |
| (Key : HKEY; |
| lpValueName : Address) return LONG; |
| pragma Import (Stdcall, RegDeleteValue, "RegDeleteValueA"); |
| |
| function RegEnumValue |
| (Key : HKEY; |
| dwIndex : DWORD; |
| lpValueName : Address; |
| lpcbValueName : LPDWORD; |
| lpReserved : LPDWORD; |
| lpType : LPDWORD; |
| lpData : Address; |
| lpcbData : LPDWORD) return LONG; |
| pragma Import (Stdcall, RegEnumValue, "RegEnumValueA"); |
| |
| function RegOpenKeyEx |
| (Key : HKEY; |
| lpSubKey : Address; |
| ulOptions : DWORD; |
| samDesired : REGSAM; |
| phkResult : PHKEY) return LONG; |
| pragma Import (Stdcall, RegOpenKeyEx, "RegOpenKeyExA"); |
| |
| function RegQueryValueEx |
| (Key : HKEY; |
| lpValueName : Address; |
| lpReserved : LPDWORD; |
| lpType : LPDWORD; |
| lpData : Address; |
| lpcbData : LPDWORD) return LONG; |
| pragma Import (Stdcall, RegQueryValueEx, "RegQueryValueExA"); |
| |
| function RegSetValueEx |
| (Key : HKEY; |
| lpValueName : Address; |
| Reserved : DWORD; |
| dwType : DWORD; |
| lpData : Address; |
| cbData : DWORD) return LONG; |
| pragma Import (Stdcall, RegSetValueEx, "RegSetValueExA"); |
| |
| function RegEnumKey |
| (Key : HKEY; |
| dwIndex : DWORD; |
| lpName : Address; |
| cchName : DWORD) return LONG; |
| pragma Import (Stdcall, RegEnumKey, "RegEnumKeyA"); |
| |
| --------------------- |
| -- Local Constants -- |
| --------------------- |
| |
| Max_Key_Size : constant := 1_024; |
| -- Maximum number of characters for a registry key |
| |
| Max_Value_Size : constant := 2_048; |
| -- Maximum number of characters for a key's value |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function To_C_Mode (Mode : Key_Mode) return REGSAM; |
| -- Returns the Win32 mode value for the Key_Mode value |
| |
| procedure Check_Result (Result : LONG; Message : String); |
| -- Checks value Result and raise the exception Registry_Error if it is not |
| -- equal to ERROR_SUCCESS. Message and the error value (Result) is added |
| -- to the exception message. |
| |
| ------------------ |
| -- Check_Result -- |
| ------------------ |
| |
| procedure Check_Result (Result : LONG; Message : String) is |
| use type LONG; |
| begin |
| if Result /= ERROR_SUCCESS then |
| raise Registry_Error with |
| Message & " (" & LONG'Image (Result) & ')'; |
| end if; |
| end Check_Result; |
| |
| --------------- |
| -- Close_Key -- |
| --------------- |
| |
| procedure Close_Key (Key : HKEY) is |
| Result : LONG; |
| begin |
| Result := RegCloseKey (Key); |
| Check_Result (Result, "Close_Key"); |
| end Close_Key; |
| |
| ---------------- |
| -- Create_Key -- |
| ---------------- |
| |
| function Create_Key |
| (From_Key : HKEY; |
| Sub_Key : String; |
| Mode : Key_Mode := Read_Write) return HKEY |
| is |
| REG_OPTION_NON_VOLATILE : constant := 16#0#; |
| |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| C_Class : constant String := "" & ASCII.NUL; |
| C_Mode : constant REGSAM := To_C_Mode (Mode); |
| |
| New_Key : aliased HKEY; |
| Result : LONG; |
| Dispos : aliased DWORD; |
| |
| begin |
| Result := |
| RegCreateKeyEx |
| (From_Key, |
| C_Sub_Key (C_Sub_Key'First)'Address, |
| 0, |
| C_Class (C_Class'First)'Address, |
| REG_OPTION_NON_VOLATILE, |
| C_Mode, |
| Null_Address, |
| New_Key'Unchecked_Access, |
| Dispos'Unchecked_Access); |
| |
| Check_Result (Result, "Create_Key " & Sub_Key); |
| return New_Key; |
| end Create_Key; |
| |
| ---------------- |
| -- Delete_Key -- |
| ---------------- |
| |
| procedure Delete_Key (From_Key : HKEY; Sub_Key : String) is |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| Result : LONG; |
| begin |
| Result := RegDeleteKey (From_Key, C_Sub_Key (C_Sub_Key'First)'Address); |
| Check_Result (Result, "Delete_Key " & Sub_Key); |
| end Delete_Key; |
| |
| ------------------ |
| -- Delete_Value -- |
| ------------------ |
| |
| procedure Delete_Value (From_Key : HKEY; Sub_Key : String) is |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| Result : LONG; |
| begin |
| Result := RegDeleteValue (From_Key, C_Sub_Key (C_Sub_Key'First)'Address); |
| Check_Result (Result, "Delete_Value " & Sub_Key); |
| end Delete_Value; |
| |
| ------------------- |
| -- For_Every_Key -- |
| ------------------- |
| |
| procedure For_Every_Key |
| (From_Key : HKEY; |
| Recursive : Boolean := False) |
| is |
| procedure Recursive_For_Every_Key |
| (From_Key : HKEY; |
| Recursive : Boolean := False; |
| Quit : in out Boolean); |
| |
| ----------------------------- |
| -- Recursive_For_Every_Key -- |
| ----------------------------- |
| |
| procedure Recursive_For_Every_Key |
| (From_Key : HKEY; |
| Recursive : Boolean := False; |
| Quit : in out Boolean) |
| is |
| use type LONG; |
| use type ULONG; |
| |
| Index : ULONG := 0; |
| Result : LONG; |
| |
| Sub_Key : Interfaces.C.char_array (1 .. Max_Key_Size); |
| pragma Warnings (Off, Sub_Key); |
| |
| Size_Sub_Key : aliased ULONG; |
| Sub_Hkey : HKEY; |
| |
| function Current_Name return String; |
| |
| ------------------ |
| -- Current_Name -- |
| ------------------ |
| |
| function Current_Name return String is |
| begin |
| return Interfaces.C.To_Ada (Sub_Key); |
| end Current_Name; |
| |
| -- Start of processing for Recursive_For_Every_Key |
| |
| begin |
| loop |
| Size_Sub_Key := Sub_Key'Length; |
| |
| Result := |
| RegEnumKey |
| (From_Key, Index, Sub_Key (1)'Address, Size_Sub_Key); |
| |
| exit when not (Result = ERROR_SUCCESS); |
| |
| Sub_Hkey := Open_Key (From_Key, Interfaces.C.To_Ada (Sub_Key)); |
| |
| Action (Natural (Index) + 1, Sub_Hkey, Current_Name, Quit); |
| |
| if not Quit and then Recursive then |
| Recursive_For_Every_Key (Sub_Hkey, True, Quit); |
| end if; |
| |
| Close_Key (Sub_Hkey); |
| |
| exit when Quit; |
| |
| Index := Index + 1; |
| end loop; |
| end Recursive_For_Every_Key; |
| |
| -- Local Variables |
| |
| Quit : Boolean := False; |
| |
| -- Start of processing for For_Every_Key |
| |
| begin |
| Recursive_For_Every_Key (From_Key, Recursive, Quit); |
| end For_Every_Key; |
| |
| ------------------------- |
| -- For_Every_Key_Value -- |
| ------------------------- |
| |
| procedure For_Every_Key_Value |
| (From_Key : HKEY; |
| Expand : Boolean := False) |
| is |
| use GNAT.Directory_Operations; |
| use type LONG; |
| use type ULONG; |
| |
| Index : ULONG := 0; |
| Result : LONG; |
| |
| Sub_Key : String (1 .. Max_Key_Size); |
| pragma Warnings (Off, Sub_Key); |
| |
| Value : String (1 .. Max_Value_Size); |
| pragma Warnings (Off, Value); |
| |
| Size_Sub_Key : aliased ULONG; |
| Size_Value : aliased ULONG; |
| Type_Sub_Key : aliased DWORD; |
| |
| Quit : Boolean; |
| |
| begin |
| loop |
| Size_Sub_Key := Sub_Key'Length; |
| Size_Value := Value'Length; |
| |
| Result := |
| RegEnumValue |
| (From_Key, Index, |
| Sub_Key (1)'Address, |
| Size_Sub_Key'Unchecked_Access, |
| null, |
| Type_Sub_Key'Unchecked_Access, |
| Value (1)'Address, |
| Size_Value'Unchecked_Access); |
| |
| exit when not (Result = ERROR_SUCCESS); |
| |
| Quit := False; |
| |
| if Type_Sub_Key = REG_EXPAND_SZ and then Expand then |
| Action |
| (Natural (Index) + 1, |
| Sub_Key (1 .. Integer (Size_Sub_Key)), |
| Directory_Operations.Expand_Path |
| (Value (1 .. Integer (Size_Value) - 1), |
| Directory_Operations.DOS), |
| Quit); |
| |
| elsif Type_Sub_Key = REG_SZ or else Type_Sub_Key = REG_EXPAND_SZ then |
| Action |
| (Natural (Index) + 1, |
| Sub_Key (1 .. Integer (Size_Sub_Key)), |
| Value (1 .. Integer (Size_Value) - 1), |
| Quit); |
| end if; |
| |
| exit when Quit; |
| |
| Index := Index + 1; |
| end loop; |
| end For_Every_Key_Value; |
| |
| ---------------- |
| -- Key_Exists -- |
| ---------------- |
| |
| function Key_Exists |
| (From_Key : HKEY; |
| Sub_Key : String) return Boolean |
| is |
| New_Key : HKEY; |
| |
| begin |
| New_Key := Open_Key (From_Key, Sub_Key); |
| Close_Key (New_Key); |
| |
| -- We have been able to open the key so it exists |
| |
| return True; |
| |
| exception |
| when Registry_Error => |
| |
| -- An error occurred, the key was not found |
| |
| return False; |
| end Key_Exists; |
| |
| -------------- |
| -- Open_Key -- |
| -------------- |
| |
| function Open_Key |
| (From_Key : HKEY; |
| Sub_Key : String; |
| Mode : Key_Mode := Read_Only) return HKEY |
| is |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| C_Mode : constant REGSAM := To_C_Mode (Mode); |
| |
| New_Key : aliased HKEY; |
| Result : LONG; |
| |
| begin |
| Result := |
| RegOpenKeyEx |
| (From_Key, |
| C_Sub_Key (C_Sub_Key'First)'Address, |
| 0, |
| C_Mode, |
| New_Key'Unchecked_Access); |
| |
| Check_Result (Result, "Open_Key " & Sub_Key); |
| return New_Key; |
| end Open_Key; |
| |
| ----------------- |
| -- Query_Value -- |
| ----------------- |
| |
| function Query_Value |
| (From_Key : HKEY; |
| Sub_Key : String; |
| Expand : Boolean := False) return String |
| is |
| use GNAT.Directory_Operations; |
| use type ULONG; |
| |
| Value : String (1 .. Max_Value_Size); |
| pragma Warnings (Off, Value); |
| |
| Size_Value : aliased ULONG; |
| Type_Value : aliased DWORD; |
| |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| Result : LONG; |
| |
| begin |
| Size_Value := Value'Length; |
| |
| Result := |
| RegQueryValueEx |
| (From_Key, |
| C_Sub_Key (C_Sub_Key'First)'Address, |
| null, |
| Type_Value'Unchecked_Access, |
| Value (Value'First)'Address, |
| Size_Value'Unchecked_Access); |
| |
| Check_Result (Result, "Query_Value " & Sub_Key & " key"); |
| |
| if Type_Value = REG_EXPAND_SZ and then Expand then |
| return Directory_Operations.Expand_Path |
| (Value (1 .. Integer (Size_Value - 1)), |
| Directory_Operations.DOS); |
| else |
| return Value (1 .. Integer (Size_Value - 1)); |
| end if; |
| end Query_Value; |
| |
| --------------- |
| -- Set_Value -- |
| --------------- |
| |
| procedure Set_Value |
| (From_Key : HKEY; |
| Sub_Key : String; |
| Value : String; |
| Expand : Boolean := False) |
| is |
| C_Sub_Key : constant String := Sub_Key & ASCII.NUL; |
| C_Value : constant String := Value & ASCII.NUL; |
| |
| Value_Type : DWORD; |
| Result : LONG; |
| |
| begin |
| Value_Type := (if Expand then REG_EXPAND_SZ else REG_SZ); |
| |
| Result := |
| RegSetValueEx |
| (From_Key, |
| C_Sub_Key (C_Sub_Key'First)'Address, |
| 0, |
| Value_Type, |
| C_Value (C_Value'First)'Address, |
| C_Value'Length); |
| |
| Check_Result (Result, "Set_Value " & Sub_Key & " key"); |
| end Set_Value; |
| |
| --------------- |
| -- To_C_Mode -- |
| --------------- |
| |
| function To_C_Mode (Mode : Key_Mode) return REGSAM is |
| use type REGSAM; |
| |
| KEY_READ : constant := 16#20019#; |
| KEY_WRITE : constant := 16#20006#; |
| KEY_WOW64_64KEY : constant := 16#00100#; |
| KEY_WOW64_32KEY : constant := 16#00200#; |
| |
| begin |
| case Mode is |
| when Read_Only => |
| return KEY_READ + KEY_WOW64_32KEY; |
| |
| when Read_Write => |
| return KEY_READ + KEY_WRITE + KEY_WOW64_32KEY; |
| |
| when Read_Only_64 => |
| return KEY_READ + KEY_WOW64_64KEY; |
| |
| when Read_Write_64 => |
| return KEY_READ + KEY_WRITE + KEY_WOW64_64KEY; |
| end case; |
| end To_C_Mode; |
| |
| end GNAT.Registry; |