Как программно изменить текущую тему Windows?
Я хочу, чтобы мои пользователи могли переключать текущую пользовательскую тему между Aero и Windows Classic(1). Есть ли способ, которым я могу сделать это программно?
Я не хочу, чтобы всплыли "Свойства дисплея", и я сомневаюсь, просто изменить реестр. (Это требует выхода и повторного входа, чтобы изменения вступили в силу).
Скины приложений (с использованием библиотек Codejock) тоже не работают.
Есть ли способ сделать это?
Приложение размещается / запускается на Windows Server 2008 через RDP.
(1) Данное приложение является размещенным "Удаленным приложением", и я хочу, чтобы пользователи могли изменять внешний вид отображаемого приложения в соответствии со своим рабочим столом.
13 ответов
Вы можете установить его с помощью следующей команды:
rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:"C:\Windows\Resources\Themes\aero.theme"
Предостережение в том, что это покажет диалог выбора темы. Вы можете убить этот диалог сразу после.
Есть, конечно, веские причины для того, чтобы захотеть изменить текущую тему программно. Например, инструменту автоматического тестирования может потребоваться переключаться между различными темами, чтобы убедиться, что приложение работает со всеми из них правильно.
Как пользователь, вы можете изменить тему, дважды щелкнув .theme
файл в Windwos Explorer и затем закрытие всплывающего апплета панели управления. Вы можете легко сделать то же самое из кода. Следующие шаги работают нормально для меня. Я тестировал только на Windows 7.
- использование
SHGetKnownFolderPath()
получить папку "Local AppData" для пользователя. Файлы тем хранятся вMicrosoft\Windows\Themes
вложенная папка. Файлы тем, хранящиеся там, применяются напрямую, тогда как файлы тем, хранящиеся в другом месте, дублируются при их выполнении. Поэтому лучше использовать файлы только из этой папки. - использование
ShellExecute()
выполнить.theme
файл, который вы нашли в шаге 1. - Подождите, пока тема будет применена. Я просто позволил моему приложению спать в течение 2 секунд.
- Вызов
FindWindow('CabinetWClass', 'Personalization')
чтобы получить дескриптор окна панели управления, которое всплывало, когда тема была применена. Надпись "Персонализация", скорее всего, будет отличаться в не Windows-английских версиях Windows. - Вызов
PostMessage(HWND, WM_CLOSE, 0, 0)
закрыть окно панели управления.
Это не очень элегантное решение, но оно делает свою работу.
Я знаю, что это старый билет, но кто-то спросил меня, как это сделать сегодня. Итак, начиная с поста Майка выше, я все исправил, добавил комментарии и опубликую полный код консольного приложения 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
}
}
Я не уверен, что это ново, но вы можете просто дважды щелкнуть файл.theme, и Windows 10 применит тему. Следовательно, вы можете легко сделать это с помощью PowerShell:
$Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
Invoke-Expression $Windows10Theme
В дополнение к посту "Ян Гойваертс": я использую 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;
}
Я считаю, что лучшее, что вы можете сделать, это открыть целевой файл.msstyles (в c:\windows\resources\themes
), в результате чего появится окно свойств экрана. На этом этапе вы можете использовать подклассы окна, чтобы программно нажимать правые кнопки.
Команда для более новых версий 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
По сути, это команда open для Personalization CPL для расширений.theme & .themepack, взятых из реестра...
После использования этой команды вы по-прежнему будете открывать окно "Персонализация", поэтому, чтобы закрыть его программно, вам придется использовать один из предложенных методов, упомянутых выше... (лично я предпочитаю скрипт Powershell)
Я экспериментировал с изменением темы Windows через командную строку и узнал, что, запустив файл темы, он также применяется в Windows 10. Итак, в вашем пакетном файле вы можете использовать одну из следующих строк:
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme
или
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme
Обратите внимание, что путь к файлам темы может потребоваться для настройки в зависимости от конфигурации пользователя вашей системы. Я настоятельно рекомендую сохранять ваши темы с именами без пробелов, так как это значительно упрощает продвижение вперед. Выполнение такой строки оставляет вас с открытым окном настроек. Чтобы справиться с этим, я подумал об использовании сценария VBS. Благодаря Патрику Хо, пользователю 1390106, есть гораздо более простой способ закрыть окно настроек.
taskkill /F /IM systemsettings.exe
Итак, обновленная версия командного файла может выглядеть так:
@echo off
if %1 == dark (
REM ================== Go Dark ==================
color 09
echo.
echo Applying DARK MODE
echo Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo DONE
) else (
REM ============== Return to Light ==============
color 30
echo.
echo Applying LIGHT MODE
echo Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo DONE
)
REM ================== Goodbye ==================
echo.
echo Goodbye
cls
exit
Обратите внимание, что путь к файлам темы может потребоваться для настройки в зависимости от конфигурации пользователя вашей системы. Сохраните приведенный выше скрипт с именем theme.bat где-нибудь на вашем диске. Этот командный файл принимает один параметр, который должен быть либоdark
или любой другой string
. Затем вы можете подготовить два ярлыка для этого командного файла, каждый из которых содержит одно из следующих значений в поле "Цель" на вкладке "Ярлык" в его свойствах:
C:\full-path-to-your-batch-file\theme.bat dark
или
C:\full-path-to-your-batch-file\theme.bat light
Замените "full-path-to-your-batch-file" фактическим путем к этому файлу. Вот ссылки на видео, показывающие, как это работает:
а) Темнеет - https://youtu.be/cBcDNhAmfyM
б) Возвращение к Свету - https://youtu.be/2kYJaJHubi4
Обратите внимание, что мой сценарий в этих видеороликах также активирует / деактивирует плагин Stylish для Chrome. Я не стал объяснять, как я выполнил эту часть, так как это не является предметом данной статьи.
Я только что понял, что вы можете дважды щелкнуть по теме, и она автоматически переключается - намного проще, так что просто выполнение темы работает, 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"
Для Windows 10 я написал это простое решение (его также можно использовать в DSC) в PowerShell.
# Apply your theme
& "C:\Windows\Resources\Themes\Brand.theme"
# We need to wait for the theme to be applied
Start-Sleep -s 5
# Close the settings window that is opened by the action above
$window = Get-Process | Where-Object {$_.Name -eq "SystemSettings"}
Stop-Process -Id $window.Id
Хорошо, вот мое мнение об этом - сценарий 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}"
Надеюсь, это поможет.
Работает на 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
Вы можете просто открыть любой из файлов .theme, находящихся в C:\Windows\Resources\Themes\, чтобы изменить тему.
Единственная загвоздка в том, что после этого также открывается приложение настроек. Но мы можем убить его, используяStop-Process
в PowerShell
Invoke-Expression "C:\Windows\Resources\Themes\<theme_name>.theme"
Start-Sleep -Seconds 2
Stop-Process -Name SystemSettings
Например:
Invoke-Expression "C:\Windows\Resources\Themes\dark.theme"