3

I'm trying to update the Raku code in this file which tries to find the path to commands/apps on a Windows machine.

I have a Windows 11 machine running inside VirtualBox. wordpad.exe cannot be run from the command line because it is not in the $PATH, and so the code will also fail to detect wordpad.exe in the path. As a fallback, the Raku code attempts to find the executable in the registry using the Windows API via the NativeCall module. However, this is also failing to find the wordpad.exe command.

Looking at the registry, there is a WORDPAD.EXE entry in the registry at Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WORDPAD.EXE. It shows a path of "%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE".

I did a little googling, and this is how paths to some executables are stored. However, I was not able to figure out how to use the Windows API to find and extract the path to the app. I'm also unclear as to why the current Raku code uses the AssocQueryStringA() function.

Some hints are dropped in this SO answer.

But, never having done Windows API programming, and only knowing basic C programming, I'm a little at a loss.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
StevieD
  • 6,925
  • 2
  • 25
  • 45
  • 1
    Sounds like you will have to use `NativeCall` to call Registry API functions like [`RegOpenKeyExA()`](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa) and [`RegQueryValueExA()`](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa), instead of using `AssocQueryStringA()` – Remy Lebeau Aug 31 '22 at 22:16
  • You can use [IQueryAssociations](https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-assocquerystringa#remarks) for more reliable function or `RegistryAPI` as @RemyLebeau recommended. *AssocQueryStringA* return ERROR_NO_ASSOCIATION, No application is associated with the specified file for this operation, for *mspaint* in my Windows11 machine. Perhaps [Association Arrays](https://learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray) is involved. – YangXiaoPo-MSFT Sep 01 '22 at 03:20
  • @RemyLebeau I looked at `RegOpenKeyExA()` and I see no obvious way to get a handle for the HKEY argument. – StevieD Sep 01 '22 at 16:17
  • @StevieDthen then you didn't read the docs well enough. The API predefines an HKEY for the HKEY_LOCAL_MACHINE hive, which you can use to open a new HKEY to the WordPad subkey, then you can read that key's `(Default)` value to get the path you want, and then close the HKEY you opened. – Remy Lebeau Sep 01 '22 at 17:45
  • Yes, I saw those predefinitions. But I don't see any function that returns a handle. – StevieD Sep 01 '22 at 17:53
  • Does [Enumerating Registry Subkeys](https://learn.microsoft.com/en-us/windows/win32/sysinfo/enumerating-registry-subkeys) work for you? – YangXiaoPo-MSFT Sep 02 '22 at 05:21
  • No, I still don't see how to create an data of type HKEY (a handle) from that example. – StevieD Sep 03 '22 at 14:31

1 Answers1

3

I was able to piece together a solution using most of this code which needed a few adjustments to get working properly. Here is the code that works:

use NativeCall;

constant BYTE    := uint8;
constant WCHAR   := uint16;   
constant DWORD   := int32;    
constant REGSAM  := int32;
constant WCHARS  := CArray[WCHAR];
constant BYTES   := CArray[BYTE];

constant HKEY_LOCAL_MACHINE = 0x80000002;
constant KEY_QUERY_VALUE   = 1;
constant ERROR_SUCCESS     = 0; # Yeah, I know. The Win-Api uses 0 for success and other values to i
+ndicate errors

sub RegOpenKeyExW( DWORD, WCHARS, DWORD, REGSAM, DWORD is rw) is native("Kernel32.dll") returns DWOR
+D { * };


sub RegQueryValueExW( DWORD, WCHARS, DWORD is rw, DWORD is rw, BYTE is rw, DWORD is rw) is native("K
+ernel32.dll") returns DWORD { * };


my $key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths';

my DWORD $hkey;
my $length = 1024;

sub wstr( Str $str ) returns WCHARS {
    my $return = CArray[WCHAR].new( $str.encode.list );
    $return[$return.elems] = 0;
    return $return;
}

my $h-key = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wstr($key), 0, KEY_QUERY_VALUE, $hkey);
say $hkey;

The key to figuring this out was that the HKEY_LOCAL_MACHINE constant has a hex value of 0x80000002 which can be used to getting the hkey value for other keys.

With this stumbling block overcome, it should now be a simple matter to retrieve data about the subkeys which contain the paths to the application in the folder using other Windows API functions.

StevieD
  • 6,925
  • 2
  • 25
  • 45