Reading registry on Windows 64

Archives Forums/Win32 Discussion/Reading registry on Windows 64

Fielder(Posted 2015) [#1]
Global valorestr:String

temp_proc:Tprocess = CreateProcess("c:\windows\sysnative\reg.exe QUERY "+Chr(34)+"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\layers"+Chr(34),1)
ttt=0
While temp_proc.status()
	If temp_proc.pipe.ReadAvail() 
				valorestr= temp_proc.pipe.ReadLine() 
				If valorestr
					If Len(valorestr)>5 
						Print ttt+" "+valorestr
						ttt:+1
					EndIf
				EndIf
	EndIf
Wend


try to add these keys to registry... (just to try to read something)

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers]
"C:\\Program Files (x86)\\Jasc Software Inc\\Paint Shop Pro 9\\Paint Shop Pro 9.exe"="FaultTolerantHeap"
"C:\\BlitzMax\\tmp\\untitled1.debug.exe"="FaultTolerantHeap"
"C:\\Program Files (x86)\\test\\Paint Shop Pro 9\\test.exe"="AAAAAAAAA"


i received....
Semetimes.. NOTHING.. (no reading)
Other times (always) .. just first string... but after that i have a

EXCEPTION_ACCESS_VIOLATION

and debug mode doesn't return to code to explain what appened...

I'currently using windows 10 but i've the same issues on other Windows 8 systems. (always 64 bit)

P.S. the code is working (try to remove "LAYERS" and read: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags .. )
everything is working but strangely the reading stops at CUSTOM key (on DEBUG MODE) ... no reaching LAYERS.. but can read everything if on RELEASE MODE.


xlsior(Posted 2015) [#2]
there's some funky redirecting going on for 32-bit apps (like Blitzmax executables) under 64-bit windows.

e.g. instead of:

HKLM\Software\Microsoft\WindowsNT\CurrentVersion

it gets redirected to:

HKLM\Software\WOW6432Node\Microsoft\WindowsNT\CurrentVersion

If you get inconsistent results, perhaps check for 64-bit windows and if you detect it, attempt reading the WOW6432Node contents instead of the 64-bit branches.


Fielder(Posted 2015) [#3]
i'm reading the SYSNATIVE (64 bit real key) using the "sysnative\reg.exe"
(if i use the SYSTEM32\reg.exe" i will read the WOW content)
this is not the issue.. i thing this is a Blitzmax Bug... on DEBUG MODE and on RELEASE MODE there are 2 different results (and on LAYERS KEY.. BOTH CRASHES WITHOUT EXPLANATIONS)

if i change the code with:

Local temp_proc:Tprocess = CreateProcess("c:\windows\sysnative\reg.exe QUERY "+Chr(34)+"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\layers"+Chr(34),1)
While temp_proc.status()
	If temp_proc.pipe.ReadAvail() Print "pipe: " + temp_proc.pipe.ReadString(20)
Wend

i can read everything (last line is cutted because is out of 20 chars) but with a 20 chars layout... (and no final characters)

and yep.. i tried to read a single char (a do a join of chars) .. but reading stops at first line.

Executing:untitled1.exe
pipe: 
HKEY_LOCAL_MACHINE
pipe: \SOFTWARE\Microsoft\
pipe: Windows NT\CurrentVe
pipe: rsion\AppCompatFlags
pipe: \layers
    C:\Prog
pipe: ram Files (x86)\Jasc
pipe:  Software Inc\Paint 
pipe: Shop Pro 9\Paint Sho
pipe: p Pro 9.exe    REG_S
pipe: Z    FaultTolerantHe
pipe: ap
    C:\Program F
pipe: iles (x86)\test\Pain
pipe: t Shop Pro 9\test.ex
pipe: e    REG_SZ    CAZZO
pipe: ZLOOOAA
    C:\Blit
pipe: zMax\tmp\untitled1.e
pipe: xe    REG_SZ    Faul

Error reading from stream

Process complete


ANOTHER STRANGE EXAMPLE.... if i add a delay(1) on the code... i can't receive ANYTHING.

Local temp_proc:Tprocess = CreateProcess("c:\windows\sysnative\reg.exe QUERY "+Chr(34)+"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags"+Chr(34),1)
While temp_proc.status()
	If temp_proc.pipe.ReadAvail() 
	valorestr$= temp_proc.pipe.ReadLine()
	Delay(1)   'this delay do the thing... NO RESULT JUST 3 EMPTY STRINGS.
	Print VALORESTR$
	EndIf
Wend


this is a timing issue i think... the stream from the createprocess if too fast... and can be lost on faster machines... (o something like that)


xlsior(Posted 2015) [#4]
FWIW, it's also possible to query the registry directly from within blitzmax, without having to resort to external processes.

There's examples in the code archives and other old forum posts, e.g.:

http://www.blitzbasic.com/codearcs/codearcs.php?code=1991

Generally it seems like a bad idea to hardcode paths to external dependencies like you did -- for example, c:\windows\sysnative\reg.exe doesn't exist on my 64 bit Windows 10 installation. it has c:\windows\system32\reg.exe (64 bit) and c:\windows\syswow64\reg.exe (presumably 32 bit)

Either way, hardcoding the location is bound to cause issues, especially when factoring in non-English windows versions which often have localized folder names that may differ from the US edition.


Fielder(Posted 2015) [#5]
Xlsior, the SYSNATIVE folder was introduced by microsoft to add compatibility (like the KEY_WOW64_64KEY flag) to 64bit to older 32 bit applications... the SYSNATIVE folder is redirected to SYSTEM32 (and not syswow64) directly from the OS (windows VISTA/7/8/8.1/10)

you can try to add an executable to system32 .. and launch it using the sysnative virtual folder.

FOR THE REGISTRY CODE POSTED:

i've used the standard API for the 32 Bit reading (and works fine) ..

for 64 bit use of that APIs (for some REGISTRY KEYS ENUMERATIONS --> yellow folders on left of regedit) i've added the:

KEY_WOW64_64KEY = 0x0100

parameter but for NOW ... i'n not able to read VALUES on RIGHT part of regedit
(like these layers values)

if anyone can fix the ENUMERATEVALUES Function with KEY_WOW64_64KEY i'll be very thankful :)

(i've tried but i've just ported to 64 bit the ENUMERATEKEYS)


xlsior(Posted 2015) [#6]
Xlsior, the SYSNATIVE folder was introduced by microsoft to add compatibility (like the KEY_WOW64_64KEY flag) to 64bit to older 32 bit applications... the SYSNATIVE folder is redirected to SYSTEM32 (and not syswow64) directly from the OS (windows VISTA/7/8/8.1/10)


Ok, hadn't heard of that one --

When calling it through blitzmax it appears that that sysnative path does resolve, when I try to invoke c:\windows\sysnative\reg.exe from the windows command prompt it does not, returns a "The system cannot find the path specified"


Fielder(Posted 2015) [#7]
the only way seems to "port"

   Function RegEnumValue:Long(hKey:Int,idx:Int,ValueName:Byte Ptr,NameSize:Byte Ptr,Reserved:Int,nType:Byte Ptr,ValueBytes:Byte Ptr,ValueSize:Byte Ptr)="RegEnumValueA" 


to the 64 BIT version including the "KEY_WOW64_64KEY = 0x0100"

on this 32 bit function ... WHERE I NEED TO PLACE THE FLAG???
' enumerates the keys contained in the passed subkey And returns them as a delimited String in
' the format: KEY=VALUE|KEY=VALUE|KEY=VALUE
Function reg_enumvalues:String(RegKey:Int,SubKey:String,delim:String="|",types=False)
   Local cRetVal:String="",key:String="",val:String=""
   Local keybank:TBank=CreateBank(100),keybanksize:TBank=CreateBank(4),valbank:TBank=CreateBank(100),valbanksize:TBank=CreateBank(4),typebank:TBank=CreateBank(4)
   Local char:Int=0,nIdx:Int=0,nType:Int=0

   ' open the key
   Local hKey=reg_openkey(RegKey,SubKey:String)
   If hKey<>-1   
   
      ' read in the values
      Repeat
         ' init the banks
         PokeInt(typebank,0,0)
         PokeInt(valbanksize,0,100)
         PokeInt(keybanksize,0,100)
      
         ' clear out the temp values
         key:String=""
         val:String=""
         
         If RegEnumValue(hKey,nIdx,BankBuf(keybank),BankBuf(keybanksize),0,BankBuf(typebank),BankBuf(valbank),BankBuf(valbanksize))<>REG_ERROR_EOF
            nType=PeekInt(typebank,0)
            
            ' tack on the delimiter
            If cRetVal:String<>""
               cRetVal:String=cRetVal:String+delim:String
            EndIf

            ' build the key name
            For char=0 To PeekInt(keybanksize,0)-1
               If PeekByte(keybank,char)=0 Then Exit
               key:String=key:String+Chr(PeekByte(keybank,char))
            Next

            Select nType
               ' read in a String Or binary value
               Case REG_SZ, REG_BINARY
                  ' build the value
                  For char=0 To PeekInt(valbanksize,0)-1
                     If PeekByte(valbank,char)=0 Then Exit
                     val:String=val:String+Chr(PeekByte(valbank,char))
                  Next
               ' read in an integer
               Case REG_DWORD
                  val:String=PeekInt(valbank,0)                  
            End Select

            If types
               cRetVal:String=(cRetVal:String+PeekInt(typebank,0)+"'"+key:String+"="+val:String)
            Else
               cRetVal:String=(cRetVal:String+key:String+"="+val:String)
            EndIf
         Else
            Exit
         EndIf         
         
         nIdx=nIdx+1
      Forever
      reg_closekey(hKey)
   EndIf
   
   typebank = Null
   valbank = Null
   valbanksize = Null
   keybank = Null
   keybanksize = Null
   
   Return cRetVal:String
End Function