2009-02-13 247 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腳本)

相關問題