How do I change the current Windows theme programmatically?

I know this is an old ticket, but somebody asked me how to do this today. So starting from Mike's post above I cleaned things up, added comments, and will post full C# console app code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Win32;

namespace Windows7Basic
{
    class Theming
    {
        /// Handles to Win 32 API
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string sClassName, string sAppName);
        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        /// Windows Constants
        private const uint WM_CLOSE = 0x10;

        private String StartProcessAndWait(string filename, string arguments, int seconds, ref Boolean bExited)
        {
            String msg = String.Empty;
            Process p = new Process();
            p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
            p.StartInfo.FileName = filename;
            p.StartInfo.Arguments = arguments;
            p.Start();

            bExited = false;
            int counter = 0;
            /// give it "seconds" seconds to run
            while (!bExited && counter < seconds)
            {
                bExited = p.HasExited;
                counter++;
                System.Threading.Thread.Sleep(1000);
            }//while
            if (counter == seconds)
            {
                msg = "Program did not close in expected time.";
            }//if

            return msg;
        }

        public Boolean SwitchTheme(string themePath)
        {
            try
            {    
                //String themePath = System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Resources\Ease of Access Themes\basic.theme";
                /// Set the theme
                Boolean bExited = false;
                /// essentially runs the command line:  rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:"%WINDIR%\Resources\Ease of Access Themes\classic.theme"
                String ThemeOutput = this.StartProcessAndWait("rundll32.exe", System.Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\shell32.dll,Control_RunDLL " + System.Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\desk.cpl desk,@Themes /Action:OpenTheme /file:\"" + themePath + "\"", 30, ref bExited);

                Console.WriteLine(ThemeOutput);

                /// Wait for the theme to be set
                System.Threading.Thread.Sleep(1000);

                /// Close the Theme UI Window
                IntPtr hWndTheming = FindWindow("CabinetWClass", null);
                SendMessage(hWndTheming, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            }//try
            catch (Exception ex)
            {
                Console.WriteLine("An exception occured while setting the theme: " + ex.Message);

                return false;
            }//catch
            return true;
        }

        public Boolean SwitchToClassicTheme()
        {
            return SwitchTheme(System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Resources\Ease of Access Themes\basic.theme");
        }

        public Boolean SwitchToAeroTheme()
        {
            return SwitchTheme(System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Resources\Themes\aero.theme");
        }

        public string GetTheme()
        {
            string RegistryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
            string theme;
            theme = (string)Registry.GetValue(RegistryKey, "CurrentTheme", string.Empty);
            theme = theme.Split('\\').Last().Split('.').First().ToString();
            return theme;
        }

        // end of object Theming
    }

    //---------------------------------------------------------------------------------------------------------------

    class Program
    {
        [DllImport("dwmapi.dll")]
        public static extern IntPtr DwmIsCompositionEnabled(out bool pfEnabled);

        /// ;RunProgram("%USERPROFILE%\AppData\Local\Microsoft\Windows\Themes\themeName.theme")      ;For User Themes
        /// RunProgram("%WINDIR%\Resources\Ease of Access Themes\classic.theme")                     ;For Basic Themes
        /// ;RunProgram("%WINDIR%\Resources\Themes\aero.theme")                                      ;For Aero Themes

        static void Main(string[] args)
        {
            bool aeroEnabled = false;
            Theming thm = new Theming();
            Console.WriteLine("The current theme is " + thm.GetTheme());

            /// The only real difference between Aero and Basic theme is Composition=0 in the [VisualStyles] in Basic (line omitted in Aero)
            /// So test if Composition is enabled
            DwmIsCompositionEnabled(out aeroEnabled);

            if (args.Length == 0 || (args.Length > 0 && args[0].ToLower(CultureInfo.InvariantCulture).Equals("basic")))
            {
                if (aeroEnabled)
                {
                    Console.WriteLine("Setting to basic...");
                    thm.SwitchToClassicTheme();
                }//if
            }//if
            else if (args.Length > 0 || args[0].ToLower(CultureInfo.InvariantCulture).Equals("aero"))
            {
                if (!aeroEnabled)
                {
                    Console.WriteLine("Setting to aero...");
                    thm.SwitchToAeroTheme();
                }//if
            }//else if
        }

        // end of object Program
    }
}


You can set it using the following command:

rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:"C:\Windows\Resources\Themes\aero.theme"

Caveat is that this will show the theme selector dialog. You could kill that dialog straight after.


There are certainly good reasons for wanting to change the current theme programmatically. E.g. an automated test tool may need to switch between various themes to make sure the application works correctly with all of them.

As a user, you can change the theme by double-clicking a .theme file in Windwos Explorer and then closing the Control Panel applet that pops up. You can easily do the same from code. The steps below work just fine for me. I've only tested on Windows 7.

  1. Use SHGetKnownFolderPath() to get the "Local AppData" folder for the user. Theme files are stored in the Microsoft\Windows\Themes subfolder. Theme files stored there are applied directly, while theme files stored elsewhere are duplicated when you execute them. So it's best to use files from that folder only.
  2. Use ShellExecute() to execute the .theme file you located in step 1.
  3. Wait for the theme to be applied. I simply let my app sleep for 2 seconds.
  4. Call FindWindow('CabinetWClass', 'Personalization') to get the handle of the Control Panel window that popped up when the theme was applied. The "Personalization" caption will likely be different on non-US-English versions of Windows.
  5. Call PostMessage(HWND, WM_CLOSE, 0, 0) to close the Control Panel window.

This isn't a very elegant solution, but it does the job.

Tags:

Windows

Themes