2009-02-13 83 views
25

我想让我的用户切换Aero和Windows Classic(1)之间的当前用户主题。有没有一种方法可以通过编程来实现?如何以编程方式更改当前的Windows主题?

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

有没有办法做到这一点?

该应用程序托管/运行于Windows Server 2008超过RDP。 (1)有问题的应用程序是托管的“远程应用程序”,我希望用户能够更改显示的应用程序的外观以匹配其桌面。

回答

61

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

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

需要注意的是,这将展会主题选择对话框。你可以直接杀掉那个对话框。

+7

生病了..你怎么知道的? – Claudiu 2011-04-08 18:53:39

+3

imma给这个赏金,因为它是实际的答案 – Claudiu 2011-04-08 21:24:27

+0

我猜测它是在手动更改主题时使用ProcessMonitor制定的。 – 2015-02-26 05:15:56

2

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

19

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

作为用户,您可以通过双击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)关闭控制面板窗口。

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

3

除了“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; 
    } 
8

我知道这是一张旧票,但有人问我今天该怎么做。因此,从麦克的帖子开始上面我清理东西,添加注释,将发布完整的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 
    } 
} 

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从注册表中取。主题& .themepack的扩展名“打开”命令

你仍然结束了个性化窗口使用此命令后beeing开放,使来关闭它编程,你将不得不使用上面提到的建议方法之一...(我个人比较喜欢Powershell脚本)

相关问题