Search code examples
windowsperlregistry

Reading from Windows registry in Perl


I'm trying to get the installation location of a program without checking a series of hard-coded paths, hoping to get it from the path stored in the Windows registry, but I get empty return values or error messages when I try to read the required key values.

I've read about Parsing windows registry using Perl, and I think I've incorporated all of the answers in my script, but I always get an empty result when I try reading a registry value, even when running from an elevated command-prompt to make sure the script has admin rights.

I first tried using Win32::TieRegistry, and as mentioned the returned value is empty, so then I tried just shelling out and using reg query, but that gives an error about the key not being found. Running the same reg query command outside of Perl successfully reads the key. What am I doing wrong?


Update: The root cause (pointed out in the couple answers I received) was trying to access a 64-bit registry view from a 32-bit Perl; by default, any 32-bit application in Windows is redirected to HKLM\Software\WOW6432Node when trying to access HKLM\Software keys, so I needed to find a way to avoid that since my key of interest does not exist in that location.


Perl script:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;
use Win32::TieRegistry (Delimiter => '/');

my $mykey = $Registry->{'HKEY_LOCAL_MACHINE/Software/ikv++ technologies ag/medini unite (x64)'};
my $mykeyval = $mykey->{'/Path'};

print " value=$mykeyval\n";

print Dumper $mykey;

my $sysCmd =`reg query "HKLM\\Software\\ikv++ technologies ag\\medini unite (x64)" /v Path`;
print " sysCmd=$sysCmd\n";

Output:

C:\Users\username\AppData\Local\Temp>perl test_reg_read.pl
Use of uninitialized value in concatenation (.) or string at test_reg_read.pl line 9.
 value=
$VAR1 = {};
ERROR: The system was unable to find the specified registry key or value.
 sysCmd=

Manual reg query command outside of Perl:

C:\Users\username\AppData\Local\Temp>reg query "HKLM\Software\ikv++ technologies ag\medini unite (x64)" /v Path

HKEY_LOCAL_MACHINE\Software\ikv++ technologies ag\medini unite (x64)
    Path    REG_SZ    C:\Program Files\ikv++ technologies ag\mediniUnite

Note, all this is running from an elevated command prompt for now; once it works there I was going to experiment with whether it is necessary.


Solution

  • As mentioned in Srgrn's answer, the problem stems from my Perl being 32-bit, running on a 64-bit system. Windows automatically redirects 32-bit processes to the Wow6432Node registry node when accessing HKLM\Software unless specifically told to use 64-bit or 32-bit registry views with an access mode flag.

    I could specify access with the old Win32API::Registry, but instead I chose to specify the access mode with TieRegistry's object functions:

    #!/usr/bin/perl -w
    use strict;
    use Win32::TieRegistry (Delimiter => '/');
    
    print "explicitly access 64-bit location:\n";
    my $mykey = new Win32::TieRegistry
      'HKEY_LOCAL_MACHINE/Software/ikv++ technologies ag/medini unite (x64)',
      { Access=>Win32::TieRegistry::KEY_READ()|0x0100, Delimiter=>'/' };
    my $mykeyval = $mykey->GetValue('Path');
    
    print " value=$mykeyval\n";
    
    print " Values are: ";
    print join(', ', $mykey->ValueNames);
    print "\n";
    
    print "explicitly access 32-bit location:\n";
    $mykey = new Win32::TieRegistry
      'HKEY_LOCAL_MACHINE/Software/ikv++ technologies ag/medini unite',
      { Access=>Win32::TieRegistry::KEY_READ()|0x0200, Delimiter=>'/' };
    $mykeyval = $mykey->GetValue('Path');
    
    print " value=$mykeyval\n";
    
    print " Values are: ";
    print join(', ', $mykey->ValueNames);
    print "\n";
    

    This gives results as expected for both the 32-bit and 64-bit keys, and additionally should work the same way in both 32-bit and 64-bit Perl (in theory anyway).

    Note: I needed to specify the full namespace for the KEY_READ() function in my version of Perl to prevent compile errors, and I'm not certain whether there are named values for the 0x0100 and 0x0200 constants, so this may be rougher than needed on some systems, but it seems to work great for me!


    Note: this also allows you to read the right version of a key with the same name in either 32-bit and 64-bit, so this answer is duplicated there.