2009-02-13 10 views
25

Quiero permitir a mis usuarios alternar el tema del usuario actual entre Aero y Windows Classic (1). ¿Hay alguna manera de que pueda hacer esto programáticamente?¿Cómo cambio el tema actual de Windows programáticamente?

No quiero que aparezcan las "Propiedades de pantalla", y tengo dudas acerca de simplemente cambiar el registro. (Esto requiere cerrar sesión y volver a iniciar sesión para que los cambios surtan efecto).

El desvanecimiento de aplicación (utilizando las bibliotecas Codejock) tampoco funciona.

¿Hay alguna forma de hacerlo?

La aplicación se aloja/ejecuta en un Windows Server 2008 sobre RDP.

(1) La aplicación en cuestión es una "Aplicación remota" alojada, y quiero que los usuarios puedan cambiar el aspecto de la aplicación mostrada para que coincida con su escritorio.

Respuesta

61

Se puede establecer mediante el siguiente comando:

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

advertencia es que esto va a mostrar el diálogo de selección de tema. Podrías matar ese diálogo enseguida.

+7

enfermo ... ¿Cómo te diste cuenta? – Claudiu

+3

imma da esto una recompensa ya que es la respuesta real – Claudiu

+0

Supongo que fue elaborado usando ProcessMonitor al cambiar el tema manualmente. –

2

Creo que lo mejor que puede hacer es abrir su archivo .msstyles de destino (en c:\windows\resources\themes), que abrirá el cuadro de propiedades de visualización. En este punto, puede usar la subclase de ventana para hacer clic en los botones de la derecha.

19

Sin duda hay buenas razones para querer cambiar el tema actual mediante programación. P.ej. una herramienta de prueba automatizada puede necesitar cambiar entre varios temas para asegurarse de que la aplicación funcione correctamente con todos ellos.

Como usuario, puede cambiar el tema haciendo doble clic en un archivo .theme en Windwos Explorer y luego cerrando el subprograma del Panel de control que aparece. Puedes hacer lo mismo fácilmente desde el código. Los pasos a continuación funcionan bien para mí. Solo he probado en Windows 7.

  1. Utilice SHGetKnownFolderPath() para obtener la carpeta "Local AppData" para el usuario. Los archivos de temas se almacenan en la subcarpeta Microsoft\Windows\Themes. Los archivos de temas almacenados allí se aplican directamente, mientras que los archivos de temas almacenados en otro lugar se duplican cuando los ejecuta. Entonces, lo mejor es usar archivos de esa carpeta solamente.
  2. Uso ShellExecute() para ejecutar el archivo .theme encontró en el paso 1.
  3. Espere a que el tema que se aplicará. Simplemente dejo que mi aplicación duerma durante 2 segundos.
  4. Llame al FindWindow('CabinetWClass', 'Personalization') para obtener el control de la ventana del Panel de control que apareció cuando se aplicó el tema. El título de "Personalización" probablemente será diferente en las versiones de Windows que no sean de inglés estadounidense.
  5. Llame al PostMessage(HWND, WM_CLOSE, 0, 0) para cerrar la ventana del Panel de control.

Esta no es una solución muy elegante, pero cumple su función.

+1

No encuentro '% LocalAppData% \ Microsoft \ Windows \ Themes' en Windows 8. Hay un'% AppData% \ Microsoft \ Windows \ Themes', pero no hay archivos THEME allí. – XP1

+0

Busque '.themepack'. Descargue o personalice manualmente un tema, haga clic derecho y "Guardar tema para compartir" como un archivo '.themepack'. Esto se puede "ejecutar" y cambia el tema actual. –

3

Además de la publicación de "Jan Goyvaerts": Uso SendMessage en lugar de PostMessage. La diferencia es que SendMessage espera que el comando sea captado por la ventana. Lo que significa que en los retornos SendMessages, sabes que el diálogo del tema está cerrado.

Así que si comienzas con el método monstruoso (pero genial) rundll32.exe sugerido por "Campbell". Debes esperar un segundo antes de enviar WM_CLOSE. De lo contrario, el tema no se establecerá y la aplicación se cerrará de inmediato.

El siguiente fragmento de código extrae un archivo del recurso (un paquete de temas). A continuación, ejecuta desk.cpl con rundll32.exe, espera 3 segundos, luego envía WM_CLOSE (0x0010), espera a que se procese el comando (el tiempo que tarda el tema en configurarse).

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

¿Qué es FindWindow()? StartProcessAndWait()? Enviar mensaje()? Este código parece útil, pero está incompleto como muestra. – Leigh

8

Sé que este es un billete de edad, pero alguien me preguntó cómo hacer esto hoy. Así que a partir de la entrada de Mike por encima de limpié las cosas, añadí comentarios, y publicará completa de código C# aplicación de consola:

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

Definitivamente me gusta esta solución. ¡Funciona de maravilla! ¡Gracias por compartir! –

0

El comando para las nuevas versiones de Windows (Windows 8 y 8.1, no se han probado en W10 aún) es:

rundll32.exe themecpl.dll,OpenThemeAction %1 

o con rutas completas:

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

Básicamente se trata de la Personalización CPL comando "abrir" para .theme & .themepack extensiones tomadas del registro ...

Aún así se terminará con la ventana de personalización abierta después de usar este comando, por lo que para cerrarlo programáticamente tendrá que usar uno de los métodos sugeridos mencionados anteriormente ... (Yo personalmente prefiero el guión de Powershell)

Cuestiones relacionadas