2009-02-13 17 views
25

Chcę umożliwić moim użytkownikom przełączanie bieżącego motywu użytkownika między Aero i Windows Classic (1). Czy istnieje sposób, że mogę to zrobić programowo?Jak programowo zmienić bieżący motyw systemu Windows?

Nie chcę wyświetlać "właściwości ekranu" i mam wątpliwości, czy wystarczy zmienić rejestr. (Wymaga to wylogowania i ponownego zalogowania, aby zmiany zaczęły obowiązywać).

Skórowanie aplikacji (przy użyciu bibliotek Codejock) również nie działa.

Czy jest sposób na zrobienie tego?

Aplikacja jest hostowana/uruchamiana na Windows Server 2008 przez RDP.

(1) Dana aplikacja jest hostowaną "aplikacją zdalną" i chcę, aby użytkownicy mogli zmieniać wygląd wyświetlanej aplikacji, aby pasowała do ich pulpitu.

Odpowiedz

61

Można go ustawić za pomocą następującego polecenia:

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

zastrzeżeniem jest to, że będzie to pokazać okno wyboru tematu. Możesz natychmiast zabić ten dialog.

+7

chory .. jak to wymyśliłeś? – Claudiu

+3

imma dać tę nagrodę, ponieważ jest to faktyczna odpowiedź – Claudiu

+0

Zgaduję, że został opracowany przy użyciu ProcessMonitor podczas ręcznej zmiany tematu. –

2

Uważam, że najlepiej jest otworzyć plik docelowy .msstyle (w c:\windows\resources\themes), który wyświetli okno właściwości wyświetlania. W tym momencie możesz użyć podklasy okna, aby programowo kliknąć odpowiednie przyciski.

19

Z pewnością istnieją uzasadnione powody, aby programowo zmienić bieżący motyw. Na przykład. zautomatyzowane narzędzie testowe może wymagać przełączania się między różnymi tematami, aby upewnić się, że aplikacja działa poprawnie z wszystkimi.

Jako użytkownik możesz zmienić motyw, klikając dwukrotnie plik .theme w Windwos Explorer, a następnie zamykając wyświetlany aplet panelu sterowania. Możesz łatwo zrobić to samo z kodu. Poniższe kroki działają dobrze dla mnie. Przetestowałem tylko na Windows 7.

  1. Użyj SHGetKnownFolderPath(), aby uzyskać folder "Local AppData" dla użytkownika. Pliki motywów są przechowywane w podfolderze Microsoft\Windows\Themes. Pliki motywów tam przechowywane są stosowane bezpośrednio, a pliki motywów przechowywane w innym miejscu są duplikowane po ich uruchomieniu. Najlepiej używać plików tylko z tego folderu.
  2. Użyj pliku ShellExecute(), aby wykonać plik .theme zlokalizowany w kroku 1.
  3. Poczekaj na zastosowanie motywu. Po prostu pozwoliłem mojej aplikacji spać przez 2 sekundy.
  4. Zadzwoń pod FindWindow('CabinetWClass', 'Personalization'), aby uzyskać uchwyt okna Panelu sterowania, które pojawiło się po zastosowaniu motywu. Podpis "Personalizacja" będzie prawdopodobnie inny w wersjach systemu Windows nie w języku angielskim.
  5. Zadzwoń pod numer PostMessage(HWND, WM_CLOSE, 0, 0), aby zamknąć okno Panelu sterowania.

To nie jest bardzo eleganckie rozwiązanie, ale spełnia swoją rolę.

+1

Nie mogę znaleźć '% LocalAppData% \ Microsoft \ Windows \ Themes' na Windows 8. Istnieje'% AppData% \ Microsoft \ Windows \ Themes', ale nie ma tam plików TEMATÓW. – XP1

+0

Poszukaj '.themepack'. Pobierz lub ręcznie dostosuj motyw, kliknij prawym przyciskiem myszy i "Zapisz motyw do udostępniania" jako plik '.themepack'. To może być "uruchomione" i zmienia bieżący motyw. –

3

Oprócz postu "Jan Goyvaerts": Używam SendMessage zamiast PostMessage. Różnica polega na tym, że SendMessage czeka, aż komenda zostanie odebrana przez okno. Oznacza to, że w SendMessages zwraca, wiesz, że okno tematu jest zamknięte.

Więc jeśli zaczniesz od monstrualnej (ale genialnej) metody rundll32.exe sugerowanej przez "Campbell". Powinieneś poczekać sekundę przed wysłaniem WM_CLOSE. W przeciwnym razie motyw nie zostanie ustawiony, a aplikacja zostanie natychmiast zamknięta.

Poniższy fragment kodu wyodrębnia plik z zasobu (plik themepack). Następnie wykonuje plik desk.cpl z plikiem rundll32.exe, czeka 3 sceondy, a następnie wysyła WM_CLOSE (0x0010), czeka na przetworzenie polecenia (czas potrzebny na ustawienie kompozycji).

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; 
    } 
+0

Co to jest FindWindow()? StartProcessAndWait()? Wyślij wiadomość()? Ten kod wygląda na użyteczny, ale jest niekompletny jako próbka. – Leigh

8

Wiem, że to stary bilet, ale ktoś mnie dzisiaj zapytał, jak to zrobić. Więc zaczynając od słupka Mike'a powyżej czyściłem rzeczy, dodane komentarze i będzie pisać pełny C# kod aplikacji konsoli:

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

Zdecydowanie podoba mi się to rozwiązanie. Działa jak marzenie! Dzięki za udostępnienie! –

0

komendy dla nowszych wersjach systemu Windows (Windows 8 i 8.1, nie próbowałem go na jeszcze W10) wynosi:

rundll32.exe themecpl.dll,OpenThemeAction %1 

lub z pełne ścieżki:

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

Zasadniczo jest to Personalizacja CPL Polecenie "otwórz" dla .theme &. Rozszerzenia thhemepack pobrane z rejestru ...

Po użyciu tego polecenia otworzy się okno Personalizacja, aby zamknąć programowo, musisz użyć jedna z sugerowanych metod wymienionych powyżej ... (ja osobiście wolę skrypt Powershell)

Powiązane problemy