You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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中,优先级建议为:

  1. 先尝试从$PATH中查找程序
  2. 若找不到,调用get-app-path()读取注册表App Paths项
  3. (可选)保留修复后的AssocQueryStringA()调用作为补充

内容的提问来源于stack exchange,提问作者StevieD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 18:57:43