如何通过Windows API获取注册表App Paths中的应用程序路径?
在Raku中正确获取Windows下非PATH中的程序路径(以WordPad为例)
问题核心
wordpad.exe不在系统$PATH中,现有代码通过AssocQueryStringA()无法定位它,但该程序的路径明确存在于注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WORDPAD.EXE的默认值中。
为什么AssocQueryStringA()会失败?
AssocQueryStringA()的核心作用是查询文件扩展名关联的命令行,而非直接读取App Paths注册表项。它的逻辑依赖文件扩展名与程序的绑定关系,对于未配置关联扩展名的程序,该函数会返回失败。
解决方案1:直接读取App Paths注册表项
Windows的App Paths注册表项是专门存储程序路径的预留位置,即便程序不在$PATH中,也能通过读取这里的值获取路径。以下是用Raku+NativeCall实现的代码:
use NativeCall; # Windows注册表API常量定义 constant KEY_READ = 0x20019; constant REG_SZ = 1; constant HKEY_LOCAL_MACHINE = 0x80000002; # 封装注册表操作API sub RegOpenKeyExA( int32, str, uint32, uint32, int32 is rw ) returns int32 is native('advapi32.dll') { * } sub RegQueryValueExA( int32, str, int32 is rw, int32 is rw, str is rw, uint32 is rw ) returns int32 is native('advapi32.dll') { * } sub RegCloseKey(int32) returns int32 is native('advapi32.dll') { * } sub ExpandEnvironmentStringsA(str, str is rw, uint32) returns uint32 is native('kernel32.dll') { * } sub get-app-path(Str $exe-name) returns Str { my int32 $hkey = 0; my $reg-path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$exe-name"; # 打开目标注册表项 my $status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, $reg-path, 0, KEY_READ, $hkey); return '' if $status != 0; # 查询默认值(空字符串代表默认值) my int32 $type = 0; my uint32 $buf-size = 1024; my str $buf = buf8.new($buf-size).decode('UTF-8'); $status = RegQueryValueExA($hkey, '', 0, $type, $buf, $buf-size); RegCloseKey($hkey); return '' if $status != 0 || $type != REG_SZ; # 解析环境变量(如%ProgramFiles%) my str $expanded = ''; $buf-size = 1024; $buf-size = ExpandEnvironmentStringsA($buf, $expanded, $buf-size); return $expanded; } # 使用示例 say get-app-path('WORDPAD.EXE'); # 输出示例:C:\Program Files\Windows NT\Accessories\WORDPAD.EXE
解决方案2:修复AssocQueryStringA()的调用逻辑
若仍想使用AssocQueryStringA(),需指定ASSOCF_OPEN_BYEXENAME标志,让函数通过程序名而非扩展名查找路径,示例代码如下:
use NativeCall; constant ASSOCF_OPEN_BYEXENAME = 0x00000020; constant ASSOCSTR_EXECUTABLE = 2; sub AssocQueryStringA( uint32, uint32, str, str, str is rw, uint32 is rw ) returns int32 is native('shlwapi.dll') { * } sub get-via-assoc(Str $exe-name) returns Str { my uint32 $buf-size = 1024; my str $buf = buf8.new($buf-size).decode('UTF-8'); my $status = AssocQueryStringA( ASSOCF_OPEN_BYEXENAME, ASSOCSTR_EXECUTABLE, $exe-name, '', $buf, $buf-size ); return $status == 0 ?? $buf !! ''; } # 使用示例 say get-via-assoc('WORDPAD.EXE');
整合到现有模块
你可以将上述逻辑整合到File::Which::Win32.pm6中,优先级建议为:
- 先尝试从
$PATH中查找程序 - 若找不到,调用
get-app-path()读取注册表App Paths项 - (可选)保留修复后的
AssocQueryStringA()调用作为补充
内容的提问来源于stack exchange,提问作者StevieD
相关产品推荐
相关产品推荐

