InternetShortcut 对象 IPersistFile::Save 失败 E_FAIL (0x80004005)

InternetShortcut object IPersistFile::Save fails with E_FAIL (0x80004005)

这个让我卡了太久,所以我自己回答这个问题。为讽刺的语气道歉!

尝试使用官方 API 创建 .url 文件只有 the following documentation:

  • Create an instance of the Internet shortcut object with CoCreateInstance, using a CLSID of CLSID_InternetShortcut.
  • Use the IUniformResourceLocator::SetURL method to set the URL in the shortcut.
  • Use the IPersistFile::Save method to save the shortcut file to a desired location.

好的,看起来应该是这样的(我的实际代码是生锈的,抱歉缺乏测试):

CoInitializeEx(nullptr, 0);

IUniformResourceLocator* url = nullptr;
CoCreateInstance(
  CLSID_InternetShortcut,
  nullptr,
  CLSCTX_INPROC,
  IID_IUniformResourceLocator,
  (void**)&url,
);

哦,E_NOINTERFACE 失败了?好吧,代码还不多,所以不难猜到你必须用 STA 初始化,而不是默认的 MTA:

CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

这是第一步,第二步就像这样:

url->SetURL(L"https://whosebug.com", 0);

现在进行第三步:

IPersistFile* file = nullptr;
url->QueryInterface(IID_IPersistFile, (void**)&file);
file->Save(L"best-site.url", FALSE);

咦,Save() 回来了 E_FAIL?真奇怪,我使用完全相同的代码将 ShellLink 对象保存到 .lnk 文件?

答案实际上很简单:InternetShortcut 出于某种原因,与 ShellLink 不同,可能其他 shell 对象要求 Save() 的路径是绝对路径。 (而且只有 Save()Load() 对相对路径很满意)

因此,使用 WIL 的完整工作代码:

#include <windows.h>
#include <IntShCut.h>
#include <wil/com.h>

void save_link(LPCWSTR url_value, LPCWSTR path) {
    auto url = wil::CoCreateInstance<IUniformResourceLocator>(CLSID_InternetShortcut, CLSCTX_INPROC);
    THROW_IF_FAILED(url->SetURL(url_value, 0));
    auto file = url.query<IPersistFile>();
    WCHAR full_path[MAX_PATH];
    GetFullPathName(path, ARRAYSIZE(full_path), full_path, nullptr);
    THROW_IF_FAILED(file->Save(full_path, FALSE));
}

int main() {
    THROW_IF_FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
    save_link(L"https://whosebug.com", L"best-site.url");
}