Windows 快捷方式种类
Windows 上包含有两种快捷方式:
- Non-advertised shortcut (Standard Shortcuts)
- Advertised shortcut (Windows Installer Shortcuts)
Non-advertised shortcut
该快捷方式中明显的位置在于存在有 Target。并且 Target 就是运行程序所在位置。
通过 IShellLink 可以读取出该 Target 值。
Advertised shortcut
该快捷方式中不存在 Target。并且指向的也不是特定的文件。它由 Windows Installer 创建。支持高级功能,修复等功能。
通过 IShellLink 读取出的 Target 值是错误的。类似这种:C:\Windows\Installer\{A6D7B449-8F4F-4FA9-B80A-101345AA998A}\***.ico
如何通过快捷方式获取到准确的安装路径
通过 MSI 的 API,我们找到了 MsiGetShortcutTarget
接口。该接口用于获取 Advertised shortcut
的路径。当返回为空的时候,快捷方式为 Non-advertised shortcut
。详细解析参考 Microsoft
那么读取路径的思路就来了。
- 通过
MsiGetShortcutTarget
读取路径,如果有路径,则快捷方式为Advertised shortcut
,直接返回。 - 如果不存在路径,说明该快捷方式为
Non-advertised shortcut
, 使用IShellLink
读取快捷方式的 Target 即可。
附代码如下:
HRESULT ResolveShortcut(/*in*/ std::wstring linkFilePath, /*out*/ LPTSTR exePath) {
HRESULT hRes = E_FAIL;
IShellLink* psl = nullptr;
TCHAR szPath[MAX_PATH] = { 0 };
WIN32_FIND_DATA wfd;
exePath[0] = '\0';
CHAR szProductCode[MAX_PATH] = { 0 };
CHAR szComponentCode[MAX_PATH] = { 0 };
// https://docs.microsoft.com/en-us/windows/win32/api/msi/nf-msi-msigetshortcuttargetw
DWORD errorCode = MsiGetShortcutTarget(ws2s(linkFilePath).c_str(), szProductCode, nullptr, szComponentCode);
if (S_OK == errorCode) {
CHAR szPathSpecial[MAX_PATH] = { 0 };
DWORD max_size = MAX_PATH;
INSTALLSTATE nState = MsiGetComponentPath(szProductCode, szComponentCode, szPathSpecial, &max_size);
if (nState == INSTALLSTATE_LOCAL || nState == INSTALLSTATE_SOURCE) {
lstrcpyn(exePath, szPathSpecial, MAX_PATH);
return ERROR_SUCCESS;
}
}
// Get a pointer to the IShellLink interface
hRes = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&psl);
if (SUCCEEDED(hRes)) {
// Get a pointer to the IPersistFile interface
IPersistFile* ppf = nullptr;
hRes = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (!SUCCEEDED(hRes)) {
return hRes;
}
// Open the shortcut file and initialize it from its contents
hRes = ppf->Load(linkFilePath.c_str(), STGM_READ);
if (SUCCEEDED(hRes)) {
// 尝试获取到快捷方式的 target 路径。即使目标被删除或者移动。
hRes = psl->Resolve(nullptr, SLR_UPDATE | SLR_NO_UI);
if (SUCCEEDED(hRes)) {
// 获取 Target 路径
hRes = psl->GetPath(szPath, MAX_PATH, &wfd, SLGP_RAWPATH);
if (FAILED(hRes)) {
ppf->Release();
psl->Release();
return hRes;
}
std::string dest = szPath;
int index = dest.find_first_of('\\');
if (index > 2) {
std::string sub = dest.substr(1, index - 2);
std::string path = getenv(sub.c_str());
if (path.length() > 0) {
if (path[path.length() - 1] == '\\') {
path = path.substr(0, path.length() - 1);
}
dest.replace(0, index, path);
}
}
strcpy(szPath, dest.c_str());
lstrcpyn(exePath, szPath, MAX_PATH);
}
}
ppf->Release();
psl->Release();
}
return hRes;
}
参考资料
Advertised shortcuts vs. non-advertised shortcuts in windows setup project