No announcement yet.

[Tutorial] Building a C++ DLL and script class to list graphics resolutions

  • Filter
  • Time
  • Show
Clear All
new posts

    [Tutorial] Building a C++ DLL and script class to list graphics resolutions

    Update: there is a simpler alternative now available for later builds of UDK, including 2012-05 onwards, located here. This tutorial is still a useful template for implementing your own DLLs.
    The console command SETRES is used to change graphics resolution. However, currently there is no ability to list all the available graphics resolutions on a client's machine.

    In this tutorial I will walk you through creating a Dynamic Link Library utility in C++, and a class in UnrealScript, that together will build a list of available graphics modes on the client PC – a useful feature for populating your UI options with a choice of screen resolutions.

    This is a moderately advanced topic, but you won't need any prior C++ experience. I've purposely kept the code simple and readable, with most of the functionality and flexibility in UnrealScript. The C++ part is mostly copy-paste.

    Summary of the steps involved
    • Setup a DLL project in Microsfot Visual C++ 2010 Express
    • Create and compile a C++ wrapper for the Windows function EnumerateDisplayModes
    • Create an UnrealScript class to call the DLL and construct a list of available resolutions

    Step 1 The DLL Project

    You can use any C++ IDE/compiler, but for this tutorial I'll be using Microsoft Visual C++ 2010 Express.
    • Select File -> New -> Project... to open the project template screen
    • Select Win32 from the list of options on the left, then Win32 Console Application from the centre
    • At the bottom of the template window enter DeviceInfo as the name for the project, and tick Create directory for solution (this is optional, but will keep you in sync with the tutorial)

    Click OK and the application wizard will appear
    • Click Next > to get to the options page
    • For the Application Type choose DLL
    • Under Additional Options select Empty Project

    Click Finish and a new Solution with the DeviceInfo project will be created for you.
    • From the main menu select Project -> Add New Item... to open the item template window. Be sure to choose from the Project menu as there are several 'add' options in the IDE
    • Select Code from the options on the left, and C++ File (.cpp) from the centre
    • Enter the name DeviceInfo.cpp for the filename
    • Click Add to finish

    You should now have an empty c++ source file. However, before we begin there is an important setting we must make to the compiler when working with the UDK.
    • In the Solution Explorer window right-click on the DeviceInfo project and choose Properties. Note: the project is beneath the Solution in the tree. If the next step doesn't make sense you probably opened the solution properties instead of the project
    • In the properties window locate the code generation settings: Configuration Properties -> C/C++ -> Code Generation
    • On the right-hand side select the drop-down box next to Struct Member Alignment and choose "4 byte (/Zp4)"

    This setting ensures that any struct we define in C++ that's intended to mirror one in UnrealScript will have the same byte padding and layout.

    Step 2 C++

    Copy and paste the following code into DeviceInfo.cpp

    /*  Include the windows header file, which resolves references to DEVMODE and EnumDisplaySettings */
    #include <windows.h>
    /*  Ensure compatibility between C and C++ code
    extern "C"
        /*  UDK_Resolution, a mirror of our UnrealScript struct Resolution.
            Both must have identical structure, and the C++ compiler should be set to pack on 4-byte boundaries (/Zp4)
        struct UDK_Resolution
            int PixelWidth, PixelHeight;
        /*  DLLEnumDisplaySettings
            A wrapper for the Windows function EnumDisplaySettings.
            UDK_Resolution * const udk_r
            This is a pointer to the local variable 'r' defined in our UnrealScript function, which allows us to
            modify it directly from the DLL. The names do not need to match with UnrealScript, and the prefix
            'udk_' is only to help define its role. The pointer is made const to add some safety. Pointers are
            powerful and brutal. Learn C/C++ before tinkering with them.
        __declspec(dllexport) int DLLEnumDisplaySettings( int modeNum, UDK_Resolution * const udk_r )
            DEVMODE dm;
            dm.dmSize = sizeof(DEVMODE);
            /*  Each call to EnumDisplaySettings populates 'dm' with the width, height, bit-depth and refresh rate
                for the device mode specified by modeNum. However, we're only concerned with resolution so we keep
                width and height, and discard the rest.
                The NULL value in the first parameter will query the current display adaptor which should be fine
                for our purposes.
            if ( EnumDisplaySettings(NULL, modeNum, &dm) )
                // copy the width and height properties of DEVMODE to UnrealScript's Resolution
                udk_r->PixelWidth    = dm.dmPelsWidth;
                udk_r->PixelHeight   = dm.dmPelsHeight;
                return 1;
            // EnumDisplaySettings returned false, meaning the specified modeNum does not exist
            return 0;
    Now from the Build menu choose Build Solution, or press F7. Visual Studio will auto-save the open cpp file and begin compiling. If everything is tickety-boo it should compile successfully, and a debug version of the DLL file will be created in the Debug folder of the DeviceInfo project. If you're using Win7 this will likely be:

    C:\Users\USERNAME\Documents\Visual Studio 2010\Projects\DeviceInfo\Debug\DeviceInfo.dll

    Copy DeviceInfo.dll and paste it into your UDK-version/Binaries/Win32/UserCode directory. On my workstation this is:


    DLLs only work with the 32-bit build of UDK. If you are working on a 64-bit platform you may be used to running the 64-bit version. Be sure to compile and run your UDK project against the Win32 binaries.

    Step 3 UnrealScript and testing

    Create a new class file for your UDK project with the name DeviceInfo.uc. Copy and paste the following code, and save the file.

    class DLLDeviceInfo extends Object
    /*  Resolution, holds the width and height of a display resolution.
    struct Resolution
        var int PixelWidth, PixelHeight;
    /*  An array of Resolutions, listing all the unique display resolutions found
    var array<Resolution> AllResolutions;
    /*  DLL import declaration, matching the call signature of the function defined in our DLL
    dllimport final function int DLLEnumDisplaySettings( int modeNum, out Resolution r );
    /*  EnumDeviceModes, builds a list of graphics resolutions supported by the hardware.
        Each call to DLLEnumDisplaySettings may return a new resolution, or a duplicate resolution with
        different bit-depth and refresh rate. For SETRES and our UI options we only require a list of the
        unique resolutions, so we'll filter out all the duplicates.
    function EnumDeviceModes()
        local Resolution    r;
        local int            modeNum;
        local int            key;
        local array<int>    keys;
        `log("Enumerating device modes...");
        // Start with mode 0
        modeNum = 0;
        // Query device mode 0, 1, 2, ... etc. DLLEnumDisplaySettings returns 0 when all modes have been reported
        while( DLLEnumDisplaySettings(modeNum, r) != 0 )
            // combine width and height into a single integer 'key'
            key = r.PixelWidth << 16 | r.PixelHeight;
        /*  This simple trick filters out duplicate resolutions by creating a unique key for each combination of
            width and height. If our key list doesn't yet contain an entry for this combo then we have a new
            resolution to add to our list of AllResolutions.
            Reduces hundreds of reported modes down to a handful of useable options. Which is nice.
            // Already added this combination of width and height?
            if ( keys.Find(key) == INDEX_NONE )
                // Add the new resolution
                // Add the key
            // next device mode
        // Report some useful info...
        `log(AllResolutions.Length @ "available resolutions from" @ modeNum @ "device modes");
        `log("The following resolutions are supported:");
        foreach AllResolutions(r)
            `log("  " $ r.PixelWidth @ "x" @ r.PixelHeight);
    To use the class you need to instantiate an instance of it and call EnumDeviceModes. Ideally this would be done as part of the initialisation and setup for your game since you only need to call it once if you keep the instance. Alternatively you could implement this into an existing user interface manager class or whatever you prefer.

    For testing we'll add it to the InitGame event in GameInfo.

    var DLLDeviceInfo DeviceInfo;
    event InitGame( string options, out string errorMessage )
        super.InitGame(options, errorMessage);
        DeviceInfo = new class'DLLDeviceInfo';
    Now compile and run your UDK project.

    The results will show up in the console and logfile. This example is from my workstation running an Nvidia GTX560Ti:

    [0002.60] ScriptLog: Enumerating device modes...
    [0002.62] ScriptLog: 16 available resolutions from 225 device modes.
    [0002.62] ScriptLog: The following resolutions are supported:
    [0002.62] ScriptLog:   640 x 480
    [0002.62] ScriptLog:   720 x 480
    [0002.62] ScriptLog:   720 x 576
    [0002.62] ScriptLog:   800 x 600
    [0002.62] ScriptLog:   1024 x 768
    [0002.62] ScriptLog:   1152 x 864
    [0002.62] ScriptLog:   1280 x 720
    [0002.62] ScriptLog:   1280 x 768
    [0002.62] ScriptLog:   1280 x 800
    [0002.62] ScriptLog:   1280 x 960
    [0002.62] ScriptLog:   1280 x 1024
    [0002.62] ScriptLog:   1360 x 768
    [0002.62] ScriptLog:   1366 x 768
    [0002.62] ScriptLog:   1600 x 900
    [0002.62] ScriptLog:   1600 x 1024
    [0002.62] ScriptLog:   1680 x 1050

    You can replace the debug version of the DLL with a release version at some point. Simply select Release from the solution configurations toolbar and rebuild. You'll find it in the release directory.

    Further reading and reference



    Thanks for this tutorial, Spoof, well done. It just so happens I'm working on my projects options menu and was soon going to explore gaining hardware device information for a list of resolutions. Thanks a lot.


      Thanks. This definitely saves me researching time


        Very nice tutorial
        Thank you very much indeed


          thanks spoof, very awesome tutorial !!!!