Подтвердить что ты не робот

Как программно изменить текущую тему Windows?

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

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

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

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

Приложение размещается/запускается на Windows Server 2008 через RDP.

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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
    }
}

код >

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

Я только что понял, что вы можете дважды щелкнуть по теме, и она автоматически переключается - намного проще, так что просто выполнение темы работает, 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"

Ответ 7

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

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

Ответ 8

Хорошо, вот мое мнение об этом - 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}" 

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

Ответ 9

Работает на 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

Ответ 10

Я не уверен, что это новая вещь, но вы можете просто дважды щелкнуть файл .theme, и Windows 10 применит тему. Следовательно, вы можете легко сделать это с помощью PowerShell:

$Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
Invoke-Expression $Windows10Theme