ShellExecute fails for local html or file URLs
Asked Answered
E

4

6

Our company is migrating our help systems over to HTML5 format under Flare. We've also added Topic based access 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 and 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.

Enjambment answered 10/10, 2014 at 17:56 Comment(0)
E
5

After some back-and-forth with Microsoft's MSDN team, they reviewed the source code to the ShellExecute() call and it was 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, then we would manually launch the default browser with the URL. Here's the pseudocode

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.

Enjambment answered 10/10, 2014 at 17:56 Comment(1)
Did you figure out a way to launch Edge so that it would accept the #cshid=<topic-id> part of the URL?Incapacitate
H
1

I tried WebDrive's solution and it didn't really work on Windows 10.

"HTTP\Shell\Open\Command" default value is set to Internet Explorer path, regardless of what my default browser setting. However, for Internet Explorer that solution DOES work.

Process to fetch default browser path on Windows 10 is a bit different (How to determine the Windows default browser (at the top of the start menu)) but even then the solution is not guaranteed to work, depending on the browser. E.g. for me it didn't work with Edge.

To get it to work with Edge I had to add "file:///" to the URL -- but that also makes the URL work with ShellExecute(). So, at least on Windows 10, all I needed to do was this:

ShellExecute(NULL,_T("open"),_T("file:///c:/Help/Default.html#cshid=1648"),NULL,NULL,NULL);

UPDATE: The above stopped working months ago. What I eventually did was go through temporary file, as described here: https://forums.madcapsoftware.com/viewtopic.php?f=9&t=28376#p130613

Hoofed answered 2/5, 2018 at 22:58 Comment(2)
Are you sure ShellExecute works with the anchor fragment? I just wrote a C++ console app and it opens to the default.html but is dropping the #cshid=... part of the URL.Incapacitate
@KeithHill The only way to get this to work with Edge seems to be to go through temporary file, see here: forums.madcapsoftware.com/viewtopic.php?f=9&t=28376#p130613Hoofed
O
1

Use FindExecutable() to get the default browser and pass the full help file path with its queries (?) and fragments (#) as the lpParameters parameter to ShellExecute(). They won't get stripped off there.
Then handle the case if it is a Store App (most likely Microsoft Edge).

Pseudo C code:

if (FindExecutable(_T("c:\Help\index.html"), NULL, szBrowser)
{
    if (szBrowser == _T("C:\WINDOWS\system32\LaunchWinApp.exe"))
    {
        // default browser is a Windows Store App
        szBrowser = _T("shell:AppsFolder\Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge");
    }
}
else
{
    szBrowser = szURL;
    szURL = NULL;
}

ShellExecute(NULL, NULL, szBrowser, szURL, NULL, SW_SHOWNORMAL);
Offering answered 3/7, 2019 at 14:7 Comment(0)
O
0

I solved the problem without using any method other than ShellExecute in a Qt Application

QString currentpath = QDir::currentPath();
QString url = "/help//html/index.html#current";
QString full_url = "file:///" + currentpath + url;
QByteArray full_url_arr= full_url.toLocal8Bit();
LPCSTR lp = LPCSTR(full_url_arr.constData());
ShellExecute(NULL, "open", lp, NULL, NULL, SW_SHOWNORMAL);

Omalley answered 17/8, 2020 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.