Click here to Skip to main content
15,861,172 members
Articles / Programming Languages / C#

Setting Screen Brightness in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (19 votes)
13 Dec 2009CPOL4 min read 109.6K   4.9K   36   23
Tutorial on programmatically setting the screen brightness using C#.

Introduction

There are a lot of parts of the Windows API that are difficult to interface with through the .NET framework due to the liberal use of pointer references. This article provides an example of interfacing to a Windows API call that utilizes a pointer to a large buffer (3 * 256 * 2 bytes large). We also look at how StackAlloc works and using the unsafe context in our code.

Background

I work in the offshore oil field and marine industry, developing user interfaces to the hardware my company develops. Working offshore, a major concern to those on the bridge of a vessel is the preservation of their night vision. This requires dimming screens down as far as they can go, but many industrial panel mount monitors don't provide brightness controls on the front panel. Using Windows API calls, we can dim the screen down about as far as the hardware dimmer will go.

Using the Code

The Microsoft Windows API provides a call titled SetDeviceGammaRamp that takes two arguments: an HDC (hardware device context), and a pointer to an array of gamma values to load into the video card. Not all video card architectures support the SetDeviceGammaRamp, so your mileage with this API call may vary.

Let's take a look at the prototype declaration for the Windows API call:

C#
[DllImport("GDI32.dll")]
private unsafe static extern bool SetDeviceGammaRamp(Int32 hdc, void* ramp); 

As you can see, there are a number of parts here. First is the DllImport; this is contained in the System.Runtime.InteropServices namespace, and provides the hook into the GDI32.dll where the SetDeviceGammaRamp function is contained. The function has two arguments, hdc and ramp. The first argument, hdc, is the pointer to the "hardware device context" that we are setting the gamma ramp on. The second argument ramp is the array of new gamma values, which are stored as a short array with dimensions [3][256].

Let's take a look at the entire class code so we can see everything in context:

C#
[DllImport("gdi32.dll")]
private unsafe static extern bool SetDeviceGammaRamp(Int32 hdc, void* ramp);

private static bool initialized = false;
private static Int32 hdc;


private static void InitializeClass()
{
    if (initialized)
        return;

    //Get the hardware device context of the screen, we can do
    //this by getting the graphics object of null (IntPtr.Zero)
    //then getting the HDC and converting that to an Int32.
    hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();

    initialized = true;
}

public static unsafe bool SetBrightness(short brightness)
{
    InitializeClass();

    if (brightness > 255)
    brightness = 255;

    if (brightness < 0)
        brightness = 0;

    short* gArray = stackalloc short[3 * 256];
    short* idx = gArray;

    for (int j = 0; j < 3; j++)
    {
        for (int i = 0; i < 256; i++)
                {
                    int arrayVal = i * (brightness + 128);

                    if (arrayVal > 65535)
                        arrayVal = 65535;

                    *idx = (short)arrayVal;
                    idx++;
                }
    }

    //For some reason, this always returns false?
    bool retVal = SetDeviceGammaRamp(hdc, gArray);

    //Memory allocated through stackalloc is automatically free'd
    //by the CLR.

    return retVal;   
}

The first function InitializeClass is used because we have a static class and we need to store some variables that only need to be found once. Because the class is static, after the InitializeClass function is called, the variables will be initialized for any caller in the same application context. The line of interest is:

C#
hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32(); 

This is where we find our hardware device context. We use the Graphics class to create a Graphics object from an Hwnd of null (IntPtr.Zero). This returns a device context that references the entire graphics display. We then use the GetHdc function to get an IntPtr to the HDC, and then convert that to an Int32 value that we can pass to the Windows API function.

The function with the meat of this article is SetBrightness. Let's look at the function declaration:

C#
public static unsafe bool SetBrightness(short brightness)

Here, the function is declared as public, static, unsafe, and returns a type of bool. The unsafe keyword means that the entire function is to be considered unsafe; that is, type checking is turned off and pointer use is allowed.

Next, we call InitializeClass to make sure that the class has been initialized. If the initialization is already done, the function call returns immediately. We then do some checking on the input data (always, always assume that the input data is bad, and clean it as necessary).

C#
short* gArray = stackalloc short[3 * 256]; 

This line uses stackalloc to allocate a section of memory on the stack where we can do our work. We can't declare multiple dimension arrays with stackalloc, so we just set aside as much space as we need for the entire element count. The actual size in bytes of the array is 3 * 256 * sizeof(short).

After that, we need to declare an indexing variable that will traverse the allocated array:

C#
short* idx = gArray; 

This is initialized to point at the first element in gArray. We will use this variable to traverse down the array and fill it with the values needed.

C#
for (int j = 0; j < 3; j++)
{
     for (int i = 0; i < 256; i++)
    {
           int arrayVal = i * (brightness + 128);
   
       if (arrayVal > 65535)
          arrayVal = 65535;

       *idx = (short)arrayVal;
       idx++;
    }
}

Here, we loop through all the elements in the array and fill it with the desired Gamma values. Idx++ at the end of the loop automatically increases the pointer sizeof(short) so that we go to the next element. This is the magic of pointer arithmetic.

All that is left is to call the API function with our HDC and Gamma array:

C#
//For some reason, this always returns false?
bool retVal = SetDeviceGammaRamp(hdc, gArray)  

which is supposed to return true or false depending on if the call succeeds, but I've found on my machine that the function call always returns false, regardless of the function working or not.

And there we have it. It's best to compile this into a separate DLL that you've marked to compile as unsafe (either add the /unsafe escape to the compiler line, or go to Properties>Build, and check the "Allow Unsafe Code" box).

Points of Interest

Interfacing back-and-forth with .NET code and pointers isn't the easiest thing in the world. Many of us got away from C/C++ because of pointer arithmetic and "DLL Hell", but occasionally, we have to get sucked back in. This just goes to show you that we can write a lot of code to interface to unmanaged code without having to write complicated wrapper functions in separate languages.

Note 1: The API function always returns false on my machine.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
President 6D Systems LLC
United States United States
I studied Software Engineering at Milwaukee School of Engineering for 2 years before switching to Management of Information Systems for a more business oriented approach. I've been developing software since the age of 14, and have waded through languages such as QBasic, TrueBasic, C, C++, Java, VB6, VB.NET, C#, etc. I've been developing professionally since 2002 in .NET.

Comments and Discussions

 
Questiongraphics is undefined Pin
pollaris1121-May-18 2:51
pollaris1121-May-18 2:51 
GeneralMy vote of 2 Pin
imozarti13-Feb-15 13:22
imozarti13-Feb-15 13:22 
QuestionWhat is the default value of brightness? Pin
Member 1050382513-Jan-14 3:14
Member 1050382513-Jan-14 3:14 
AnswerRe: What is the default value of brightness? Pin
Ron Beyer13-Jan-14 4:10
professionalRon Beyer13-Jan-14 4:10 
AnswerRe: What is the default value of brightness? Pin
Adrao15-Aug-14 1:03
Adrao15-Aug-14 1:03 
GeneralMy vote of 5 Pin
Amir Mohammad Nasrollahi9-Aug-13 20:13
professionalAmir Mohammad Nasrollahi9-Aug-13 20:13 
GeneralMy vote of 5 Pin
MD. Mohiuddin Ahmed27-Mar-13 11:31
MD. Mohiuddin Ahmed27-Mar-13 11:31 
QuestionNeed full compiled solution Pin
peela8528-Aug-12 17:53
peela8528-Aug-12 17:53 
AnswerRe: Need full compiled solution Pin
SimonQuah2122-Jan-13 18:40
SimonQuah2122-Jan-13 18:40 
QuestionAwesome Pin
Xeshan Ahmed15-Oct-11 2:02
Xeshan Ahmed15-Oct-11 2:02 
GeneralMy vote of 5 Pin
TeapotDev8-Aug-11 3:16
TeapotDev8-Aug-11 3:16 
GeneralMy vote of 5 Pin
Jazzflyer15-Nov-10 8:02
Jazzflyer15-Nov-10 8:02 
GeneralSuggestions for actually changing the brightness Pin
Skaha Software7-Jun-10 0:42
Skaha Software7-Jun-10 0:42 
GeneralRe: Suggestions for actually changing the brightness Pin
Chords Of Life17-Feb-11 0:02
Chords Of Life17-Feb-11 0:02 
GeneralRe: Suggestions for actually changing the brightness Pin
Chords Of Life17-Feb-11 0:04
Chords Of Life17-Feb-11 0:04 
GeneralRe: Suggestions for actually changing the brightness Pin
Chords Of Life17-Feb-11 0:05
Chords Of Life17-Feb-11 0:05 
GeneralMy vote of 2 Pin
Ziwdon1-Mar-10 14:29
Ziwdon1-Mar-10 14:29 
Generalthis could be handy Pin
Christ Kennedy14-Dec-09 18:05
mvaChrist Kennedy14-Dec-09 18:05 
GeneralRe: this could be handy Pin
Ron Beyer15-Dec-09 6:06
professionalRon Beyer15-Dec-09 6:06 
Well like I said, I work in the offshore industry. I was looking for a way to programmatically dim the screen, and all the examples I found were unmanaged C. Looking through the API documentation and the samples I found, along with a bunch of trial-and-error, I arrived at this solution.
GeneralGamma != Brightness Pin
csharprulez14-Dec-09 4:43
csharprulez14-Dec-09 4:43 
GeneralRe: Gamma != Brightness Pin
Ron Beyer14-Dec-09 5:20
professionalRon Beyer14-Dec-09 5:20 
GeneralWindows 7 Pin
Brad Bruce14-Dec-09 1:26
Brad Bruce14-Dec-09 1:26 
GeneralInteresting Pin
kornakar13-Dec-09 20:57
kornakar13-Dec-09 20:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.