Retrieving Windows Registry key values with Microsoft Dexterity

Every so often you get these development requests that seem to push Dexterity to its limits. One of such requests is being able to read a Windows Registry key value using nothing more than SanScript.

The following example will show how to use the Microsoft Windows Management Instrumentation (WMI) Scripting library, ADVAPI32.DLL to retrieve a Windows Registry key value by examining how to retrieve the default Internet browser software being used.

The code will use the RegOpenKeyA and RegQueryValueExA DLL functions to a) return a handle for the registry path where we can found the key, b) then retrieve the actual key value. In order to access external DLL functions, it is necessary to create Dexterity prototype global procedures for the external DLL functions.

RegOpenKeyA@ADVAPI.DLL

{ prototype procedure RegOpenKeyA@ADVAPI.DLL }
out long return_value; {function returns ERROR_SUCCESS }
in long hKey; {Handle of parent key to open the new key under}
in string lpcstr; {Name of the key under hkey to open }
inout long phkey; {Destination for the resulting Handle }



RegQueryValueExA@ADVAPI32.DLL

{ prototype procedure RegQueryValueExA@ADVAPI32.DLL }
out long return_value; {function returns ERROR_SUCCESS }
in long hKey; {handle of the key to query }
in string sName; {Name of value under hkey to query }
in long lReserved; {Reserved, must be null }
inout long lType; {Destination for the value type, or NULL if not}
{required. }
inout string sKeyValue; {Destination for the values contents, or NULL }
{if not required. }
inout long lResultLen; {Size of sKeyValue, updated with the number of }
{bytes returned. }


Now that we have the prototype functions, we can proceed to use the Constant Definition window in Dexterity to define values for each Registry hive.

Registry Hive Constants

Constant Name Constant Value
HKEY_CLASSES_ROOT 2147483648
HKEY_CURRENT_USER 2147483649
HKEY_LOCAL_MACHINE 2147483650
HKEY_USERS 2147483651
HKEY_PERFORMANCE_DATA 2147483652
HKEY_CURRENT_CONFIG 2147483653


NOTE: These constants are usually known by their hexadecimal values, but Dexterity does treats hexadecimal constants as strings, hence the decimal notation used.

Once the prototype functions have been defined, we can wrap these in an API that isolates the developer from dealing with the innerworks of the calls. We will define to global functions as follow:

RegKeyExists

{ global function RegKeyExists }
function returns long phkey;

in long hkey;
in string lpcstr;

local long return_value;

try
extern 'RegOpenKeyA@ADVAPI32.dll'
, return_value
, hkey
, lpcstr
, phkey;
catch[EXCEPTION_CLASS_OBJECT_EXCEPTION]
error "Error calling RegOpenKeyA@ADVAPI32.DLL. Could not locate DLL pointer.";
else
throw;
end try;


RegKeyExists accepts the registry hive parameter (hkey) and the registry path and returns a handler if the path is valid (phkey).

RegGetKeyValue

{ global function RegGetKeyValue }
function returns string key_value;
in long hKey;

local long return_value, lValueType, lValueLength;
local string sKeyValue;

set lValueLength to 255;

try
extern 'RegQueryValueExA@ADVAPI32.DLL'
, return_value
, hKey
, ""
, 0
, lValueType
, sKeyValue
, lValueLength;
catch[EXCEPTION_CLASS_OBJECT_EXCEPTION]
error "Error calling RegQueryValueExA@ADVAPI32.DLL. Could not locate DLL pointer.";
else
throw;
end try;

if return_value = OKAY then
key_value = sKeyValue;
else
key_value = "KEY_ERROR";
end if;


RegGetKeyValue in turn will take the handler (returned by RegKeyExists) and attempt to retrieve a value for the Default entry of the path previously given. If the function succeeds, it will return a string with the actual value, else the user will get a KEY_ERROR message.

Making it all work together..

For this project, I have created the following form:



The form contains 4 local variables:

'(L) RegistryHive': is a drop-down list with the following string values corresponding to each registry hive, as follows: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, and HKEY_CURRENT_CONFIG.

'(L) RegistryPath': is a string of 255 characters in length.

'(L) RegistryKey': is a string of 50 characters in length, not used in this project. I will leave this for a future post.

'(L) RegKeyValue': is a string of 255 characters in length. Set the property to Editable to False.

Now that you have the strings and drop-down list, drag the 'OK Button' push button control to finish -- I am working in the Dynamics dictionary, DYNAMICS.DIC.

The actual production form will look like this:



We can now add the following Sanscript code to the OK Button change script:

syGetRegistryKey OK Button K_CHG

local long hKey; { [In] Handle to an open key. }
local long lHive;

local long return_value, nSubkeys, nSubkeyMaxSize,nMaxChars, nValues, lValueName, lValueData, lClassLen;
local reference ft;
local string lClass;

{ check for the last backslash character on the path string }
if substring('(L) RegistryPath', length('(L) RegistryPath'), 1) <> CH_BACKSLASH then
'(L) RegistryPath' = '(L) RegistryPath' + CH_BACKSLASH;
end if;

{ check the hive selected from the DDL and assign the proper constant }
case '(L) RegistryHive'
in [1] lHive = HKEY_CLASSES_ROOT;
in [2] lHive = HKEY_CURRENT_USER;
in [3] lHive = HKEY_LOCAL_MACHINE;
in [4] lHive = HKEY_USERS;
in [5] lHive = HKEY_PERFORMANCE_DATA;
in [6] lHive = HKEY_CURRENT_CONFIG;
end case;

{ establish if the key entered is valid within the hive }
hKey = RegKeyExists(lHive, '(L) RegistryPath');

{ RegKeyExists will return 0 if the path does not exist within the hive,
else it will return the handle value for the key
}

lClass = "";
lClassLen = 0;

if hKey <> 0 then
{ Get the key value; will append the key to the path to make it whole }
'(L) RegKeyValue' = trim(RegGetKeyValue(hKey));
else
warning "Invalid registry key entered";
end if;


You can compile and run the code in test mode and use the Developer Assistant form to open the newly created form. For example, let's check the registry for the default Internet browser running:



I will be taking a shot a two other functions in the Microsoft WMI Scripting library in a futute installment. Hope you enjoy this article and can't wait to hear your comments.

Acknowledgements

A big thank you to Jon Eastman for his insight on the ADVAPI32.DLL. He gave me the idea for this post... that's what it's all about!

Until next post!

MG.-
Mariano Gomez, MVP
Maximum Global Business, LLC
http://www.maximumglobalbusiness.com

Comments

Anonymous said…
Thank you so much. This is exactly what I was looking for.

Popular posts from this blog

Power Apps - Application Monitoring with Azure Application Insights

DBMS: 12 Microsoft Dynamics GP: 0 error when updating to Microsoft Dynamics GP 2013 R2

eConnect Integration Service for Microsoft Dynamics GP 2010