0

I have a script that generates some HTML pages, when this is done I open the index file of the generated pages. To do this I have this code:

if exist "generated_pages/index.html" start "" "generated_pages/index.html"

Right now the page opens in my default text editor for .html files, how can I make sure it opens in the user's default browser? I don't want to use commands for specific browsers as I don't know what the user's default browser will be.

5
  • 1
    I think it is opened in the default handler for .html files, which happens to be a text editor in your Windows account. Commented Dec 7, 2018 at 11:12
  • 1
    Change the default handler for .html extensions, or start the browser with the .html file as a parameter. If you're on Windows you could force IE to open it, but you should allow the file to be opened by the default handler, regardless. Commented Dec 7, 2018 at 11:23
  • The best way to display the page would be to use a small HTTP server -- IIS Express, for example, or netcat or mongoose if you prefer something install-less. That would solve your file association conundrum, but it might also address other issues you haven't considered -- browser security preventing JavaScript from running within local files, for example. You might also consider using mshta.exe "generated_pages/index.html" to view your html. Commented Dec 7, 2018 at 13:34
  • Use the ASSOC /? command to see how associations can be managed. You might also be interested in thewindowsclub.com/change-file-associations-windows Commented Dec 7, 2018 at 20:41
  • The thing is that this script is meant for other people on different computers, that's why I want the .html to open with the default browser and not a specific one like chrome. Commented Dec 7, 2018 at 20:56

4 Answers 4

1

Unfortunately, there's no way to specify with the start command what type of program you wish to launch. It will launch the default associated program based on the file's extension, and you're at the ill-fated mercy of the user's dubious choice of file association for .html files. If you want to ensure your file gets opened only by a web browser and not by a text editor, then it'd be better to pass a URL into start than a filesystem location. Using an http address as an argument to start should guarantee that the thing opening the location will be a web browser.

Serving your .html file over http can be done without relying on 3rd party binaries. It's not prohibitively difficult to use .Net methods to create a rudimentary web server and serve the web page back over localhost. That way you can start "" "http://localhost:port/" and you'll have a much better chance of avoiding opening the file in a text editor if your users have screwed up their file associations.

Save the following sorcery as a .bat script, tweak the html file name and location as needed, and give it a try.

<# : httptest.bat -- https://stackoverflow.com/a/53689025/1683264
@echo off & setlocal

if exist test.html call :display test.html
goto :EOF

:display <htmlfile>
setlocal
set "infile=%~f1"
powershell -noprofile "iex (${%~f0} | out-string)"
endlocal & exit /b

: end Batch / begin PowerShell polyglot code #>
$tcpClient = new-object Net.Sockets.TcpClient
while ($port = get-random -min 1024 -max 65535) {
    try {$tcpClient.Connect("localhost", $port)}
    catch {$tcpClient.Dispose(); break}
}
$endpoint = new-object Net.IPEndPoint([Net.IPAddress]::Any, $port)
$listener = new-object Net.Sockets.TcpListener $endpoint
$listener.start()
cmd /c start "" "http://localhost:$($port)/"
$client = $listener.AcceptTcpClient()
$stream = $client.GetStream()
if ($stream.CanRead) {
    [void]$stream.read((new-object byte[] 1024), 0, 1024);
}
if ($stream.CanWrite) {
    $content = "HTTP/1.1 200 OK`n`n$(gc $env:infile)"
    $out = [text.encoding]::UTF8.GetBytes($content)
    $stream.write($out, 0, $out.length)
}
$stream.close()
$stream.dispose()
$listener.stop()

As a side benefit, serving your html over http can help you avoid tripping some browsers' security prohibiting JavaScript from executing from file:/// URLs.


If you want to include other referenced files, such as images, css files, sourced JavaScript files, etc, then that does get a little more tricky. Here's a more thorough example that listens for an initial http request for up to 60 seconds, then continues serving relative-path sourced files as the browser requests them until no requests have been received for 5 seconds. It should properly announce mime types of images and other sourced files. If you need a longer timeout, change the serve-content 5 line near the bottom.

<# : httptest2.bat -- https://stackoverflow.com/a/53689025/1683264
@echo off & setlocal

if exist "%~1" (call :display "%~1") else goto usage
goto :EOF

:usage
echo Usage: %~nx0 htmlfile
exit /b

:display <htmlfile>
setlocal
set "infile=%~f1"
powershell -noprofile "iex (${%~f0} | out-string)"
endlocal & exit /b

: end Batch / begin PowerShell polyglot code #>
Add-Type -as System.Web
$rootpath = (get-item $env:infile).DirectoryName
$filename = (get-item $env:infile).Name
$webname = [Web.HttpUtility]::UrlEncode($filename)
$tcpClient = new-object Net.Sockets.TcpClient
while ($port = get-random -min 1024 -max 65535) {
    try {$tcpClient.Connect("localhost", $port)}
    catch {$tcpClient.Dispose(); break}
}
cmd /c start "" "http://localhost:$($port)/$webname"

function log($polarity, $txt) {
    $color = (("red","darkgray"),("green","white"))[$polarity]
    write-host -nonewline "[" -f $color[1]
    write-host -nonewline "*" -f $color[0]
    write-host "] $txt" -f $color[1]
}

function serve-content($seconds) {
    $timer = (get-date).AddSeconds($seconds)
    while (!$listener.Pending()) {
        start-sleep -milliseconds 10
        if ((get-date) -ge $timer) { return $false }
    }
    $client = $listener.AcceptTcpClient()
    $stream = $client.GetStream()
    if ($stream.CanRead) {
        $request = new-object byte[] 1024
        $size = $stream.read($request, 0, $request.length)
        $headers = [text.encoding]::UTF8.GetString($request, 0, $size)
        if ($stream.CanWrite) {
            $loc = $headers.split("`r?`n")[0] -replace "^\S+\s+|\s+HTTP/\d.+$"
            $loc = $loc -replace "^/", "$rootpath/" -replace "/", "\"
            $loc = [Web.HttpUtility]::UrlDecode($loc)
            if ($loc) {
                if (!(test-path $loc -type leaf)) {
                    $loc = [Web.HttpUtility]::UrlDecode($loc)
                }
                if (test-path $loc -type leaf) {
                    $response = ,"HTTP/1.1 200 OK"
                    $mime = [Web.MimeMapping]::GetMimeMapping($loc)
                    $response += ,"Content-Type: $mime"
                    $response += ,"Content-Length: $((gi $loc).length)","",""
                    $out = [text.encoding]::UTF8.GetBytes(($response -join "`n"))
                    [byte[]]$body = gc $loc -enc byte
                    $out += $body
                    $stream.write($out, 0, $out.length)
                    log $true $loc
                }
                else {
                    $response = "HTTP/1.1 404 Not Found","",@"
<html lang="en">
    <head>
        <title>Error 404</title>
    </head>
    <body>
        <h3>Not Found</h3>
        <p>The requested resource could not be located.</p>
    </body>
</html>
"@
                    $out = [text.encoding]::UTF8.GetBytes(($response -join "`n"))
                    $stream.write($out, 0, $out.length)
                    log $false $loc
                }
            }
        }
    }
    $stream.close()
    $stream.dispose()
    $client.close()
    return $true
}

$endpoint = new-object Net.IPEndPoint([Net.IPAddress]::Any, $port)
$listener = new-object Net.Sockets.TcpListener $endpoint
$listener.start()

[void](serve-content 60)
while ((serve-content 5)) {}
$listener.stop()
Sign up to request clarification or add additional context in comments.

Comments

0

I don't know if this works to other browsers, but for Chrome, this works just fine. You can open your html file with chrome.exe just like the following:

if exist "generated_pages/index.html" start "" "full\path\to\chrome.exe" file:///C:/example/generated_pages/index.html

Another way, would be to change the default handler for html files in your Windows Account. (right-click to file => Open With... => select your browser and option "Open always html files...").

1 Comment

The thing is that this script is meant for other people on different computers, that's why I want the .html to open with the default browser and not a specific one like chrome.
0

Mine implementation:

Source code

open_by_default_browser_nowait.bat:

@echo off

setlocal DISABLEDELAYEDEXPANSION

rem Query the registry to find the ProgID associated with .html files (default browser)
for /F "usebackq tokens=1,2,*"eol^= %%i in (`@"%SystemRoot%\System32\reg.exe" query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.html\UserChoice" /v "Progid" 2^>nul`) do if "%%i" == "Progid" set "BrowserProgID=%%k"

rem If no UserChoice is found, fall back to HKEY_CLASSES_ROOT\htmlfile
if not defined BrowserProgID set "BrowserProgID=htmlfile"

rem Query the registry to get the command for opening the default browser
for /f "usebackq tokens=1,2,*"eol^= %%i in (`@"%SystemRoot%\System32\reg.exe" query "HKCR\%BrowserProgID%\shell\open\command" /ve 2^>nul`) do if "%%i" == "(Default)" set "BrowserCommand=%%k"

setlocal ENABLEDELAYEDEXPANSION

rem Handle a simple case with already surrounded quotes at first!
set "BrowserCommand=!BrowserCommand:"%%0"=""!"

for /L %%i in (1,1,9) do set "BrowserCommand=!BrowserCommand:"%%%%i"="%%~%%i"!"

set "BrowserCommand=!BrowserCommand:%%0=""!"

for /L %%i in (1,1,9) do set "BrowserCommand=!BrowserCommand:%%%%i="%%~%%i"!"

for /F "usebackq tokens=* delims="eol^= %%i in ('"!BrowserCommand!"') do endlocal & set "BrowserCommand=%%~i"

if not defined BrowserCommand exit /b 255

set "?3E=>"
setlocal ENABLEDELAYEDEXPANSION & for /F "usebackq tokens=* delims="eol^= %%i in ('"!BrowserCommand!"') do endlocal & set /P "=>" <nul & call echo;%%~i
call start "" %BrowserCommand%
exit /b
> open_by_default_browser_nowait.bat generated_pages/index.what-ever-extension

For example, if the Acrobat Reader is installed together with the Firefox, then for a PDF file it will open the Firefox using a default command line from the registry:

(Windows Registry Editor Version 5.00)

[HKEY_CLASSES_ROOT\FirefoxHTML-<SOME-ID>\shell\open\command]
@="\"C:\\Program Files\\Mozilla Firefox\\firefox.exe\" -osint -url \"%1\""

So you can improve the command line to open it in the Private Browser mode instead:

[HKEY_CLASSES_ROOT\FirefoxHTML-<SOME-ID>\shell\open\command]
@="\"C:\\Program Files\\Mozilla Firefox\\firefox.exe\" -osint -private-window \"%1\""

Old command line variant:

[HKEY_CLASSES_ROOT\FirefoxHTML-<SOME-ID>\shell\open\command]
@="\"C:\\Program Files\\Mozilla Firefox\\firefox.exe\" -osint -private -url \"%1\""

Comments

0

in 2018 the default Windows Browser was often still Internet Explorer for some and edge for others. Depending on Locality or windows variant most users may have had that replaced by The Administrator. (Internet Explorer was officially retired and went out of support on June 15, 2022.)

In 2025 the position is generally simpler as Edge is the inbuilt replacement and thus more common the default on most Current Windows 11 devices. Even the majority of Legacy Windows 10 or earlier 8/7 if still in use.

The command to start the Browser with either a HTML or PDF or other Browser Compatible Format is simply:

Start "" MsEdge --app="File URL"

So for instance, to see this page as a download from remote. We can run in Windows enter image description here

Start "" MsEdge --app="https://stackoverflow.com/questions/53668189"

Once saved local or generated local we need to use different Windows Specific URI/URL and it is the same either as a CMD console line or a batch file. Simply one line. With backslashes will do, as its Windows file system. Spot the difference! (The logo is not local so missing)

>if exist "%cd%\generated_pages\index.html" start "" msedge --app="file://%cd%\generated_pages\index.html"

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.