如何以编程方式更改当前的Windows主题?

时间:2009-02-13 17:17:13

标签: windows themes

我想允许我的用户在Aero和Windows Classic(1)之间切换当前用户主题。有没有办法以编程方式执行此操作?

我不想弹出“显示属性”,我只是想改变注册表。 (这需要注销并重新登录才能使更改生效。)

应用程序换肤(使用Codejock库)也不起作用。

有没有办法做到这一点?

该应用程序在Windows Server 2008RDP托管/运行。

(1)有问题的应用程序是托管的“远程应用程序”,我希望用户能够更改显示的应用程序的外观以匹配他们的桌面。

12 个答案:

答案 0 :(得分:63)

您可以使用以下命令进行设置:

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

警告是,这将显示主题选择器对话框。您可以直接杀死该对话框。

答案 1 :(得分:19)

想要以编程方式更改当前主题肯定有充分的理由。例如。自动化测试工具可能需要在各种主题之间切换,以确保应用程序与所有主题一起正常工作。

作为用户,您可以通过双击Windwos Explorer中的.theme文件,然后关闭弹出的控制面板小程序来更改主题。您可以通过代码轻松完成相同的操作。以下步骤对我来说很合适。我只在Windows 7上测试过。

  1. 使用SHGetKnownFolderPath()获取用户的“Local AppData”文件夹。主题文件存储在Microsoft\Windows\Themes子文件夹中。存储在那里的主题文件直接应用,而存储在别处的主题文件在执行时会重复。因此,最好只使用该文件夹中的文件。
  2. 使用ShellExecute()执行您在步骤1中找到的.theme文件。
  3. 等待主题应用。我只是让我的应用程序睡了2秒钟。
  4. 调用FindWindow('CabinetWClass', 'Personalization')以获取应用主题时弹出的“控制面板”窗口的句柄。对于非美国英语版本的Windows,“个性化”标题可能会有所不同。
  5. 致电PostMessage(HWND, WM_CLOSE, 0, 0)以关闭“控制面板”窗口。
  6. 这不是一个非常优雅的解决方案,但它可以胜任。

答案 2 :(得分:10)

我知道这是一张旧票,但今天有人问我该如何做。所以从Mike的帖子开始,我清理了一些内容,添加了评论,并将发布完整的C#控制台应用程序代码:

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

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

答案 3 :(得分:2)

我相信你能做的最好的就是打开你的目标.msstyles文件(在c:\windows\resources\themes中),它会弹出显示属性框。此时,您可以使用窗口子类化以编程方式单击右键。

答案 4 :(得分:2)

除了“Jan Goyvaerts”之外: 我使用SendMessage而不是PostMessage。不同之处在于SendMessage等待窗口接收命令。这意味着在SendMessages返回时,您知道主题对话框已关闭。

因此,如果您使用“Campbell”建议的怪异(但天才)rundll32.exe方法启动它。您应该在发送WM_CLOSE之前等待一秒钟。否则,主题将不会被设置,应用程序会立即关闭。

下面的代码片段从资源(一个主题包)中提取文件。然后用rundll32.exe执行desk.cpl,等待3个sceonds,然后发送WM_CLOSE(0x0010),等待命令进行处理(设置主题所需的时间)。

    private Boolean SwitchToClassicTheme()
    {
        //First unpack the theme
        try
        {
            //Extract the theme from the resource
            String ThemePath = System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\Resources\Themes\ClassicTheme.themepack";
            //WriteFileToCurrentDirectory("ClassicTheme.theme", TabletConfigurator.Resources.ClassicTheme);
            if(File.Exists(ThemePath))
            {
                File.Delete(ThemePath);
            }
            if(File.Exists(ThemePath))
            {
                throw new Exception("The file '" + ThemePath + "' exists and can not be deleted. You can try to delete it manually.");
            }
            using (BinaryWriter sw = new BinaryWriter(new FileStream(ThemePath, FileMode.OpenOrCreate)))
            {
                sw.Write(TabletConfigurator.Resources.ClassicTheme);
                sw.Flush();
                sw.Close();
            }

            if(!File.Exists(ThemePath))
            {
                throw new Exception("The resource theme file could not be extracted");
            }

            //Set the theme file as like a user would have clicked it
            Boolean bTimedOut = false;
            String ThemeOutput = 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 + "\"", ref bTimedOut);

            System.Threading.Thread.Sleep(3000);
            //Wait for the theme to be set
            IntPtr hWndTheming = FindWindow("CabinetWClass", null);
            SendMessage(hWndTheming, (uint)WM_CLOSE, 0, 0);

            //using (Bitmap bm = CaptureScreenShot())
            //{
            //    Boolean PixelIsGray = true;
            //    while (PixelIsGray)
            //    {
            //        System.Drawing.Color pixel = bm.GetPixel(0, 0)
            //    }
            //}

        }
        catch(Exception ex)
        {
            ShowError("An exception occured while setting the theme: " + ex.Message);
            return false;
        }
        return true;
    }

答案 5 :(得分:0)

较新Windows版本的命令(Windows 8和8.1,尚未在W10上试过):

rundll32.exe themecpl.dll,OpenThemeAction %1

或完整路径:

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

基本上它是个性化CPL &#34;打开&#34;命令.theme&amp; .themepack扩展来自注册表...

使用此命令后,您仍然会打开个性化窗口,因此要以编程方式关闭它,您必须使用上述建议的方法之一...(我个人更喜欢Powershell脚本)

答案 6 :(得分:0)

好的,这是我的看法-http://localhost:3000/movies/。有点讨厌,但我能(最好)想出的最好的方法。

对于登录的用户,我们仅在用户登录时运行ChangeTheme.vbs(例如,自动运行)。该脚本以desk.cpl开始,并将必需的参数以及所选主题的名称传递给该脚本。

一个可以运行带有或不带有参数的脚本:

> ChangeTheme.vbs
> ChangeTheme.vbs AnyThemeName

脚本:

' ////////////////////////////////////////////////////////////////////
'
' 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}" 

希望有帮助。

答案 7 :(得分:0)

它适用于Windows 10。

这是我的脚本。它将更改主题并关闭窗口。我将其保存到批处理文件中,然后从TaskScheduler运行此补丁文件:

C:\WINDOWS\system32\rundll32.exe C:\WINDOWS\system32\themecpl.dll,OpenThemeAction C:\Users\xxx\Misc_computer_stuff\themes\my_fav_gr.theme

TIMEOUT 1 & REM Waits 1 seconds before executing the next command

TASKKILL /F /IM systemsettings.exe & close window

exit

答案 8 :(得分:0)

我刚刚意识到您可以双击主题并自动切换主题-简单得多,因此只需执行主题即可,例如批处理文件:

:: 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"

答案 9 :(得分:0)

我不确定这是否是新事物,但是您可以双击.theme文件,Windows 10将应用该主题。因此,您可以使用PowerShell轻松做到这一点:

$Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
Invoke-Expression $Windows10Theme

答案 10 :(得分:0)

我一直在尝试通过命令行更改Windows主题,并且我了解到通过执行主题文件,Windows 10也会应用它。因此,在批处理文件中,您可以使用以下几行之一:

C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme

C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme

请注意,根据系统用户配置,可能需要调整主题文件的路径。我强烈建议您使用除空格以外的名称来保存您的主题,因为这样可以更轻松地前进。执行该行会使您打开“设置”窗口。为了解决这个问题,我考虑改用VBS脚本。多亏了Patrick Haugh user1390106,可以更轻松地关闭“设置”窗口。

taskkill /F /IM systemsettings.exe

因此批处理文件的更新版本可能如下所示:

@echo off
if %1 == dark (
REM ================== Go Dark ==================
color 09
echo.
echo   Applying DARK MODE
echo   Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo   DONE
) else (
REM ============== Return to Light ==============
color 30
echo.
echo   Applying LIGHT MODE
echo   Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo   DONE
)
REM ================== Goodbye ==================
echo.
echo   Goodbye
cls
exit

请注意,根据系统用户配置,可能需要调整主题文件的路径。使用驱动器中某个位置的名称theme.bat保存以上脚本。 此批处理文件采用一个参数,该参数必须为dark或任何其他string。然后,您可以为此批处理文件准备两个快捷方式,每个快捷方式的属性均位于“快捷方式”选项卡上名为“目标”的框中,其中之一:

C:\full-path-to-your-batch-file\theme.bat dark

C:\full-path-to-your-batch-file\theme.bat light

请用该文件的实际路径替换“您的批处理文件的完整路径”。 以下是显示其工作原理的视频的链接:

a)天黑– https://youtu.be/cBcDNhAmfyM

b)重归光明– https://youtu.be/2kYJaJHubi4

请注意,我在这些视频中的脚本还激活/禁用了适用于Chrome的时尚插件。由于本文不是本文的主题,因此省略了解释如何完成该部分的内容。

答案 11 :(得分:0)

对于Windows 10,我在PowerShell中编写了这个简单的解决方案(也可以在DSC中使用)

# Apply your theme
& "C:\Windows\Resources\Themes\Brand.theme"
# We need to wait for the theme to be applied
Start-Sleep -s 5
# Close the settings window that is opened by the action above
$window = Get-Process | Where-Object {$_.Name -eq "SystemSettings"}
Stop-Process -Id $window.Id