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

0

This one had me stuck for too long, so I'm self answering this. Apologies for the sardonic tone!

Trying to use the official API to create .url files has only 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.

Ok, so that should look something like this (my actual code is rust, sorry for the lack of testing):

CoInitializeEx(nullptr, 0);

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

Oh this fails with E_NOINTERFACE? Well there's not much code yet, so it's not too hard to guess that you have to init with STA, instead of the default MTA:

CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

So that's the first step, the second is just something like:

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

Now for the third step:

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

Huh, Save() is returning E_FAIL? That's strange, I'm using exactly the same code for saving a ShellLink object to a .lnk file?

windows
winapi
com
asked on Stack Overflow May 19, 2020 by Simon Buchan

1 Answer

3

The answer is actually quite simple: InternetShortcut for whatever reason, unlike ShellLink and presumably other shell objects requires that the path for Save() is absolute. (And only Save(), Load() is quite happy with a relative path)

So, the full working code using 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://stackoverflow.com", L"best-site.url");
}
answered on Stack Overflow May 19, 2020 by Simon Buchan • edited May 19, 2020 by Simon Buchan

User contributions licensed under CC BY-SA 3.0