我想让我的用户在Aero和 Windows Classic(1) 之间切换当前用户主题。有没有办法可以以编程方式做到这一点?
我不想弹出“显示属性”,我怀疑只是更改注册表。(这需要注销并重新登录才能使更改生效)。
应用程序皮肤(使用Codejock库)也不起作用。
有没有办法做到这一点?
该应用程序通过RDP在Windows Server 2008上托管/运行。
(1) 有问题的应用程序是托管的“远程应用程序”,我希望用户能够更改显示的应用程序的外观以匹配他们的桌面。
您可以使用以下命令进行设置:
rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:"C:\Windows\Resources\Themes\aero.theme"
需要注意的是,这将显示主题选择器对话框。您可以立即终止该对话框。
想要以编程方式更改当前主题当然有充分的理由。例如,自动化测试工具可能需要在各种主题之间切换,以确保应用程序与所有主题都能正常工作。
作为用户,您可以通过在 Windwos Explorer 中双击.theme
文件,然后关闭弹出的控制面板小程序来更改主题。您可以轻松地从代码中执行相同的操作。下面的步骤对我来说很好。我只在 Windows 7 上测试过。
SHGetKnownFolderPath()
获取用户的“Local AppData”文件夹。主题文件存储在Microsoft\Windows\Themes
子文件夹中。存储在那里的主题文件被直接应用,而存储在别处的主题文件在您执行它们时会被复制。所以最好只使用该文件夹中的文件。ShellExecute()
执行.theme
您在步骤 1 中找到的文件。FindWindow('CabinetWClass', 'Personalization')
以获取应用主题时弹出的控制面板窗口的句柄。在非美国英语版本的 Windows 上,“个性化”标题可能会有所不同。PostMessage(HWND, WM_CLOSE, 0, 0)
以关闭控制面板窗口。这不是一个非常优雅的解决方案,但它可以完成工作。
我知道这是一张旧票,但今天有人问我该怎么做。因此,从上面 Mike 的帖子开始,我清理了一切,添加了评论,并将发布完整的 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
我相信您能做的最好的事情就是打开您的目标 .msstyles 文件(在 中c:\windows\resources\themes
),这将弹出显示属性框。此时,您可以使用窗口子类化以编程方式单击右侧按钮。
除了“Jan Goyvaerts”的帖子:我使用 SendMessage 而不是 PostMessage。不同之处在于 SendMessage 等待命令被窗口接收。这意味着在 SendMessages 返回时,您知道主题对话框已关闭。
因此,如果您使用“Campbell”建议的怪异(但天才)rundll32.exe 方法启动它。在发送 WM_CLOSE 之前,您应该等待一秒钟。否则将不会设置主题并且应用程序会立即关闭。
下面的代码片段从资源(主题包)中提取文件。然后用rundll32.exe执行desk.cpl,等待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;
}
较新的 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
基本上它是从注册表中获取的 .theme 和 .themepack 扩展的个性化 CPL “打开”命令......
使用此命令后,您仍然会打开个性化窗口,因此要以编程方式关闭它,您必须使用上面提到的建议方法之一......(我个人更喜欢 Powershell 脚本)
我刚刚意识到您可以双击主题并自动切换它 - 更简单,因此只需执行主题即可,例如批处理文件:
:: 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 主题,我了解到通过执行主题文件,它也被 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 脚本。感谢 Patrick Haugh user1390106,有一种更简单的方法可以关闭“设置”窗口。
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”替换为该文件的实际路径。以下是展示其工作原理的视频链接:
a) 走向黑暗 - https://youtu.be/cBcDNhAmfyM
b) 回归光明 – https://youtu.be/2kYJaJHubi4
请注意,我在这些视频中的脚本也会激活/停用 chrome 的 Stylish 插件。我省略了解释我是如何完成这部分的,因为它不是本文的主题。
对于 Windows 10,我在 PowerShell 中编写了这个简单的解决方案(也可以在 DSC 中使用)
# 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