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

如何在C#/Win32 API中解析Windows应用执行别名的目标路径

解析Windows应用执行别名(AppExeCLink)获取目标路径

当你通过微软商店安装Microsoft Windows Terminal时,会在~\AppData\Local\Microsoft\WindowsApps\目录下生成一个0字节的wt.exe,这就是Windows的应用执行别名(AppExeCLink)——它不是传统的文件系统符号链接,而是在CreateProcess API层面进行解析的特殊别名。

从PowerShell的输出能直观看到它的特性:

❯ dir ~\AppData\Local\Microsoft\WindowsApps\wt.exe
Mode  Name
----  ----
la--- wt.exe -> C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.0.1401.0_x64__8wekyb3d8bbwe\WindowsTerminal.exe
❯ Get-Item .\wt.exe | fl
Name     : wt.exe
Length   : 0
LinkType : AppExeCLink
Target   : C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.0.1401.0_x64__8wekyb3d8bbwe\WindowsTerminal.exe

目前这类别名的官方文档比较难找,搜索PS AppExeCLink也很难得到有效信息。如果要在C#应用中快速解析这类别名获取目标文件,推荐直接调用Win32 API的非托管实现,比WMI或者体积较大的NuGet包高效得多。

C#实现方案(基于Win32 API)

我们可以使用GetFinalPathNameByHandleW API来解析这类特殊别名。以下是完整的代码示例:

using System;
using System.IO;
using System.Runtime.InteropServices;

public static class AppExeCLinkResolver
{
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern uint GetFinalPathNameByHandleW(IntPtr hFile, [Out] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr CreateFileW(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);

    private const uint GENERIC_READ = 0x80000000;
    private const uint FILE_SHARE_READ = 0x00000001;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
    private const uint VOLUME_NAME_NT = 0x00000001;

    public static string ResolveAppExeCLink(string aliasPath)
    {
        IntPtr hFile = CreateFileW(aliasPath, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
        if (hFile == IntPtr.Zero || hFile == (IntPtr)(-1))
        {
            throw new IOException("Failed to open the alias file", Marshal.GetLastWin32Error());
        }

        try
        {
            StringBuilder sb = new StringBuilder(1024);
            uint result = GetFinalPathNameByHandleW(hFile, sb, (uint)sb.Capacity, VOLUME_NAME_NT);
            if (result == 0)
            {
                throw new IOException("Failed to get final path", Marshal.GetLastWin32Error());
            }

            // 去掉路径开头的"\\?\"前缀
            string finalPath = sb.ToString();
            if (finalPath.StartsWith(@"\\?\"))
            {
                finalPath = finalPath.Substring(4);
            }

            return finalPath;
        }
        finally
        {
            CloseHandle(hFile);
        }
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        string aliasPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\WindowsApps\wt.exe");
        try
        {
            string targetPath = AppExeCLinkResolver.ResolveAppExeCLink(aliasPath);
            Console.WriteLine($"解析得到的目标路径:{targetPath}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"解析失败:{ex.Message}");
        }
    }
}

代码说明

  • CreateFileW:打开别名文件,需要指定FILE_FLAG_BACKUP_SEMANTICS标志来处理这类特殊文件对象
  • GetFinalPathNameByHandleW:通过文件句柄获取最终的目标路径,使用VOLUME_NAME_NT参数确保返回完整的NT格式路径
  • 最后处理掉路径开头的\\?\前缀,得到可读性更好的标准路径

这种方式直接调用底层Win32 API,性能高效,不需要依赖任何额外的库,完美满足快速解析的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 07:12:33