如何在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
相关产品推荐
相关产品推荐

