Search code examples
urlvisual-c++mfcwebview2

Converting file path to URL with MFC


I have seen similar questions (e.g. Encode/Decode URLs in C++). But, for me:

CString strURL;
DWORD dwSize = _MAX_PATH;
if (InternetCanonicalizeUrl(strFile, strURL.GetBuffer(_MAX_PATH), &dwSize, ICU_BROWSER_MODE))
{
    // still has backslash
    AfxMessageBox(strURL);
}
strURL.ReleaseBuffer();

strURL = strFile;
strURL.Replace(L"\\", L"/");
strURL = L"file:///" + strURL;
AfxMessageBox(strURL);

Using InternetCanonicalizeUrl did not work:

  1. The prefix was file:// and not file:///.
  2. The \ was not replaced with /.

I did it manually and my version of the URL works with my subsequent WebView2 function. To clarify, the path itself was built with ::GetTempPath() and/or ::GetTempFileName().

Why did the built-in API call not do what I needed?


Solution

  • Why did the built-in API call not do what I needed?

    The best answer I can give to this "Why?" part of your question is that the "WinInet" library component of the WinAPI uses (or, at least, is based on) Internet Explorer internally … and we all know how dodgy and non-conformant IE can be.

    However, the documentation for the InternetCanonicalizeUrl function does actually suggest (albeit cryptically) an alternative if you want a more complete/compliant result:

    In Internet Explorer 4.0 and later, InternetCanonicalizeUrl always functions as if the ICU_BROWSER_MODE flag is set. Client applications that must canonicalize the entire URL should use either CoInternetParseUrl (with the action PARSE_CANONICALIZE and the flag URL_ESCAPE_UNSAFE) or UrlCanonicalize.

    I experimented with the latter alternative (it seemed the simpler of the two) and found a relatively trivial fix for your issue:

    //  if (InternetCanonicalizeUrl(strFile, strURL.GetBuffer(_MAX_PATH), &dwSize, ICU_BROWSER_MODE))
        if (UrlCanonicalize(strFile, strURL.GetBuffer(_MAX_PATH), &dwSize, 0) == S_OK)
        {
            AfxMessageBox(strURL);
        }
        strURL.ReleaseBuffer();
    

    In the test I ran (using the path from GetTempPath()), this gives the full file:/// prefix and replaces all backslashes with forward slashes.

    (You will need to #include <shlwapi.h> somewhere in your code and will also need to include the "Shlwapi.lib" library when linking.)


    On the use of three slashes after a file: prefix for "localhost" URLs, see this Q/A on Super User.