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

我想让我的用户在Aero和Windows Classic之间切换当前的用户主题(1)。 有没有办法,我可以做这个编程?

我不想popup“显示属性”,我只是改变registry。 (这需要注销并重新login以使更改生效)。

应用程序蒙皮(使用Codejock库)也不起作用。

有没有办法做到这一点?

该应用程序通过RDP托pipe/运行在Windows Server 2008上。

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

您可以使用以下命令来设置它:

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

注意,这将显示主题选择器对话框。 你可以直接杀死那个对话框。

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

作为用户,您可以通过双击Windwos Explorer中的.theme文件来更改主题,然后关闭弹出的控制面板小程序。 你可以很容易地从代码做同样的事情。 下面的步骤对我来说工作得很好。 我只在Windows 7上测试过。

  1. 使用SHGetKnownFolderPath()获取用户的“本地应用程序数据”文件夹。 主题文件存储在Microsoft\Windows\Themes子文件夹中。 存储在那里的主题文件直接应用,而在其他地方存储的主题文件在执行时被复制。 所以最好只使用该文件夹中的文件。
  2. 使用ShellExecute()来执行在第1步中找到的.theme文件。
  3. 等待主题被应用。 我只是让我的应用程序睡2秒。
  4. 调用FindWindow('CabinetWClass', 'Personalization')来获取在应用主题时弹出的控制面板窗口的句柄。 在非美国英语版本的Windows上,“个性化”标题可能会有所不同。
  5. 调用PostMessage(HWND, WM_CLOSE, 0, 0)关闭控制面板窗口。

这不是一个非常优雅的解决方案,但它可以完成这项工作。

我知道这是一张旧票,但有人问我今天该怎么办。 所以从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 } } 

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

所以如果你用“Campbell”建议的怪异的(但是很有意思的)rundll32.exe方法启动它。 在发送WM_CLOSE之前,您应该等待一会儿。 否则,主题将不会被设置,并且应用程序立即关闭。

下面的代码片段从资源(一个themepack)中提取一个文件。 然后用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; } 

我相信你可以做的最好的是打开你的目标.msstyles文件(在c:\windows\resources\themes ),这将弹出显示属性框。 在这一点上,您可以使用窗口子类编程方式单击正确的按钮。