Setting the device brightness on Windows with WPF

Datetime:2017-04-16 05:18:39         Topic: WPF          Share        Original >>
Here to See The Original Article!!!

I am working on a project which I can’t really detail, but one aspect caused me a few headaches. Finally I reached out to the Windows team at Microsoft and got great help from Katie Anderson who works on the brightness team. This was quite tricky, and we had to have a couple of roundtrips before we managed to find a way that worked.

Well, why?

In the application I am working on, it is necessary for the user to set the device brightness from the application itself. You might wonder why, because most Windows devices have hard keys to set the monitor brightness. For example on a Surface, you can use the Fn-Del and Fn-Backspace key combinations to alter the brightness of the screen. Or you can of course swipe from the right side and use the Brightness button to change the value in the Action Center.

However on that particular project, the user doesn’t have access to the keyboard, nor to the Action Center. This is a kind of kiosk scenario if you will where many of the Windows features are deactivated by policies, and no hard keyboard is provided. I had to find a way to do this programmatically.

First approach: Not so good

At first I thought I would have my application’s main window run in full screen and set a black Rectangle on top of everything, and change the Rectangle’s opacity in my code. That worked well but of course it wouldn’t work if other applications were in the foreground (yes, I forgot to mention, it’s a kiosk app which can start other apps and put them in the foreground…).

Second approach: Better but not great

OK, no problems I thought, let’s have my app open a modal full screen window which is always on top. I will then have this window made insensitive to touch or mouse clicks with the following code. Finally, this window will have the black Rectangle and be on top of everything all the time.

public static class WindowsServices
{
    private const int GwlExstyle = -20;
    private const int WsExTransparent = 0x00000020;

    public static void SetWindowExTransparent(IntPtr hwnd)
    {
        var extendedStyle = GetWindowLong(hwnd, GwlExstyle);
        SetWindowLong(hwnd, GwlExstyle, extendedStyle | WsExTransparent);
    }

    [DllImport("user32.dll")]
    static extern int GetWindowLong(IntPtr hwnd, int index);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
}

This worked OK but somehow it was not satisfying. I really wanted to have a solution where my app would modify the actual device brightness. There are a few reasons why the client also wanted that, and so I started to investigate deeper. Unfortunately, Bing searches (and the equivalent Google searches) didn’t really return anything satisfactory.

Using Powershell

After searching a bit, I got a first clue that what I wanted to achieve was doable : You can query and modify the device brightness from Powershell with the following script:

CODE TO GET THE BRIGHTNESS INSTANCE

PS C:> Get-Ciminstance -Namespace root/WMI -ClassName WmiMonitorBrightness

CODE TO SET THE BRIGHTNESS

PS C:\Users\lbugn> $monitor = Get-WmiObject -ns root/wmi -class wmiMonitorBrightNessMethods
PS C:\Users\lbugn> $monitor.WmiSetBrightness(50,10)

If you run these commands in Powershell, you will marvel at the result: Yes it does work and the screen’s brightness is modified. Great Scott, I am on the right track! The key to this is the WMI (Windows Management Interface) class WmiMonitorBrightness .

Converting to .NET

The next step was obvious: I needed to convert the Powershell script to some usable .NET code. Unfortunately, easier said than done. This is where I reached out to Microsoft for help and thankfully Katie really followed through and after a few iterations we got it to work.

First she used the WMI Code Creator tool to convert the Powershell script to .NET . I had no idea that such a tool existed, but then again I never had to dive so deep in the entrails of Windows. Unfortunately when running the code that the tool created (which I won’t post here to avoid confusion), I got some errors. One more roundtrip to Redmond (via email) and Katie found the way: The following code will indeed modify the brightness of the screen, yay!!

public static class WindowsServices
{
    private static ManagementObject _brightnessInstance;
    private static ManagementBaseObject _brightnessClass;

    static WindowsServices()
    {
        // Querying the Windows service to get the Brightness API.
        var searcher = new ManagementObjectSearcher(
            "root\\WMI", 
            "SELECT * FROM WmiMonitorBrightness");

        var results = searcher.Get();
        var resultEnum = results.GetEnumerator();
        resultEnum.MoveNext();
        _brightnessClass = resultEnum.Current;

        // We need to create an instance to use the Set method!
        var instanceName = (string)_brightnessClass["InstanceName"];
        _brightnessInstance = new ManagementObject(
            "root\\WMI",
            "WmiMonitorBrightnessMethods.InstanceName='" + instanceName + "'",
            null);
    }

    public static int GetDeviceCurrentBrightness()
    {
        // Getting the current value.
        var value = _brightnessClass.GetPropertyValue("CurrentBrightness");
        var valueString = value.ToString();
        return int.Parse(valueString); // Direct cast fails.
    }

    public static void SetDeviceBrightness(int newValue)
    {
        if (newValue < 0)
        {
            newValue = 0;
        }

        if (newValue > 100)
        {
            newValue = 100;
        }

        var inParams = _brightnessInstance.GetMethodParameters("WmiSetBrightness");
        inParams["Brightness"] = newValue;
        inParams["Timeout"] = 0;
        _brightnessInstance.InvokeMethod("WmiSetBrightness", inParams, null);
    }
}

I made a small sample here which gets the current value of the screen brightness and then increases it by 10% every time you click on a button. When you reach 100%, it goes back to zero on the next click. Simple enough, and does the trick.

I hope that this will be useful to a reader looking for the same feature. I’d love to be the first one to ever blog about this. I doubt that I am, and probably someone will point me to the article that I never found but seriously, I really couldn’t find any mention of something like that on the whole WWW.

Happy coding!

Laurent








New