Как программно изменить текущую тему Windows? - PullRequest
27 голосов
/ 13 февраля 2009

Я хочу, чтобы мои пользователи могли переключать текущую пользовательскую тему между Aero и Windows Classic (1). Есть ли способ, которым я могу сделать это программно?

Я не хочу открывать «Свойства дисплея», и я сомневаюсь, что нужно просто изменить реестр. (Для того чтобы изменения вступили в силу, требуется выйти из системы и снова войти в нее).

Скины приложений (с использованием библиотек Codejock ) также не работают.

Есть ли способ сделать это?

Приложение размещено / запущено на Windows Server 2008 over RDP .

(1) Данное приложение является размещенным «Удаленным приложением», и я хочу, чтобы пользователи могли изменять внешний вид отображаемого приложения в соответствии со своим рабочим столом.

Ответы [ 9 ]

64 голосов
/ 03 февраля 2011

Вы можете установить его с помощью следующей команды:

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

Предостережение в том, что это покажет диалог выбора темы. Вы можете убить этот диалог сразу после.

19 голосов
/ 20 марта 2011

Конечно, есть веские причины для того, чтобы захотеть изменить текущую тему программно. Например. автоматизированному инструменту тестирования может потребоваться переключение между различными темами, чтобы убедиться, что приложение корректно работает со всеми из них.

Как пользователь, вы можете изменить тему, дважды щелкнув файл .theme в Windwos Explorer, а затем закрыв всплывающее окно панели управления. Вы можете легко сделать то же самое из кода. Шаги ниже работают просто отлично для меня. Я тестировал только на Windows 7.

  1. Используйте SHGetKnownFolderPath(), чтобы получить папку «Local AppData» для пользователя. Файлы тем хранятся в подпапке Microsoft\Windows\Themes. Файлы тем, хранящиеся там, применяются напрямую, тогда как файлы тем, хранящиеся в другом месте, дублируются при их выполнении. Поэтому лучше использовать файлы только из этой папки.
  2. Используйте ShellExecute(), чтобы выполнить файл .theme, который вы нашли в шаге 1.
  3. Подождите, пока тема будет применена. Я просто оставляю мое приложение на 2 секунды.
  4. Позвоните FindWindow('CabinetWClass', 'Personalization'), чтобы получить дескриптор окна панели управления, которое всплывало при применении темы. Надпись «Персонализация», скорее всего, будет отличаться в англоязычных версиях Windows.
  5. Позвоните PostMessage(HWND, WM_CLOSE, 0, 0), чтобы закрыть окно панели управления.

Это не очень элегантное решение, но оно выполняет свою работу.

10 голосов
/ 22 апреля 2014

Я знаю, что это старый билет, но кто-то спросил меня, как это сделать сегодня. Итак, начиная с поста Майка выше, я исправил ситуацию, добавил комментарии и опубликую полный код консольного приложения C #:

</p> <pre><code>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 } }

2 голосов
/ 17 сентября 2012

Кроме поста "Яна Гойваертса": Я использую SendMessage вместо PostMessage. Разница в том, что SendMessage ожидает, пока команда не примет команду. Это означает, что в возврате SendMessages вы знаете, что диалоговое окно темы закрыто.

Так что, если вы начнете его с чудовищного (но гениального) метода rundll32.exe, предложенного "Campbell". Вам следует подождать секунду перед отправкой WM_CLOSE. В противном случае тема не будет установлена, и приложение сразу закроется.

Приведенный ниже фрагмент кода извлекает файл из ресурса (тематического пакета). Затем исполняет файл desk.cpl с помощью rundll32.exe, ожидает 3 сценария, затем отправляет 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;
    }
2 голосов
/ 13 февраля 2009

Полагаю, лучшее, что вы можете сделать, - это открыть целевой файл .msstyles (в c:\windows\resources\themes), в котором появится окно свойств экрана. На этом этапе вы можете использовать подклассы окна, чтобы программно нажимать правые кнопки.

0 голосов
/ 11 ноября 2018

Я только что понял, что вы можете дважды щелкнуть по теме, и она автоматически переключается - гораздо проще, так что просто выполнение темы работает, ex batch file:

:: Reactivate my theme after an remote desktop session
:: We must select another theme first before we can select ours again and hence re-activate Aero, please wait..."
@echo Off
"C:\Windows\Resources\Themes\aero.theme"
::echo "Simulating a pause while"
ping 127.0.0.1 -n 10 > null && "D:\Users\danielsokolowski\Windows 7 Aero Themes\`danielsokolowski` Theme (without Glass).theme"
::or ping 127.0.0.1 -n 3 > null && "%userprofile%\AppData\Local\Microsoft\Windows\Themes\`danielsokolowski` Theme (without Glass).theme"
0 голосов
/ 11 октября 2018

Работает на Windows 10.

это мой сценарий. Это меняет тему и закрывает окно. Я сохраняю его в командный файл и запускаю этот файл патча из TaskScheduler:

C:\WINDOWS\system32\rundll32.exe C:\WINDOWS\system32\themecpl.dll,OpenThemeAction C:\Users\xxx\Misc_computer_stuff\themes\my_fav_gr.theme

TIMEOUT 1 & REM Waits 1 seconds before executing the next command

TASKKILL /F /IM systemsettings.exe & close window

exit
0 голосов
/ 16 июля 2018

Хорошо, вот мое мнение об этом - VB скрипт . Это немного неприятно, но лучшее, что я могу придумать (к сожалению).

Для пользователя, который входит в систему, мы просто запускаем ChangeTheme.vbs, когда пользователь входит в систему (например, автозапуск). Сценарий запускается desk.cpl и передает ему необходимые параметры и название выбранной темы.

Можно запустить скрипт с параметрами или без параметров:

> ChangeTheme.vbs
> ChangeTheme.vbs AnyThemeName

Сценарий:

' ////////////////////////////////////////////////////////////////////
'
' Changes the theme.
'
' Name:
'     ChangeTheme.vbs
' Parameter 1: 
'     Theme name e.g. aero or anything 
'     located in in C:\Windows\Resources\Themes. 
'     If not present, a default theme will be used.
'
' Example: 
'     Inside a command line run
'     > ChangeTheme.vbs TheThemeName
'
' ////////////////////////////////////////////////////////////////////

If(Wscript.Arguments.Count <= 0) Then
  ' If no parameter was given we set the following theme as default
  selectedTheme = "aero"
Else
  ' Get theme via the first argument  
  selectedTheme = Wscript.Arguments(0)
End If

' Create WScript shell object
Set WshShell = WScript.CreateObject("WScript.Shell")

' Run the command to open the "theme application" (or whatever)
Set process = WshShell.Exec("rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:""C:\Windows\Resources\Themes\" & selectedTheme & ".theme""")

' Wait for the application to start

Wscript.Sleep 250

Success = False
maxTries = 20
tryCount = 0

Do Until Success = True

  Wscript.Sleep 1000

  ' Set focus to our application    
  ' If this fails, or the application loses focus, it won't work!    
  Success = WshShell.AppActivate(process.ProcessId)

  tryCount = tryCount + 1

  If (tryCount >= maxTries) Then
    ' If it does not work after maxTries we give up ..
    MsgBox("Cannot change theme - max tries exceeded ..")
    Exit Do
  End If

Loop

' The crucial part: Send keys ALT + B for applying the theme    
WshShell.Sendkeys "%(B)"

' Send key "escape" to close the window
WshShell.Sendkeys "{ESCAPE}" 

Надеюсь, это поможет.

0 голосов
/ 05 марта 2018

Команда для более новых версий 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 для персонализации команда "open" для расширений .theme & .themepack, взятых из реестра ...

После использования этой команды вы по-прежнему будете открывать окно «Персонализация», поэтому, чтобы закрыть его программно, вам придется использовать один из предложенных методов, упомянутых выше ... (лично я предпочитаю скрипт Powershell)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...