Page 1 of 1

COMatePLus and request on remote host

Posted: Thu Nov 26, 2020 1:20 pm
by tatanas
Hi,

I would like to get the installed program list from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall registry key on remote computers.
I'm trying with COMatePlus and the StdRegProv class with its EnumKey method but I can' connect to the remote computer.

Code: Select all

XIncludeFile "COMatePLUS.pbi"

hostname.s = "host"
login.s = "administrator"
password.s = "adminpass"
HKEY_LOCAL_MACHINE = $80000002
strKeyPath.s = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
objLocator.COMateObject
objService.COMateObject


objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")
If objLocator
	Debug "objLocator ok"
	objLocator\ConnectServer(hostname, "root\CIMV2", login, password)
; 	objLocator\Invoke("ConnectServer(" + "'" + hostname + "', '" + "root\CIMV2" + "', '" + login + "', '" + password + "')")

	objService = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
	If objService
		Debug "objService ok"

		objService\Invoke("EnumKey(" + Str(HKEY_LOCAL_MACHINE) + ", '" + strKeyPath + "', '" + Str(@dwValue))	
		Debug PeekS(dwValue, -1, #PB_Unicode)	
		SysFreeString_(dwValue)		

		objService\Release()
	EndIf
	objLocator\Release()
EndIf
The error : "Interface method not found: ConnectServer."
However "ConnectServer" is a method of SWbemLocator object. Should I proceed in a different way ?

Re: COMatePLus and request on remote host

Posted: Thu Nov 26, 2020 1:24 pm
by infratec
Use

Code: Select all

objLocator\Invoke("ConnectServer(" + "'" + hostname + "', '" + "root\CIMV2" + "', '" + login + "', '" + password + "')")
Debug COMate_GetLastErrorDescription()
To see what's the reason.

Re: COMatePLus and request on remote host

Posted: Thu Nov 26, 2020 1:37 pm
by infratec

Code: Select all

EnableExplicit

XIncludeFile "COMatePLUS.pbi"

Define.l dwValue
Define.i HKEY_LOCAL_MACHINE
Define Hostname$, login$, password$, strKeyPath$
Define.COMateObject objLocator, objService

Hostname$ = "192.168.0.1"
login$ = "Administrator"
password$ = "XXXXXX"
HKEY_LOCAL_MACHINE = $80000002
strKeyPath$ = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"


objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")
If objLocator
  Debug "objLocator ok"
  
  If objLocator\Invoke("ConnectServer(" + "'" + Hostname$ + "', '" + "root\CIMV2" + "', '" + login$ + "', '" + password$ + "')") = #S_OK
    Debug COMate_GetLastErrorDescription()
    
    objService = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
    If objService
      Debug "objService ok"
      
      objService\Invoke("EnumKey(" + Str(HKEY_LOCAL_MACHINE) + ", '" + strKeyPath$ + "', '" + Str(@dwValue))
      If COMate_GetLastErrorCode() = 0
        Debug PeekS(dwValue, -1, #PB_Unicode)   
        SysFreeString_(dwValue)
      Else
        Debug COMate_GetLastErrorDescription()
      EndIf
      
      objService\Release()
    EndIf
  Else
    Debug COMate_GetLastErrorDescription()
  EndIf
  objLocator\Release()
EndIf

Re: COMatePLus and request on remote host

Posted: Thu Nov 26, 2020 2:09 pm
by tatanas
Ok, so I should use Invoke method.

Using your code, I replaced the EnumKey method by GetStringValue :

Code: Select all

EnableExplicit

XIncludeFile "COMatePLUS.pbi"

Define.l dwValue
Define.i HKEY_LOCAL_MACHINE
Define Hostname$, login$, password$, strKeyPath$
Define.COMateObject objLocator, objService

Hostname$ = "x.x.x.x"
login$ = "Administrator"
password$ = "adminpass"
HKEY_LOCAL_MACHINE = $80000002
strKeyPath$ = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"


objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")
If objLocator
	Debug "objLocator ok"
	
	If objLocator\Invoke("ConnectServer(" + "'" + Hostname$ + "', '" + "root\CIMV2" + "', '" + login$ + "', '" + password$ + "')") = #S_OK
		Debug COMate_GetLastErrorDescription()
		
		objService = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
		If objService
			Debug "objService ok"
			
			objService\Invoke("GetStringValue(" + Str(HKEY_LOCAL_MACHINE) + ", '" + strKeyPath$ + "\VLC media player" + "', '" + "InstallLocation"  + "', " + Str(@dwValue) + " BYREF)")
			If COMate_GetLastErrorCode() = 0
				Debug PeekS(dwValue, -1, #PB_Unicode)   
				SysFreeString_(dwValue)
			Else
				Debug COMate_GetLastErrorDescription()
			EndIf
			
			objService\Release()
		EndIf
	Else
		Debug COMate_GetLastErrorDescription()
	EndIf
	objLocator\Release()
EndIf
Error message : "Type mismatch occurred" from objService\Invoke("GetStringValue...

EDIT : I found this post viewtopic.php?p=312528#p312528 from SFSxOI. His code seemed to work in 2010 but I have the same error now.

EDIT2 : I just realize that I should replace the '.' after "winmgmts:\\" with the hostname/@ip, but if I do that i've got an denied access error...

Re: COMatePLus and request on remote host

Posted: Fri Nov 27, 2020 8:22 am
by tatanas
I modified the ConnectServer part because I have to connect to "root\default:StdRegProv" not "root\CIMv2".
The second problem is the return of ConnectServer. It should be a SWbemServices object, how can I get it through "Invoke" ?

Code: Select all

XIncludeFile "COMatePLUS.pbi"

Define.l dwValue
Define.l HKEY_LOCAL_MACHINE
Define Hostname$, login$, password$, strKeyPath$
Define.COMateObject objLocator, objService

Hostname$ = "x.x.x.x"
login$ = "Administrator"
password$ = "adminpass"
HKEY_LOCAL_MACHINE = $80000002
strKeyPath$ = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"


objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")

If objLocator
	Debug "objLocator ok"
	
	If objLocator\Invoke("ConnectServer(" + "'" + Hostname$ + "', '" + "root\default:StdRegProv" + "', '" + login$ + "', '" + password$ + "')") = #S_OK
		Debug COMate_GetLastErrorDescription()

; 		If objService
; 			Debug "objService ok"
	
			objService\Invoke("GetStringValue(" + Str(#HKEY_LOCAL_MACHINE) + ", '" + strKeyPath$ + "\VLC media player" + "', '" + "InstallLocation"  + "', " + Str(@dwValue) + " BYREF)")
			If COMate_GetLastErrorCode() = 0
				Debug PeekS(dwValue, -1, #PB_Unicode)   
				SysFreeString_(dwValue)
			Else
				Debug COMate_GetLastErrorDescription()
			EndIf
			
			objService\Release()
; 		Else
; 			Debug COMate_GetLastErrorDescription()
; 		EndIf
	Else
		Debug COMate_GetLastErrorDescription()
	EndIf
	objLocator\Release()
EndIf

Re: COMatePLus and request on remote host

Posted: Wed Dec 09, 2020 12:48 pm
by tatanas
Finally get it to work in 32bits... (got an error in 64bits) :

Code: Select all

XIncludeFile "COMatePLUS.pbi"

Define.s Hostname, login, password, strKeyPath, strValueName
Define.COMateObject objLocator, objService, objReg

Hostname = "PCNAME"
login = "Administrator"
password = "password"
hkey = #HKEY_LOCAL_MACHINE
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\VLC media player"
strValueName = "InstallLocation"

objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")

If objLocator
	Debug "objLocator ok"
	objService = objLocator\GetObjectProperty("ConnectServer(" + "'" + Hostname + "', '" + "root\default" + "', '" + login + "', '" + password + "')")
	If objService

		objReg = objService\GetObjectProperty("Get('StdRegProv')")
		If objReg

			If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + "', '" + strValueName  + "', " + Str(@dwValue) + " BYREF)") = #S_OK
				Debug PeekS(dwValue, -1, #PB_Unicode)   
				SysFreeString_(dwValue)
			Else
				Debug "invoke error : " + COMate_GetLastErrorDescription()
			EndIf
			objReg\Release()
		Else
			Debug "objReg erreur"
		EndIf
		objService\Release()
	Else
		Debug "objService erreur"
	EndIf
	objLocator\Release()
EndIf

Re: COMatePLus and request on remote host

Posted: Wed Dec 09, 2020 3:57 pm
by infratec
Your code still returns 0 for dwValue
And VLC is installed on the target PC.

Re: COMatePLus and request on remote host

Posted: Thu Dec 10, 2020 8:07 am
by tatanas
You get 0 even if vlc is installed ? It may be a problem with "SOFTWARE\WOW6432Node" path.
Try with another key like this one "SYSTEM\CurrentControlSet\services\NetBT\Parameters" and this value "NbProvider".
For me, it returns "_tcp" if compiled in 32bits.

Re: COMatePLus and request on remote host

Posted: Thu Dec 10, 2020 8:12 am
by infratec
You are right, this works.
So I will double check the path.

Re: COMatePLus and request on remote host

Posted: Thu Dec 10, 2020 9:25 am
by tatanas
New difficulty :
It's great to be able to read a key but I would like to enumerate the subkeys.
There is a method named Enumkey (https://docs.microsoft.com/en-us/previo ... stdregprov) but the last parameter is an array of strings passed by reference. I don't know how to declare and what kind of variable I have to pass to this method.
Any idea ?

Code: Select all

objReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath + "', " + Str(@myarr(0)) + " BYREF)")
EDIT : I found a snippet in VB where variables are declared as variant type. I know it is possible in Purebasic but I don't know how to use it.

Code: Select all

Option Explicit

Private Sub Form_Load()
    Dim Registry As Object, varKey As Variant, varKeys As Variant
    Set Registry = GetObject("winmgmts:\\.\root\default:StdRegProv")
    Registry.EnumKey &H80000001, "software\microsoft\windows\currentversion\unreadmail", varKeys
    For Each varKey In varKeys
        Debug.Print varKey
    Next
End Sub

Re: COMatePLus and request on remote host

Posted: Fri Dec 11, 2020 8:26 am
by tatanas
Another unsuccessful attempt (E_INVALIDARG error) :

Code: Select all

XIncludeFile "COMatePLUS.pbi"

Define.COMateObject oReg

hkey.i = #HKEY_LOCAL_MACHINE
strKeyPath.s = "SYSTEM\CurrentControlSet\Services"


oReg = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
If oReg

		If oReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath + "', " + Str(@safeArray) + " BYREF)") = #S_OK

			; Retrieve the strings from the safearray
			SafeArrayGetLBound_(safeArray, 1, @lBound)
			SafeArrayGetUBound_(safeArray, 1, @uBound)
			For i = lBound To uBound
				ret = SafeArrayGetElement_(safeArray, @i, @temp)
				If ret = #S_OK
					Debug temp
					If temp
						Debug PeekS(temp, -1, #PB_Unicode)
					EndIf
				Else
					If ret = #DISP_E_BADINDEX : Debug "erreur #DISP_E_BADINDEX" : EndIf
					If ret = #E_INVALIDARG : Debug "erreur #E_INVALIDARG" : EndIf
					If ret = #E_OUTOFMEMORY : Debug "erreur #E_OUTOFMEMORY" : EndIf
				EndIf
			Next
			
			;Destroy the array.
			SafeArrayDestroy_(safeArray)

		Else
			Debug "invoke EnumKey error"
			Debug COMate_GetLastErrorDescription()
		EndIf


;    If oReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + "', '" + strValueName + "', " + Str(@dwValue) + " BYREF)") = #S_OK
;       sValue$ = PeekS(dwValue, -1, #PB_Unicode)
;       SysFreeString_(dwValue)
;       Debug "registry value = " + sValue$
;    Else
;       Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
;    EndIf

   oReg\Release()
Else
   Debug "objet error"
EndIf

uBound variable seems to get the exact number of sub keys but I can't get the text associated.

Re: COMatePLus and request on remote host

Posted: Sat Dec 12, 2020 4:21 pm
by Justin
You have to pass the array as variant by reference, the array elements are variants too, this worked:

Code: Select all

XIncludeFile "COMatePLUS\COMatePLUS.pbi"


Define.COMateObject oReg
Define.i safeArray
Define.i hst
Define.s command
Define.VARIANT varNamesArr, varName

hkey.l = #HKEY_LOCAL_MACHINE
strKeyPath.s = "SYSTEM\CurrentControlSet\Services"


oReg = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
If oReg
	command = "EnumKey(" + Str(hkey) + ", '" + strKeyPath + "', " + Str(@varNamesArr) + " AS variant BYREF)"
      If oReg\Invoke(command) = #S_OK
				safeArray = varNamesArr\parray
         ; Retrieve the strings from the safearray
         SafeArrayGetLBound_(safeArray, 1, @lBound)
         SafeArrayGetUBound_(safeArray, 1, @uBound)
         For i = lBound To uBound
            ret = SafeArrayGetElement_(safeArray, @i, @varName)
            If ret = #S_OK
               If varName\bstrVal
                  Debug PeekS(varName\bstrVal, -1, #PB_Unicode)
                  SysFreeString_(varName\bstrVal)
                  varName\bstrVal = 0
               EndIf
            Else
               If ret = #DISP_E_BADINDEX : Debug "erreur #DISP_E_BADINDEX" : EndIf
               If ret = #E_INVALIDARG : Debug "erreur #E_INVALIDARG" : EndIf
               If ret = #E_OUTOFMEMORY : Debug "erreur #E_OUTOFMEMORY" : EndIf
            EndIf
         Next
         
         ;Destroy the array.
         SafeArrayDestroy_(safeArray)

      Else
         Debug "invoke EnumKey error"
         Debug COMate_GetLastErrorDescription()
      EndIf


;    If oReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + "', '" + strValueName + "', " + Str(@dwValue) + " BYREF)") = #S_OK
;       sValue$ = PeekS(dwValue, -1, #PB_Unicode)
;       SysFreeString_(dwValue)
;       Debug "registry value = " + sValue$
;    Else
;       Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
;    EndIf

   oReg\Release()
Else
   Debug "objet error"
EndIf

Re: COMatePLus and request on remote host

Posted: Mon Dec 14, 2020 8:12 am
by tatanas
Many thanks Justin !
I had tried with "As variant BYREF" too but didn't use "safeArray = varNamesArr\parray". I guess it was the problem.

Do you have an idea why it is not working when compiled in 64 bits ?

EDIT :

Ok, I'm able to list installed softwares but the automatic redirection 32/64bits always redirects my request to WOW6432Node because it's (for now) a 32bits compiled program.
I read this article https://docs.microsoft.com/en-us/window ... t-platform but I don't know how to apply to my code...

Code: Select all

XIncludeFile "COMatePlus\COMatePLUS.pbi"

Define.s Hostname, login, password, strKeyPath, strKeyPath32, strValueName
Define.COMateObject objLocator, objService, objReg, objCtx

hkey = #HKEY_LOCAL_MACHINE
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Uninstall\"
strKeyPath32 = "Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
strValueName = "DisplayName"


Define.COMateObject objReg
Define.i safeArray
Define.VARIANT varNamesArr, varName
Define.s subkeyname

objReg = COMate_GetObject("winmgmts:\\.\root\default:StdRegProv", "")
If objReg
	
	
	; first list from "Software\Microsoft\Windows\CurrentVersion\Uninstall\"
	If objReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath + "', " + Str(@varNamesArr) + " AS variant BYREF)") = #S_OK
		safeArray = varNamesArr\parray
		
		SafeArrayGetLBound_(safeArray, 1, @lBound)
		SafeArrayGetUBound_(safeArray, 1, @uBound)
		
		For i = lBound To uBound
			ret = SafeArrayGetElement_(safeArray, @i, @varName)
			If ret = #S_OK
				If varName\bstrVal
					subkeyname = PeekS(varName\bstrVal, -1, #PB_Unicode)
					SysFreeString_(varName\bstrVal)
					varName\bstrVal = 0
					
					
					If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + subkeyname + "', '" + strValueName + "', " + Str(@dwValue) + " BYREF)") = #S_OK
						If dwValue <> 1 ; NOT SURE ABOUT THAT TEST (if the key is not present, the pointer should be null but it seems its value is 1
							sValue$ = PeekS(dwValue, -1, #PB_Unicode)
							SysFreeString_(dwValue)
							Debug "registry value = " + sValue$
						Else
							; Debug "no key"
						EndIf
						dwValue = 0
					Else
						Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
					EndIf
					
				EndIf
			Else
				If ret = #DISP_E_BADINDEX : Debug "erreur #DISP_E_BADINDEX" : EndIf
				If ret = #E_INVALIDARG : Debug "erreur #E_INVALIDARG" : EndIf
				If ret = #E_OUTOFMEMORY : Debug "erreur #E_OUTOFMEMORY" : EndIf
			EndIf
		Next
		
		SafeArrayDestroy_(safeArray)
		
	Else
		Debug "invoke EnumKey error"
		Debug COMate_GetLastErrorDescription()
	EndIf
	
	
	Debug "-----------------------------------------------------------------------------"
	

	; second list from "Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
	If objReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath32 + "', " + Str(@varNamesArr) + " AS variant BYREF)") = #S_OK
		safeArray = varNamesArr\parray
		
		SafeArrayGetLBound_(safeArray, 1, @lBound)
		SafeArrayGetUBound_(safeArray, 1, @uBound)
		
		For i = lBound To uBound
			ret = SafeArrayGetElement_(safeArray, @i, @varName)
			If ret = #S_OK
				If varName\bstrVal
					subkeyname = PeekS(varName\bstrVal, -1, #PB_Unicode)
					SysFreeString_(varName\bstrVal)
					varName\bstrVal = 0
					
					If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath32 + subkeyname + "', '" + strValueName + "', " + Str(@dwValue) + " BYREF)") = #S_OK
						If dwValue <> 1 ; NOT SURE ABOUT THAT TEST (if the key is not present, the pointer should be null but it seems its value is 1
							sValue$ = PeekS(dwValue, -1, #PB_Unicode)
							SysFreeString_(dwValue)
							Debug "registry value = " + sValue$
						Else
							; Debug "no key"
						EndIf
						dwValue = 0
					Else
						Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
					EndIf
					
				EndIf
			Else
				If ret = #DISP_E_BADINDEX : Debug "erreur #DISP_E_BADINDEX" : EndIf
				If ret = #E_INVALIDARG : Debug "erreur #E_INVALIDARG" : EndIf
				If ret = #E_OUTOFMEMORY : Debug "erreur #E_OUTOFMEMORY" : EndIf
			EndIf
		Next
		
		SafeArrayDestroy_(safeArray)
		
	Else
		Debug "invoke EnumKey error"
		Debug COMate_GetLastErrorDescription()
	EndIf
	


	objReg\Release()
	
Else
	Debug "objet error"
EndIf

Re: COMatePLus and request on remote host

Posted: Mon Dec 14, 2020 11:21 am
by Justin
hkey is a long(4 bytes), you did not declare it and it defaults to integer wich is 8 bytes in 64 bit so it was wrong, also use variant by ref as the return type.

It is better to use always enableexplicit and declare all the correct types.

This worked in 64 bit, i removed the hostname, user, pwd to use the local machine:

Code: Select all

XIncludeFile "COMatePLUS\COMatePLUS.pbi"

EnableExplicit


Define.s Hostname, login, password, strKeyPath, strValueName
Define.COMateObject objLocator, objService, objReg
Define.VARIANT varValue
Define.i value
Define.l hkey

Hostname = ""
login = ""
password = ""
hkey = #HKEY_LOCAL_MACHINE
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\VLC media player"
strValueName = "InstallLocation"

objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")

If objLocator
   Debug "objLocator ok"
   objService = objLocator\GetObjectProperty("ConnectServer(" + "'" + Hostname + "', '" + "root\default" + "', '" + login + "', '" + password + "')")
   If objService

      objReg = objService\GetObjectProperty("Get('StdRegProv')")
      If objReg

         If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + "', '" + strValueName  + "', " + Str(@varValue) + " AS variant BYREF)") = #S_OK
            If varValue\vt = #VT_BSTR And varValue\bstrVal
            	Debug PeekS(varValue\bstrVal, -1, #PB_Unicode)   
            	SysFreeString_(varValue\bstrVal)
            EndIf 
         Else
            Debug "invoke error : " + COMate_GetLastErrorDescription()
         EndIf
         objReg\Release()
      Else
         Debug "objReg erreur"
      EndIf
      objService\Release()
   Else
      Debug "objService erreur"
   EndIf
   objLocator\Release()
EndIf

Re: COMatePLus and request on remote host

Posted: Mon Dec 14, 2020 12:54 pm
by tatanas
Thank you very much. It works like a charm !

Here is the code to list all installed sofwares from a remote computer :

Code: Select all

EnableExplicit

XIncludeFile "COMatePlus\COMatePLUS.pbi"

Define.s Hostname, login, password, strKeyPath, strKeyPath32, strValueName
Define.COMateObject objLocator, objService, objReg
Define hkey.l
Define.i lBound, uBound, i, ret

Hostname = "RemotePC"
login = "Administrator"
password = "password"
hkey = #HKEY_LOCAL_MACHINE
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Uninstall\"
strKeyPath32 = "Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
strValueName = "DisplayName"
; "UninstallString", QuietUninstallString

Define.COMateObject oReg
Define.i safeArray
Define.VARIANT varNamesArr, varName, dwValue
Define.s subkeyname


objLocator = COMate_CreateObject("WbemScripting.SWbemLocator")
If objLocator

   objService = objLocator\GetObjectProperty("ConnectServer(" + "'" + Hostname + "', '" + "root\default" + "', '" + login + "', '" + password + "')")
   If objService

      objReg = objService\GetObjectProperty("Get('StdRegProv')")
      If objReg

	
			If objReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath + "', " + Str(@varNamesArr) + " AS variant BYREF)") = #S_OK
				safeArray = varNamesArr\parray
		
				SafeArrayGetLBound_(safeArray, 1, @lBound)
				SafeArrayGetUBound_(safeArray, 1, @uBound)
		
				For i = lBound To uBound
					ret = SafeArrayGetElement_(safeArray, @i, @varName)
					If ret = #S_OK
						If varName\bstrVal
							subkeyname = PeekS(varName\bstrVal, -1, #PB_Unicode)
							SysFreeString_(varName\bstrVal)
							varName\bstrVal = 0

							If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath + subkeyname + "', '" + strValueName + "', " + Str(@dwValue) + " AS variant BYREF)") = #S_OK
				            If dwValue\vt = #VT_BSTR And dwValue\bstrVal ; 8 = #VT_BSTR, 1 = #VT_NULL
				               Debug PeekS(dwValue\bstrVal, -1, #PB_Unicode)   
				               SysFreeString_(dwValue\bstrVal)
				            EndIf
							Else
								Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
							EndIf

						EndIf
					Else
						If ret = #DISP_E_BADINDEX : Debug "error #DISP_E_BADINDEX" : EndIf
						If ret = #E_INVALIDARG : Debug "error #E_INVALIDARG" : EndIf
						If ret = #E_OUTOFMEMORY : Debug "error #E_OUTOFMEMORY" : EndIf
					EndIf
				Next
				
				SafeArrayDestroy_(safeArray)
				
			Else
				Debug "invoke EnumKey error"
				Debug COMate_GetLastErrorDescription()
			EndIf

Debug "-----------------------------------------------------------------------------"

			If objReg\Invoke("EnumKey(" + Str(hkey) + ", '" + strKeyPath32 + "', " + Str(@varNamesArr) + " AS variant BYREF)") = #S_OK
				safeArray = varNamesArr\parray
		
				SafeArrayGetLBound_(safeArray, 1, @lBound)
				SafeArrayGetUBound_(safeArray, 1, @uBound)
		
				For i = lBound To uBound
					ret = SafeArrayGetElement_(safeArray, @i, @varName)
					If ret = #S_OK
						If varName\bstrVal
							subkeyname = PeekS(varName\bstrVal, -1, #PB_Unicode)
							SysFreeString_(varName\bstrVal)
							varName\bstrVal = 0

							If objReg\Invoke("GetStringValue(" + Str(hkey) + ", '" + strKeyPath32 + subkeyname + "', '" + strValueName + "', " + Str(@dwValue) + " AS variant BYREF)") = #S_OK
				            If dwValue\vt = #VT_BSTR And dwValue\bstrVal ; 8 = #VT_BSTR, 1 = #VT_NULL
				               Debug PeekS(dwValue\bstrVal, -1, #PB_Unicode)   
				               SysFreeString_(dwValue\bstrVal)
				            EndIf
							Else
								Debug "GetStringValue error : " + COMate_GetLastErrorDescription()
							EndIf

						EndIf
					Else
						If ret = #DISP_E_BADINDEX : Debug "error #DISP_E_BADINDEX" : EndIf
						If ret = #E_INVALIDARG : Debug "error #E_INVALIDARG" : EndIf
						If ret = #E_OUTOFMEMORY : Debug "error #E_OUTOFMEMORY" : EndIf
					EndIf
				Next
				
				SafeArrayDestroy_(safeArray)
				
			Else
				Debug "invoke EnumKey error"
				Debug COMate_GetLastErrorDescription()
			EndIf



			objReg\Release()

		Else
			Debug "objReg error"
		EndIf

	Else
		Debug "objService error"
	EndIf
Else
	Debug "objLocator error"
EndIf