Windows Themes and .NET

Introduction

As many of you know, I am quite inquisitive and curious. I’ve got this inherent need to know how stuff works. Today’s topic is no different. Some time ago, I wrote an article or two about customizing the desktop settings. These articles covered setting desktop backgrounds, screensavers, and the odd theme. Some articles even demonstrated how to launch Desktop Icons and Control Panel applets from within your programs. I mention this here because you will need the skills taught in these articles to fully understand what you will be doing and how complicated it can get.

Today, you will learn how to launch the Windows Theme window, and how to set the theme from within your .NET programs.

Before you start with the code, please make sure you have gone through these articles:

Our Project

You can do this project in either C# or in VB.NET. It is a simple Windows Forms project. Add three buttons on your form and one TextBox.

Add a Class to your project. I have named my Class clsThemeDetails. After the class has been added, please add the following Namespaces:

C#

using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Threading;

VB.NET

Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.Win32

InterOpServices is used with the Windows API which you will add shortly. Win32 provides access to the Windows Registry. Add the code for the APIs now:

C#

      [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);


      private const uint WM_CLOSE = 0x10;

VB.NET

   <DllImport("user32.dll", EntryPoint:="FindWindow")>
   Private Shared Function FindWindow(ByVal sClassName As String, _
      ByVal sAppName As String) As IntPtr
   End Function

   <DllImport("user32.dll")>
   Private Shared Function SendMessage(ByVal hWnd As IntPtr, _
      ByVal Msg As UInteger, ByVal wParam As IntPtr, _
      ByVal lParam As IntPtr) As IntPtr
   End Function

   Private Const WM_CLOSE As UInteger = 16

The FindWindow API is used to find a certain window by its name or by its title. SendMessage is used to send a Message to a desired window; in this case, a Close message will be sent. The APIs are set up nicely now; you will make use of them a bit later.

Add the logic to start a process. The process that you will start will be the Theme window (the same as when you right-click on your desktop and choose Personalize).

C#

      private String StartProc(string strFile, string strArgs,
         int intSec, ref Boolean blnExited)
      {
         String strMsg = String.Empty;
         Process proc = new Process();

         proc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;

         proc.StartInfo.FileName = strFile;
         proc.StartInfo.Arguments = strArgs;

         proc.Start();

         blnExited = false;

         int intSecCount = 0;

         while (!blnExited && intSecCount < intSec)
         {

            blnExited = proc.HasExited;

            intSecCount++;

            Thread.Sleep(1000);

         }

         if (intSecCount == intSec)
         {

            strMsg = "Program did not close in expected time.";
         }

         return strMsg;
      }

VB.NET

   Private Function StartProc(ByVal strFile As String, _
         ByVal strArgs As String, ByVal intSec As Integer, _
         ByRef blnExited As Boolean) As String

      Dim strMsg As String = String.Empty
      Dim proc As Process = New Process()

      proc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized

      proc.StartInfo.FileName = strFile
      proc.StartInfo.Arguments = strArgs
      proc.Start()

      blnExited = False
      Dim intSecCount As Integer = 0

      While Not blnExited AndAlso intSecCount < intSec

         blnExited = proc.HasExited
         intSecCount += 1
         Thread.Sleep(1000)

      End While

      If intSecCount = intSec Then

         strMsg = "Program did not close in expected time."

      End If

      Return strMsg

   End Function

Add the Theme functions to your Class.

C#

      public Boolean SwitchTheme(string strPath)
      {

         try
         {

            Boolean blnExit = false;
            String ThemeOutput = this.StartProc("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:""
               + strPath + """, 30, ref blnExit);

            MessageBox.Show(ThemeOutput);

            Thread.Sleep(1000);

            // FindWindow(Nothing, "Personalization")
            IntPtr hWndTheme = FindWindow("CabinetWClass", null);

            SendMessage(hWndTheme, WM_CLOSE, IntPtr.Zero,
               IntPtr.Zero);

         }

         catch (Exception ex)
         {

            MessageBox.Show(ex.Message);


            return false;

         }

         return true;

      }

      public string CurrentTheme()
      {

         string strReg = @"HKEY_CURRENT_USERSoftwareMicrosoft
            WindowsCurrentVersionThemes";

         string strTheme = (string)Registry.GetValue(strReg,
            "CurrentTheme", string.Empty);

         return strTheme;

      }

VB.NET

   Public Function SwitchTheme(strPath As String) As Boolean

      Try

         Dim blnExit As Boolean = False

         Dim ThemeOutput As String = StartProc("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:""" _
            & strPath & """", 30, blnExit)

         MessageBox.Show(ThemeOutput)
         Thread.Sleep(1000)

         'FindWindow(Nothing, "Personalization")'
         Dim hWndTheme As IntPtr = FindWindow("CabinetWClass", _
            Nothing)

         SendMessage(hWndTheme, WM_CLOSE, IntPtr.Zero, IntPtr.Zero)

      Catch ex As Exception

         MessageBox.Show(ex.Message)

         Return False

      End Try

      Return True

   End Function

   Public Function CurrentTheme() As String

      Dim strReg As String = "HKEY_CURRENT_USERSoftware _
         MicrosoftWindowsCurrentVersionThemes"
      Dim strTheme As String = CStr(Registry.GetValue(strReg, _
         "CurrentTheme", String.Empty))

      Return strTheme

   End Function

The SwitchTheme function demonstrates one way to switch themes by running the Theme control Panel applet. CurrentTheme displays a name of the current theme.

On to your Form…

Add the following namespaces to your Form.

C#

using System.Globalization;
using System.Runtime.InteropServices;

VB.NET

Imports System.Globalization
Imports System.Runtime.InteropServices

Add the necessary APIs and objects.

C#

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

      bool blnAero = false;
      clsThemeDetails thm = new clsThemeDetails();

VB.NET

   <DllImport("dwmapi.dll")>
   Public Shared Function DwmIsCompositionEnabled(<Out> _
      ByRef pfEnabled As Boolean) As IntPtr
   End Function

   Private blnAero As Boolean = False

   Private thm As clsThemeDetails = New clsThemeDetails()

The DwmIsCompositionEnabled Windows API determines if Composition is enabled. If Aero is enabled, hence the Boolean flag underneath. The thm object represents the Class you created earlier.

Add code to display the Current theme behind button1.

C#

      private void button1_Click(object sender, EventArgs e)
      {

         MessageBox.Show("Current Theme = " + thm.CurrentTheme());

         Console.WriteLine(thm.CurrentTheme());
      }

VB.NET

   Private Sub button1_Click(ByVal sender As Object, _
         ByVal e As EventArgs) Handles button1.Click

      MessageBox.Show("Current Theme = " & thm.CurrentTheme())

      Console.WriteLine(thm.CurrentTheme())

   End Sub

Add code to switch Themes behind button2.

C#

      private void button2_Click(object sender, EventArgs e)
      {

         DwmIsCompositionEnabled(out blnAero);
         thm.SwitchTheme(textBox1.Text); //C:\Users\Hannes\
            AppData\Local\Microsoft\Windows\Themes\
            African W;//
      }

VB.NET

   Private Sub button2_Click(ByVal sender As Object, _
         ByVal e As EventArgs) Handles button2.Click

      DwmIsCompositionEnabled(blnAero)
      'C:UsersHannesAppDataLocalMicrosoftWindowsThemes'
      'African W'
      thm.SwitchTheme(textBox1.Text)

   End Sub

Add code to open the Theme Personalization Dialog box behind button3.

C#

      private void button3_Click(object sender, EventArgs e)
      {

         Process.Start("explorer.exe", "/n,shell:::{ED834ED6-4B5A
            -4bfe-8F11-A626DCB6A921} ");
      }

VB.NET

   Private Sub Button3_Click(sender As Object, e As EventArgs) _
         Handles Button3.Click
      Process.Start("explorer.exe", "/n,shell:::{ED834ED6-4B5A- _
         4bfe-8F11-A626DCB6A921} ")
   End Sub

Similar to the SwitchTheme function you created earlier, this button launches explorer.exe with the Theme Personalization GUID.

Conclusion

Playing with the Windows Desktop is extremely fun and not too difficult. Always explore, and most importantly, always try to have fun and make fun programs.

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read