2

I'd like to open a local HTML file navigated to a specific headline with an ID.
string url = "file:///C:/myFile.html#myHeading"
If I paste the URL in the browser directly, it correctly navigates to the desired heading.
But when I try to have the system handle the URL, it omits the hash entirely.

ProcessStartInfo si = new ProcessStartInfo(url);
si.UseShellExecute = true;
Process.Start(si);

will simply open file:///C:/myFile.html.

Is there a way I can make this work?
Sadly, I don't know where this stripping happens, so my research so far turned up empty.
I tried replacing the hash symbol with its escaped form %23, which of course didn't work (could not find the file specified).

A work-around I'm using in the meantime:

From here, I wrote an extension which tells me the path to the executable set as the default app for opening files with a given extension.

public static string getDefaultAssociation(string extension)
{
    if (extension.Substring(0, 1) != ".")
    {
        int os = extension.IndexOf('.');
        if (os > 0)
        {
            extension = extension.Substring(os);
        }
        else
        {
            extension = "." + extension;
        }
    }
    const int S_OK = 0;
    const int S_FALSE = 1;

    uint length = 0;
    uint ret = AssocQueryString(AssocF.None, AssocStr.Executable, extension, null, null, ref length);
    if (ret != S_FALSE)
    {
        //throw new InvalidOperationException("Could not determine associated string");
        WriteLog("Could not determine associated string");
        return "";
    }

    var sb = new StringBuilder((int)length); // (length-1) will probably work too as the marshaller adds null termination
    ret = AssocQueryString(AssocF.None, AssocStr.Executable, extension, null, sb, ref length);
    if (ret != S_OK)
    {
        //throw new InvalidOperationException("Could not determine associated string");
        WriteLog("Could not determine associated string");
        return "";
    }

    return sb.ToString();
}

[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern uint AssocQueryString(
    AssocF flags,
    AssocStr str,
    string pszAssoc,
    string pszExtra,
    [Out] StringBuilder pszOut,
    ref uint pcchOut
);

[Flags]
public enum AssocF
{
    None = 0,
    Init_NoRemapCLSID = 0x1,
    Init_ByExeName = 0x2,
    Open_ByExeName = 0x2,
    Init_DefaultToStar = 0x4,
    Init_DefaultToFolder = 0x8,
    NoUserSettings = 0x10,
    NoTruncate = 0x20,
    Verify = 0x40,
    RemapRunDll = 0x80,
    NoFixUps = 0x100,
    IgnoreBaseClass = 0x200,
    Init_IgnoreUnknown = 0x400,
    Init_Fixed_ProgId = 0x800,
    Is_Protocol = 0x1000,
    Init_For_File = 0x2000
}

public enum AssocStr
{
    Command = 1,
    Executable,
    FriendlyDocName,
    FriendlyAppName,
    NoOpen,
    ShellNewValue,
    DDECommand,
    DDEIfExec,
    DDEApplication,
    DDETopic,
    InfoTip,
    QuickTip,
    TileInfo,
    ContentType,
    DefaultIcon,
    ShellExtension,
    DropTarget,
    DelegateExecute,
    Supported_Uri_Protocols,
    ProgID,
    AppID,
    AppPublisher,
    AppIconReference,
    Max
}

So I can use

string def = getDefaultAssociation("html");
ProcessStartInfo si = new ProcessStartInfo(def);
si.Arguments = url;
si.UseShellExecute = true;
Process.Start(si);

to directly tell my default browser to go to the file URL, INCLUDING the ID hash.

Ben Philipp
  • 1,832
  • 1
  • 14
  • 29
  • Also, a more elegant way might be to use a `WebBrowser` Control in-app to display the file and navigate to the desired ID, if that's feasible for your app. – Ben Philipp Oct 28 '20 at 18:33

1 Answers1

1

File scheme (file://) just doesn't support anchors or actually any parameters, according to RFC. So query string, anchors or anything else besides path to file can be ignored by whatever processes url with this scheme, which is what happens here. If you try file:///C:/myFile.html?x=1 - query string will also be stripped. If on the other hand you'll try http://google.com?x=1#test then it will open exactly this url in browser, because other scheme.

So I'd say it works as expected and your workaround is fine.

Community
  • 1
  • 1
Evk
  • 98,527
  • 8
  • 141
  • 191
  • Thank you for your explanation! It's a bit disappointing that the scheme is so much less than it could be, but understandable. – Ben Philipp Oct 28 '20 at 18:26