CSH Workaround for ShellExecute URL failures

This forum is for all Flare issues not related to any of the other categories.
Post Reply
Kri_D
Jr. Propeller Head
Posts: 1
Joined: Mon Oct 13, 2014 12:41 pm

CSH Workaround for ShellExecute URL failures

Post by Kri_D »

This is an issue one of my developers ran into. This problem links back to a bug with Microsoft software; we spent a bit of time haggling with their ticketing system. I felt our workaround might be useful to others who are having trouble getting CSH to work with Windows Explorer.

The Problem
Our company is migrating our help systems over to HTML5 format with Flare. We've also added CSH to the help systems using Flare CSHID's on the URI command line for accessing the topic directly, such as index.html#CSHID=GettingStarted to launch the GettingStarted.html help page.

Our apps are written in C++ and leverage the Win32 ShellExecute() function to spawn the default application associated with HTTP to display the help system. We've noticed that ShellExecute() works fine when no hashtag is specified, such as

ShellExecute(NULL,_T("open"),_T("c:\Help\index.html"),NULL,NULL,SW_SHOWNORMAL);

This function will launch the default browser associated with viewing HTML pages. In this case, the File:/// protocol handler will kick in, the browser will launch, and you will see file:///c:/Help/index.html in the address bar.

However, once you add the # information for the topic, ShellExecute() fails to open the page:

ShellExecute(NULL,_T("open"),_T("c:\Help\index.html#cshid=GettingStarted"),NULL,NULL,SW_SHOWNORMAL);

If the browser opens at all, you'll be directed to file:///c:/Help/index.html without the #cshid=GettingStarted topic identification.

Note that this is only a problem if the File protocol handler is engaged through ShellExecute(). If the help system lives out on the web, and the Http or Https protocol handler is engaged, everything works great.

For our customers, some of whom are on a private LAN, we cannot always rely on Internet access, so our help systems must ship with the application.

The Solution
After some back-and-forth with Microsoft's MSDN team, they reviewed the source code to the ShellExecute() call and determined that yes, when processing File:/// based URLs in ShellExecute(), the ShellExecute() call will strip off the # and any data it finds after the # before launching the default browser and sending in the HTML page to open. MS's stance is that they do this deliberately to prevent injections into the function.

The solution was to beef up the ShellExecute() call by searching the URL for a # and, if one was found, manually launch the default browser with the URL. Here's the pseudocode

Code: Select all

void WebDrive_ShellExecute(LPCTSTR szURL)
{
    if ( _tcschr(szURL,_T('#')) )
    {
        //
        //Get Default Browser from Registry, then launch it.
        //
        ::RegGetStr(HKCR,_T("HTTP\\Shell\\Open\\Command"),szBrowser);
        ::CreateProcess ( NULL, szBrowser + _T(" ") + szURL, NULL, NULL, FALSE, 0, NULL, NULL, &sui, &pi);
    }
    else
        ShellExecute(NULL,_T("open"),szURL,NULL,NULL,SW_SHOWNORMAL);
}
Granted there's a bit more to the C++ code, but this general design worked for us.
rob hollinger
Propellus Maximus
Posts: 661
Joined: Mon Mar 17, 2008 8:40 am

Re: CSH Workaround for ShellExecute URL failures

Post by rob hollinger »

Great information and thank you for sharing. We will add this to our KB because we get this question a lot in Support.
What happens if there is no "default" browser set? Rare, but be sure the application can handle a null returned value.

I have found that controlling the Help window with the Application can also be a way around this by using an IE control for the Help window. The IE control will always be available on the Windows OS.
Here is the example we send out to help address this issue:

When the application loads - it will start the iexplore.exe process and when a call is made to the help, it will launch the help in a new window (form) and display it. This window can be modal to the application or anyway you want it set up.

Code: Select all

ProcessStartInfo startInfo;
        public Form1()
        {
            InitializeComponent();
            startInfo = new ProcessStartInfo("iexplore.exe");
        }
When the call to help is made:

Code: Select all

private void button1_Click(object sender, EventArgs hlpevent)
        {
            //Call the Red Topic
            HelpForm helpForm = new HelpForm();
            String appdir = Path.GetDirectoryName(Application.ExecutablePath);
            string myfile = appdir + "\\HTML5\\Default.htm#cshid=100";
            helpForm.Url = new Uri("file:///" + myfile);
            helpForm.Show();
        }
Attached is the sample application (source code and help files) created in C# Visual Studio 2010
You do not have the required permissions to view the files attached to this post.
Rob Hollinger
MadCap Software
Post Reply