Kiosk Mode in Windows – Chrome on Multiple Displays

The problem

Did you ever think about putting several TVs/monitors to the wall and show there some dashboards? Current status, some pie charts and trends, alerts or advertisements. Or much like an arrival/departure tables in airport?

So, how would you do it?

  • IT’s quite straightforward, isn’t it” – you would answer originally – “simply take a PC with necessary video slots, connect all monitors/TVs… and the thing’s done.”
  • Really?
  • Ok, we would need some apps to show the information and most probably the is a portal will all that available, so simply launch several Chrome instances in full screen mode and that’s it“.

But if you are reading this blog post probably you’ve already realized that isn’t really so simple.

  • First, because you want all instances to start in full screen mode automatically
  • Second, you want them on certain monitors.

And when the first part is not really complicated – you may find chrome command line options allowing to start Chrome in full screen on even in kiosk mode – you will definitely face problems with the second one. Although Chrome command line has option for specifying window position, you will find out it doesn’t work in full screen, here is what you’ll find on the issue while googling:

But besides Chrome, you may want to launch some other app. Like video monitoring software (to ensure all slaves work hard J)

Enough Words, Give Me a Solution

A good start for the solution is given by the Stack Overflow answer.

As we need to be able to modify easily the apps, URL and entire solution easily, I’ve decided to create a script on PowerShell. But helper functions wrapping WinApi are put into a .NET dll.

A .NET/C# Library

Expose necessary WinApi functions:

public static class WinApi
{

    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
           int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);

    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);
}

I will not provide Enums referenced, you may find them on http://www.pinvoke.net/ on the respective functions description page, or download entirely in my code sample. (Link at the bottom)

Assuming we can start the process from a script easily, then next function we need is actually moving the window:

public static bool MoveToMonitor(IntPtr windowHandle, int monitor)
{
     monitor = monitor - 1;
     return WinApi.SetWindowPos(windowHandle, IntPtr.Zero, 
            Screen.AllScreens[monitor].WorkingArea.Left,
            Screen.AllScreens[monitor].WorkingArea.Top, 1000, 800, 
            SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOREDRAW);
}

But we cannot move full screen window easily this way. That’s why we will start Chrome in regular not full screen mode, and then send F11 (full Screen shortkey) to it via

public static void SendKey(IntPtr windowHandle, string keys)
{
         WinApi.SetForegroundWindow(windowHandle);
         SendKeys.SendWait(keys);
         SendKeys.Flush();
}

PowerShell Part

Before you start, ensure you have enabled scripts execution:

Run PowerShell as Administrator and execute the following, confirm when prompted.

Set-ExecutionPolicy RemoteSigned

Now let’s write a script implementing this logic:

First, let’s reference our assembly with helper functions described above. (These are class names from my real example, in your case names could be different.):

Add-Type -Path Tomin.Tools.KioskMode.dll
$WinAPI = [Tomin.Tools.KioskMode.WinApi]
$Helpers = [Tomin.Tools.KioskMode.Helper]

Second, here is the code

function Chrome-Kiosk($Url, $MonitorNum)
{

    Write-Output "starting chrome $Url , monitor: $MonitorNum"
    Start-Process $chromePath "$chromeArguments $Url"
    Start-Sleep -Seconds $ChromeStartDelay

    $window = (Get-Process -Name chrome | where MainWindowHandle -ne ([IntPtr]::Zero) | select -First 1).MainWindowHandle

    $WinAPI::ShowWindow($window, [Tomin.Tools.KioskMode.Enums.ShowWindowCommands]::Restore)
    $Helpers::MoveToMonitor($window, $MonitorNum)
    $Helpers::SendKey($window, '{F11}')
    Start-Sleep -Seconds $ChromeStartDelay
}

We moved it into a function to easy start several instances.

Let me explain what it does:

  1. First we start a chrome instance with some command line parameters, an easy part.
  2. Then we need a window handle to manipulate it. But Chrome is special, main window of the stated process is not the one we want. The good part – the is only one ManWindow set for all Chrome processes – that is one currently active. Thus we wait a second for chrome to create a window,
  3. And look for that window handle, based on the above described criteria. Once window handle is know you are the master of it.
  4. But as mentioned above there could be a problem trying to move full screened window, thus we restore it to normal
  5. Move to required monitor (numbering starts from 1)
  6. And send F11 key to it.
  7. Last delay command is to wait for F11 to take effect, because otherwise next consequent function execution may fail, as Chrome did not switch his process to new window in time

Of course you will need also to initialize couple of variables:

$chromePath = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
$chromeArguments = '--new-window --incognito'
# if Window not moved (especially on machine start) - try increaing the delay.
$ChromeStartDelay = 3

And the final calls would then look like:

# &taskkill /im chrome* /F
Chrome-Kiosk 'http://google.com' -MonitorNum 1
Chrome-Kiosk 'http://http://www.bbc.com/' -MonitorNum 2

The first command is optional and commented, but could be useful when somebody played with your kiosk and you want quickly to restore everything back by a single click.

Final Step

Create a link to your PowerShell script:

And put it into your Startup folder.

Entire Solution, No Programming

You may download already compiled solution with sample script.

Prerequisites

Windows 8.1

  • No

Windows 7

All you need to do:

1 Download and Extract

Download link is at the bottom.

Extract contents to C:\Kiosk-Mode.

You may put into another folder – but ensure you renamed path in all places.

2. Modify Ciklum-Kiosk.ps1

Put necessary URLs and Monitor Numbers (starting from 1)

3. Enable Scripts Execution

Run PowerShell as Administrator and execute the following, confirm when prompted.

Set-ExecutionPolicy RemoteSigned

4. Launch Shortcut

Double click on link (Start – Kios – modifypath.lnk) to ensure browsers are started and opened on necessary displays.

Note: youi may need to modify your link if you put file not into C:\Kiosk-Mode. Right click on it -> properties and fill it accordingly:

5. Autostart

Copy the link to desktop for easy access and to your startup folder:

6. Troubleshooting

If window is not moved to another desktop – try increasing the delay in script ($ChromeStartDelay) variable.

Script has been tested on Windows 8.1 machine.

Links

GitHub page: https://github.com/alex-tomin/Tomin.Tools.KioskMode

Binaries: https://github.com/alex-tomin/Tomin.Tools.KioskMode/releases/download/1.0/Tomin.Tools.KioskMode.zip

33 thoughts on “Kiosk Mode in Windows – Chrome on Multiple Displays

  1. This solution is the missing link in a multi-screen project I’m looking at. However, I can’t get it to work (Windows 7), after doing the necessary adjustments for paths, URLs and monitor disposition, when running, it stumbles at the dll call with “Add-Type : Could not load file or assembly ‘file:///C:\kiosk-mode\Tomin.Tools.KioskMode.dll’ or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.”.
    Is the solution in fact only Win 8.1? Is there an easy adaptation to Win 7?

  2. OK – that’s got it working. Many thanks, nice solution!
    Assume there’s not a way to get “true” Kiosk mode (rather than Full Screen)? I guess not, as that requires starting Chrome with a switch in the command line, rather than sending a key sequence.

    1. It is possible to specify command line switches in my solution. Variable $chromeArguments in the script.
      But the problem is – we cannot move FullScreen window. That’s why I start it normally move to the necessary Display, and then turn full screen by sending a keystroke.

      Chrome has command line argument for window position and fullscreen (or kiosk mode), but they don’t work together.

      In fact, it is possible to move even fullscreen window, but it is much more complicated.

  3. Thank you for this! I managed to combine your C# into the powershell script so I go the entire thing in one file I can just execute from command line. No DLLs to compile and copy to the right place. Basically involves putting all the C# code in as a string and then using Add-Type to compile it. I’ll send if you are interested.

  4. Awesome! It does exactly what I needed. I did struggle to get it working on windows 7 though, turns out that the “HelperFunctions.ps1” and the “Tomin.Tools.KioskMode.dll” files were blocked by windows causing the powerscript to fail on a ‘not digitally signed’ error. In order to remove the block, right click the files, go to properties and click “Unblock”. Hope this helps someone else 🙂

  5. This is a great article, thank you so much for sharing. I just found a solution on how to make this a real kiosk mode. Just add “–kiosk” at the end of this line:

    $chromeArguments = ‘–new-window –incognito –kiosk

    But the problem is, the second browser is not moving at the second monitor. Can you help me on this sir?

    Your help will greatly appreciated.

    1. Hi, I’m glad you find it useful.
      I started my journey with Chrome “–kiosk” switch. But I didn’t find a way to move a window in that mode.
      Though it wasn’t strongly necessary for my purposes, full screen was enough.

  6. Tomin Oleksandr Congratulations, your work is amazing !! But I doubt has arisen, we need the status bar is hidden chrome and do not know how. We thought about using Firefox (if you can hide the bar) but we are not able to release it. Could you guide?
    Thank you
    Best regards

    1. Hi, not sure I understand your problem. By default Chrome hides its status bar. If you mean favorites bar – you can configure it in Chrome settings, but again it should be hidden by default…

  7. On Windows 10 the Chrome windows do not get moved to the designated monitors. All instances open without error, but they don’t get moved.

  8. Mr. Tomin,

    Thanks for publishing this, very useful. One question, we’re trying to communicate between these windows via JavaScript but we’re not sure how to reference each window. Any insights would be greatly appreciated. Thanks!

  9. Tomin

    I am trying to set it up in Windows 10 Pro and I am having difficulties. First the Set-ExecutionPolicy RemoteSigned does not work. When I set it and try to execute the PS Script, I get a signature error. When I set it to unrestricted, I can run it once. But when I run it, I get lots of error message:

    Add-Type : Could not load file or assembly ‘file:///C:\Kiosk-Mode\Tomin.Tools.KioskMode.dll’

    Any idea on what the issue might be ?

    I have all the files in C:\Kiosk-Mode folder

  10. It’s just what I need but I have a little bug:
    It opens the n.1 link on the n.1 display. Then it goes full screen BUT when it opens the second window upon the primary, it goes also full screen but it leaves a ghost image on the primary window. I think it’s like a refresh issue because if I click on the primary window, the ghost image disappears.
    What can I do?
    Maybe sending the full screen mode to the secondary window first?

    Thank you

  11. Thanks for your good work, I tried it on and W8.1 & W10, works perfectly.
    I wanted to ask if there is the possibility to set the resolution of the various monitors; would import for viewing photos.

    Thank you very much again
    Best regards
    Fabio

  12. Not sure if this is still monitored. I’m using this script which is excellent but I have a problem in that the pages I’m using prompt for a username/password. If I launch the pages manually it uses the stored Chrome credentials

  13. I have a slightly different use case where I’m using 4 separate chrome windows over two displays and having them tiled vertically, 2 up on each monitor.

    I’ve been using just a single monitor with two chrome apps with the following command and it’s been great:
    …\Google\Chrome\Application\chrome.exe” –profile-directory=Default –app-id=
    and then using the “objShell.TileVertically” vbscript command to run the two windows side-by-side.

    But now I want to add a monitor and a further two chrome apps and having great difficulty ensuring the apps open on the appropriate monitors.

    I came across your powershell script which looks like it would work but yours is designed to push each to a monitor and full screen where I’m looking to tile them. Also, you work with the Chrome window where I need them to be apps to make better use of the screen real estate.

    Do you think your script could be manipulated to work with chrome apps instead?

  14. Hi, how are you? I am pretty amazed with this solution because simplify really long the process to run two instances in kiosk mode for terminal equipments.
    But, I tried to use it to run to windows of Internet Explorer instead of Chrome. Can you give me a hand to find why the two instances are opening in the same screen? It´´s like the selection of the monitors does not work for this app.

    Thank you so much in advance!

  15. Hi, Sometimes i have a problem with 3 : InvalidCastArguments

    $WinAPI::ShowWindow($window, [Tomin.Tools.KioskMode.Enums.ShowWindowCommands]::Restore)
    $Helpers::MoveToMonitor($window, $MonitorNum)
    $Helpers::SendKey($window, ‘{F11}’)

    solution ?

  16. I ask for your help: You need to do the same, but open 1 window in –kiosk mode, and open the second window through F11 in full screen.

  17. Hey, great work and very helpful.
    How to get numbers of monitors as integer?
    Example: I want to create three if statements, so
    if number of monitors is 2 – to open two urls,
    if number of monitors is three – to open three urls and
    if number of monitors is four – to open four urls

  18. Hi there. I have little modified starting script to have compartibility with microsoft edge chromium. Main problem was that first window was not refresh after second window opened on top an next moved to second screen. Here is a difference:
    ===============ciklum-kiosk.ps1============

    $chromePath = ‘C:\Program Files (x86)\Microsoft\Edge Beta\Application\msedge.exe’
    $chromeArguments = ‘–new-window –kiosk –disable-infobars –disable-session-crashed-bubble’

    $wnd1 = Chrome-Kiosk ‘http://ya.ru’ -MonitorNum 1
    $wnd2 = Chrome-Kiosk ‘http://rambler.ru’ -MonitorNum 2

    $temp1 = Chrome-Refresh $wnd1
    $temp1 = Chrome-Refresh $wnd2
    ===============ciklum-kiosk.ps1============

    ===============HelperFunctions.ps1=========
    function Chrome-Kiosk($Url, $MonitorNum)
    {
    Write-Host “starting MSEdge “$Url” , monitor: “$MonitorNum
    Start-Process $chromePath “$chromeArguments $Url”
    $temp1 = Start-Sleep -Seconds $ChromeStartDelay
    $window = (Get-Process -Name msedge | where MainWindowHandle -ne ([IntPtr]::Zero) | select -First 1).MainWindowHandle

    $temp1 = $WinAPI::ShowWindow($window, [Tomin.Tools.KioskMode.Enums.ShowWindowCommands]::Restore)
    $temp1 = $Helpers::MoveToMonitor($window, $MonitorNum)
    $temp1 = $Helpers::SendKey($window, ‘{F11}’)
    $temp1 = Start-Sleep -Seconds $ChromeStartDelay

    return $window
    }

    function Chrome-Refresh($window)
    {
    Write-Host “refreshing window value =” $window
    $Helpers::SendKey($window, ‘{F5}’)
    Start-Sleep -Seconds $ChromeStartDelay
    }

    ===============HelperFunctions.ps1=========

Leave a reply to Xian Cancel reply