Module: system-internals Synopsis: Settings and user profiles, for Win32 Author: Scott McKay Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Settings implementation for Win32 define inline-only constant $DWORD-SIZE = raw-as-integer(primitive-word-size()); define constant = ; define constant = ; define inline-only constant $ERROR-SUCCESS = as(, 0); define inline-only constant $REG-SZ = as(, 1); define inline-only constant $REG-EXPAND-SZ = as(, 2); define inline-only constant $REG-MULTI-SZ = as(, 7); define inline-only constant $KEY-QUERY-VALUE = #x0001; define inline-only constant $KEY-SET-VALUE = #x0002; define inline-only constant $KEY-CREATE-SUB-KEY = #x0004; define inline-only constant $KEY-ENUMERATE-SUB-KEYS = #x0008; define inline-only constant $REG-OPTION-NON-VOLATILE = as(, #x00000000); define inline-only constant $HKEY-CLASSES-ROOT = as(, #x80000000); define inline-only constant $HKEY-CURRENT-USER = as(, #x80000001); define inline-only constant $HKEY-LOCAL-MACHINE = as(, #x80000002); define inline-only constant $HKEY-USERS = as(, #x80000003); define inline-only function RegQueryValueEx (hKey :: , value-name :: ) => (data :: , type :: , status :: ) let type-buffer :: = make(, size: $DWORD-SIZE, fill: '\0'); let buffer-size-buffer :: = make(, size: $DWORD-SIZE, fill: '\0'); let status = primitive-wrap-machine-word (%call-c-function ("RegQueryValueExA", c-modifiers: "__stdcall") (hKey :: , lpValueName :: , lpReserved :: , lpType :: , lpData :: , lpcbData :: ) => (success? :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(value-name), primitive-cast-raw-as-pointer(integer-as-raw(0)), primitive-cast-raw-as-pointer(primitive-string-as-raw(type-buffer)), primitive-cast-raw-as-pointer(integer-as-raw(0)), primitive-cast-raw-as-pointer(primitive-string-as-raw(buffer-size-buffer))) end); if (status = $ERROR-SUCCESS) let buffer-size = raw-as-integer (primitive-c-unsigned-long-at (primitive-cast-raw-as-pointer(primitive-string-as-raw(buffer-size-buffer)), integer-as-raw(0), integer-as-raw(0))); let buffer :: = make(, size: buffer-size, fill: '\0'); let status = primitive-wrap-machine-word (%call-c-function ("RegQueryValueExA", c-modifiers: "__stdcall") (hKey :: , lpValueName :: , lpReserved :: , lpType :: , lpData :: , lpcbData :: ) => (success? :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(value-name), primitive-cast-raw-as-pointer(integer-as-raw(0)), primitive-cast-raw-as-pointer(primitive-string-as-raw(type-buffer)), primitive-cast-raw-as-pointer(primitive-string-as-raw(buffer)), primitive-cast-raw-as-pointer(primitive-string-as-raw(buffer-size-buffer))) end); let type = primitive-wrap-machine-word (primitive-c-unsigned-long-at (primitive-cast-raw-as-pointer(primitive-string-as-raw(type-buffer)), integer-as-raw(0), integer-as-raw(0))); // NOTE: For registry entries, the returned buffer-size may include a // trailing NUL character... values(if (status = $ERROR-SUCCESS) let amount-to-copy = if (type = $REG-SZ | type = $REG-EXPAND-SZ) buffer-size - 1 elseif (type = $REG-MULTI-SZ) buffer-size - 2 else buffer-size end; copy-sequence(buffer, end: amount-to-copy) else "" end, type, status) else values("", $REG-SZ, status) end end function RegQueryValueEx; define inline-only function RegSetValueEx (hKey :: , value-name :: , type :: , data :: ) => (status :: ) primitive-wrap-machine-word (%call-c-function ("RegSetValueExA", c-modifiers: "__stdcall") (hKey :: , lpValueName :: , reserved :: , dwType :: , lpData :: , cbData :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(value-name), integer-as-raw(0), primitive-unwrap-machine-word(type), primitive-string-as-raw(data), integer-as-raw(size(data) + 1)) end) end function RegSetValueEx; define inline-only function RegDeleteValue (hKey :: , value-name :: ) => (status :: ) primitive-wrap-machine-word (%call-c-function ("RegDeleteValueA", c-modifiers: "__stdcall") (hKey :: , lpValueName :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(value-name)) end) end function RegDeleteValue; define inline-only function RegOpenKeyEx (hKey :: , subkey-name :: , desired-sam :: ) => (hSubKey :: , status :: ) let hSubKey-buffer :: = make(, size: $DWORD-SIZE, fill: '\0'); let status = primitive-wrap-machine-word (%call-c-function ("RegOpenKeyExA", c-modifiers: "__stdcall") (hKey :: , lpSubKey :: , dwOptions :: , samDesired :: , phkResult :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(subkey-name), integer-as-raw(0), primitive-unwrap-machine-word(desired-sam), primitive-cast-raw-as-pointer(primitive-string-as-raw(hSubKey-buffer))) end); values(primitive-wrap-machine-word (primitive-c-unsigned-long-at (primitive-cast-raw-as-pointer(primitive-string-as-raw(hSubKey-buffer)), integer-as-raw(0), integer-as-raw(0))), status) end function RegOpenKeyEx; define inline-only function RegCreateKeyEx (hKey :: , subkey-name :: , class :: , options :: , desired-sam :: ) => (hSubKey :: , status :: ) let hSubKey-buffer :: = make(, size: $DWORD-SIZE, fill: '\0'); let disposition-buffer :: = make(, size: $DWORD-SIZE, fill: '\0'); let status = primitive-wrap-machine-word (%call-c-function ("RegCreateKeyExA", c-modifiers: "__stdcall") (hKey :: , lpSubKey :: , reserved :: , lpClass :: , dwOptions :: , samDesired :: , lpSecurityAttributes :: , phkResult :: , lpdwDisposition :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(subkey-name), integer-as-raw(0), primitive-string-as-raw(class), primitive-unwrap-machine-word(options), primitive-unwrap-machine-word(desired-sam), primitive-cast-raw-as-pointer(integer-as-raw(0)), primitive-cast-raw-as-pointer(primitive-string-as-raw(hSubKey-buffer)), primitive-cast-raw-as-pointer(primitive-string-as-raw(disposition-buffer))) end); values(primitive-wrap-machine-word (primitive-c-unsigned-long-at (primitive-cast-raw-as-pointer(primitive-string-as-raw(hSubKey-buffer)), integer-as-raw(0), integer-as-raw(0))), status) end function RegCreateKeyEx; define inline-only function RegCloseKey (hKey :: ) => (status :: ) primitive-wrap-machine-word (%call-c-function ("RegCloseKey", c-modifiers: "__stdcall") (hKey :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey))) end) end function RegCloseKey; define inline-only function RegDeleteKey (hKey :: , subkey-name :: ) => (status :: ) primitive-wrap-machine-word (%call-c-function ("RegDeleteKeyA", c-modifiers: "__stdcall") (hKey :: , lpSubKey :: ) => (status :: ) (primitive-cast-raw-as-pointer(primitive-unwrap-machine-word(hKey)), primitive-string-as-raw(subkey-name)) end) end function RegDeleteKey; /// Reading, writing, and removing values define inline-only function settings-key-handle (settings :: ) => (hkey :: false-or()) settings-handle(settings) end settings-key-handle; // Binds 'data' to a byte string containing the key's value define macro reading-value { reading-value (?data:name = ?settings:expression, ?key:expression) ?:body end } => { begin let _hKey = settings-key-handle(?settings); if (_hkey) let (_buffer, _type, _result) = RegQueryValueEx(_hKey, ?key); // If we got an error or the type is wrong, just say the key wasn't found //---*** Is this really what we want to do with all errors? if (_result ~= $ERROR-SUCCESS | _type ~= $REG-SZ) values(#f, #f) else let ?data = as(, _buffer); values(begin ?body end, #t) end else values(#f, #f) end end } end macro reading-value; // 'body' should evaluate to a byte string, which is written as the key's value define macro writing-value { writing-value (?settings:expression, ?key:expression) ?:body end } => { begin let _string :: = begin ?body end; let _hKey = settings-key-handle(?settings); if (_hkey) let _result = RegSetValueEx(_hKey, ?key, $REG-SZ, _string); //---*** Is this really what we want to do with all errors? if (_result ~= $ERROR-SUCCESS) #f else #t end end end } end macro writing-value; define sealed method get-value (settings :: , key :: , type == ) => (value :: false-or(), found? :: ) reading-value (data = settings, key) data end end method get-value; define sealed method set-value (value :: , settings :: , key :: , type == ) => (success? :: ) writing-value (settings, key) value end end method set-value; define sealed method get-value (settings :: , key :: , type == ) => (value :: false-or(), found? :: ) reading-value (data = settings, key) as(, data) end end method get-value; define sealed method set-value (value :: , settings :: , key :: , type == ) => (success? :: ) writing-value (settings, key) as(, value) end end method set-value; define sealed method get-value (settings :: , key :: , type == ) => (value :: false-or(), found? :: ) reading-value (data = settings, key) string-to-integer(data) end end method get-value; define sealed method set-value (value :: , settings :: , key :: , type == ) => (success? :: ) writing-value (settings, key) integer-to-string(value) end end method set-value; define sealed method get-value (settings :: , key :: , type == ) => (value :: false-or(), found? :: ) reading-value (data = settings, key) let value = as-lowercase!(data); case value = "yes" => #t; value = "no" => #f; otherwise => #t; end end end method get-value; define sealed method set-value (value :: , settings :: , key :: , type == ) => (success? :: ) writing-value (settings, key) if (value) "yes" else "no" end end end method set-value; define sealed method get-value (settings :: , key :: , type == ) => (value :: false-or(), found? :: ) reading-value (data = settings, key) string-to-machine-word(data) end end method get-value; define sealed method set-value (value :: , settings :: , key :: , type == ) => (success? :: ) writing-value (settings, key) machine-word-to-string(value) end end method set-value; define sealed method do-remove-value! (settings :: , key :: ) => () let hKey = settings-key-handle(settings); when (hKey) RegDeleteValue(hKey, key) end; end method do-remove-value!; /// Creating keys define sealed method settings-key-name (settings :: ) => (key-name :: ) "HKEY_CLASSES_ROOT" end method settings-key-name; define sealed method settings-handle (settings :: ) => (handle :: ) $HKEY-CLASSES-ROOT end method settings-handle; define sealed method settings-writable? (settings :: ) => (writable? :: ) #t end method settings-writable?; define sealed method settings-key-name (settings :: ) => (key-name :: ) error("Site settings not supported under Windows") end method settings-key-name; define sealed method settings-key-name (settings :: ) => (key-name :: ) error("Site settings not supported under Windows") end method settings-key-name; define sealed method settings-key-name (settings :: ) => (key-name :: ) "HKEY_LOCAL_MACHINE" end method settings-key-name; define sealed method settings-handle (settings :: ) => (handle :: ) $HKEY-LOCAL-MACHINE end method settings-handle; define sealed method settings-writable? (settings :: ) => (writable? :: ) #t end method settings-writable?; define sealed method settings-key-name (settings :: ) => (key-name :: ) "Software" end method settings-key-name; define sealed method settings-key-name (settings :: ) => (key-name :: ) "Hardware" end method settings-key-name; define sealed method settings-key-name (settings :: ) => (key-name :: ) "HKEY_USERS" end method settings-key-name; define sealed method settings-handle (settings :: ) => (handle :: ) $HKEY-USERS end method settings-handle; define sealed method settings-writable? (settings :: ) => (writable? :: ) #t end method settings-writable?; define sealed method settings-key-name (settings :: ) => (key-name :: ) "Software" end method settings-key-name; define sealed method settings-key-name (settings :: ) => (key-name :: ) "HKEY_CURRENT_USER" end method settings-key-name; define sealed method settings-handle (settings :: ) => (handle :: ) $HKEY-CURRENT-USER end method settings-handle; define sealed method settings-writable? (settings :: ) => (writable? :: ) #t end method settings-writable?; define sealed method settings-key-name (settings :: ) => (key-name :: ) "Software" end method settings-key-name; /// Registering keys define inline-only constant $READING-SAM = as(, logior($KEY-QUERY-VALUE, $KEY-ENUMERATE-SUB-KEYS)); define inline-only constant $WRITING-SAM = as(, logior($KEY-QUERY-VALUE, $KEY-SET-VALUE, $KEY-CREATE-SUB-KEY, $KEY-ENUMERATE-SUB-KEYS)); define variable *settings-default-class* = "Open Dylan"; define sealed method initialize-settings (settings :: , for-writing? :: ) => () local method open () let parent = element($settings-table, settings-parent(settings)); initialize-settings(parent, for-writing?); let hKey = settings-key-handle(parent); if (hKey) let key = settings-key-name(settings); let class = *settings-default-class*; let (phkResult, result) = if (for-writing?) RegCreateKeyEx(hKey, key, class, $REG-OPTION-NON-VOLATILE, $WRITING-SAM) else RegOpenKeyEx(hKey, key, $READING-SAM) end; if (result ~= $ERROR-SUCCESS) //---*** What should we do with errors? #f else settings-writable?(settings) := for-writing?; settings-handle(settings) := phkResult; end end end method; let handle = settings-key-handle(settings); if (handle) when (for-writing? & ~settings-writable?(settings)) RegCloseKey(handle); invalidate-settings-caches(settings); open() end else open() end end method initialize-settings; define sealed method register-key (settings :: , key-name :: , for-writing? :: ) => (key :: ) // No need to do anything except ensure that all the parent // settings have been initialized //---*** This should transmogrify '?' and '*' characters initialize-settings(settings, for-writing?); key-name end method register-key; define sealed method unregister-key (settings :: , key-name :: ) => () initialize-settings(settings, #t); let hKey = settings-key-handle(settings); when (hKey) RegDeleteKey(hKey, key-name) end; end method unregister-key;