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:
- Running Windows XP Control Panel Applets from Visual Basic.NET
- explorer.exe and Visual Basic.NET
- Wallpaper Changer
- Customizing Your Desktop with Visual Basic.NET
- Working with Desktop Icons from Visual Basic
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.