How do I change the current Windows theme programmatically?

前端 未结 12 1944
轻奢々
轻奢々 2020-12-04 11:12

I want to allow my users to toggle the current user theme between Aero and Windows Classic(1). Is there a way that I can do this programatically?

I don\'t want to po

相关标签:
12条回答
  • 2020-12-04 12:02

    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
        }
    }
    

    0 讨论(0)
  • 2020-12-04 12:04

    I just realized you can double click the theme and it autoswitches it - much simpler, so just executing the theme works, ex batch file:

    :: Reactivate my theme after an remote desktop session
    :: We must select another theme first before we can select ours again and hence re-activate Aero, please wait..."
    @echo Off
    "C:\Windows\Resources\Themes\aero.theme"
    ::echo "Simulating a pause while"
    ping 127.0.0.1 -n 10 > null && "D:\Users\danielsokolowski\Windows 7 Aero Themes\`danielsokolowski` Theme (without Glass).theme"
    ::or ping 127.0.0.1 -n 3 > null && "%userprofile%\AppData\Local\Microsoft\Windows\Themes\`danielsokolowski` Theme (without Glass).theme"
    
    0 讨论(0)
  • 2020-12-04 12:09

    The command for newer Windows versions (Windows 8 and 8.1, haven't tried it on W10 yet) is:

    rundll32.exe themecpl.dll,OpenThemeAction %1
    

    or with full paths:

    C:\WINDOWS\system32\rundll32.exe C:\WINDOWS\system32\themecpl.dll,OpenThemeAction %LocalAppData%\Microsoft\Windows\Themes\yourtheme.theme
    

    Basically it's the Personalisation CPL "open" command for .theme & .themepack extensions taken from registry...

    You'll still end up with the Personalisation window beeing open after using this command so to close it down programatically you'll have to use one of the suggested methods mentioned above... (I personally prefer the Powershell script)

    0 讨论(0)
  • 2020-12-04 12:10

    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.

    0 讨论(0)
  • 2020-12-04 12:11

    I'm not sure if this is a new thing, but you can just double click the .theme file and Windows 10 will apply the theme. Hence, you can do this with PowerShell easily:

    $Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
    Invoke-Expression $Windows10Theme
    
    0 讨论(0)
  • 2020-12-04 12:16

    Okay so here is my take on this - a VB script. It's a bit nasty but the best I could come up with (sadly).

    For a user that logs in, we simply run ChangeTheme.vbs as the user logs in (e.g. autorun). The script starts desk.cpl and passes the required parameters to it as well as the name of the selected theme.

    One can run the script with or without parameters:

    > ChangeTheme.vbs
    > ChangeTheme.vbs AnyThemeName
    

    The script:

    ' ////////////////////////////////////////////////////////////////////
    '
    ' Changes the theme.
    '
    ' Name:
    '     ChangeTheme.vbs
    ' Parameter 1: 
    '     Theme name e.g. aero or anything 
    '     located in in C:\Windows\Resources\Themes. 
    '     If not present, a default theme will be used.
    '
    ' Example: 
    '     Inside a command line run
    '     > ChangeTheme.vbs TheThemeName
    '
    ' ////////////////////////////////////////////////////////////////////
    
    If(Wscript.Arguments.Count <= 0) Then
      ' If no parameter was given we set the following theme as default
      selectedTheme = "aero"
    Else
      ' Get theme via the first argument  
      selectedTheme = Wscript.Arguments(0)
    End If
    
    ' Create WScript shell object
    Set WshShell = WScript.CreateObject("WScript.Shell")
    
    ' Run the command to open the "theme application" (or whatever)
    Set process = WshShell.Exec("rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:""C:\Windows\Resources\Themes\" & selectedTheme & ".theme""")
    
    ' Wait for the application to start
    
    Wscript.Sleep 250
    
    Success = False
    maxTries = 20
    tryCount = 0
    
    Do Until Success = True
    
      Wscript.Sleep 1000
    
      ' Set focus to our application    
      ' If this fails, or the application loses focus, it won't work!    
      Success = WshShell.AppActivate(process.ProcessId)
    
      tryCount = tryCount + 1
    
      If (tryCount >= maxTries) Then
        ' If it does not work after maxTries we give up ..
        MsgBox("Cannot change theme - max tries exceeded ..")
        Exit Do
      End If
    
    Loop
    
    ' The crucial part: Send keys ALT + B for applying the theme    
    WshShell.Sendkeys "%(B)"
    
    ' Send key "escape" to close the window
    WshShell.Sendkeys "{ESCAPE}" 
    

    Hope that helps.

    0 讨论(0)
提交回复
热议问题