ユーザーが現在のユーザーテーマを Aero とWindows Classic(1)の間で切り替えられるようにしたいのですが。これをプログラムで行う方法はありますか?
「表示プロパティ」をポップアップ表示したくないので、レジストリを変更するだけに疑問があります。 (これには、変更を有効にするために、ログアウトして再度ログインする必要があります)。
アプリケーションスキニング( Codejock ライブラリを使用)も機能しません。
これを行う方法はありますか?
アプリケーションは [Windows Server 2008 over [〜#〜] rdp [〜#〜] でホスト/実行されます。
(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()
を使用して、ユーザーの「ローカルAppData」フォルダーを取得します。テーマファイルは、_Microsoft\Windows\Themes
_サブフォルダーに格納されます。そこに保存されているテーマファイルは直接適用されますが、他の場所に保存されているテーマファイルは実行時に複製されます。したがって、そのフォルダのファイルのみを使用するのが最善です。ShellExecute()
を使用して、手順1で見つけた_.theme
_ファイルを実行します。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
}
}
</ code>
私ができる最善のことは、ターゲット.msstylesファイルを開くことです(c:\windows\resources\themes
)、表示プロパティボックスをポップアップします。この時点で、ウィンドウのサブクラス化を使用して、プログラムで右ボタンをクリックできます。
「Jan Goyvaerts」の投稿に加えて、PostMessageではなくSendMessageを使用しています。違いは、SendMessageがコマンドがウィンドウに取り込まれるのを待つことです。 SendMessagesで戻るということは、テーマダイアログが閉じていることを意味します。
したがって、 "Campbell"によって提案された巨大な(しかし天才的な)rundll32.exeメソッドで起動するとします。 WM_CLOSEを送信する前に、1秒待つ必要があります。それ以外の場合、テーマは設定されず、アプリケーションはすぐに閉じます。
以下のコードスニペットは、リソース(テーマパック)からファイルを抽出します。次に、rundll32.exeでdesk.cplを実行し、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;
}
テーマをダブルクリックして自動切り替えできることに気づきました-非常にシンプルなので、テーマを実行するだけで、バッチファイルから実行できます。
:: 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"
これが新しいものかどうかはわかりませんが、.themeファイルをダブルクリックするだけでWindows 10がテーマを適用します。したがって、PowerShellでこれを簡単に行うことができます。
$Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
Invoke-Expression $Windows10Theme
新しい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拡張子のPersonalization CPL "open"コマンドです...
このコマンドを使用した後も、パーソナライゼーションウィンドウが開いたままになるため、プログラムで閉じるには、上記の推奨方法のいずれかを使用する必要があります...(私は個人的には、Powershellスクリプトを好みます)
さて、これが私の見解です-a VBスクリプト 。それは少し厄介ですが、私が思いつくことができた最高の(悲しいことに)。
ログインするユーザーの場合、ユーザーがログインするときにChangeTheme.vbs
を実行するだけです(例:autorun)。スクリプトは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