Windows 通过快捷方式获取软件所在路径

Windows 快捷方式种类

Windows 上包含有两种快捷方式:

  1. Non-advertised shortcut (Standard Shortcuts)
  2. Advertised shortcut (Windows Installer Shortcuts)

Non-advertised shortcut

Non-advertised shortcut.jpg

该快捷方式中明显的位置在于存在有 Target。并且 Target 就是运行程序所在位置。

通过 IShellLink 可以读取出该 Target 值。

Advertised shortcut

Advertised shortcut.jpg

该快捷方式中不存在 Target。并且指向的也不是特定的文件。它由 Windows Installer 创建。支持高级功能,修复等功能。

通过 IShellLink 读取出的 Target 值是错误的。类似这种:C:\Windows\Installer\{A6D7B449-8F4F-4FA9-B80A-101345AA998A}\***.ico

如何通过快捷方式获取到准确的安装路径

通过 MSI 的 API,我们找到了 MsiGetShortcutTarget 接口。该接口用于获取 Advertised shortcut 的路径。当返回为空的时候,快捷方式为 Non-advertised shortcut。详细解析参考 Microsoft

那么读取路径的思路就来了。

  1. 通过 MsiGetShortcutTarget 读取路径,如果有路径,则快捷方式为 Advertised shortcut,直接返回。
  2. 如果不存在路径,说明该快捷方式为 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

Windows系统没有目标位置的快捷方式及其目标文件获取

你可能感兴趣的:(Windows 通过快捷方式获取软件所在路径)