Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I change a visual theme programatically in Windows 8/8.1 by P/Invoking?

In C# or else VB.Net, knowing the ubication of a visual theme .theme file, I would like to apply that visual theme in Windows, without depending on other applications such as RunDll32.exe, just P/Invoking, but avoiding weird/strange things such as opening the personalization window and then using FindWindow function to close it, the procedure should be automated from platform invoking not interacting with other windows.

This question about how to apply a theme was asked before in S.O by many people (Included by me, with a solution via registry modification plus service stoping/resuming that only works under Windows 7), I think its time for an expert to illustrate us with a WinAPI approach that does not involve RunDll32.exe neither opening the personalization window.

I wonder this could be done by setting some values on the registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ThemeManager and then posting/sending a message via SendMessage or PostMessage or other function, or maybe notifying about an environment change via SendMessageTimeOut function or SHChangeNotify or SystemParametersInfo or another function, because in the uxtheme.dll library seems there is nothing usefull for this task, the question is what function and with what parameters to apply a visual theme change, there are some commercial applications that can do this, which are the steps to do it then?, I tried all those functions without success.


This is the solution I did for Windows 7 in the past, I remember that is not perfect because for some themes the colors were not applied properlly and only has beeen solved with an user session re-logon to affect changes properlly after modifications:

Private Sub SetAeroTheme(ByVal themeFile As String,
                         Optional ByVal colorName As String = "NormalColor",
                         Optional ByVal sizeName As String = "NormalSize")

    Dim regKeyPath As String = "Software\Microsoft\Windows\CurrentVersion\ThemeManager"

    Using themeService As New ServiceController("Themes")

        If themeService.Status = ServiceControllerStatus.Running Then
            themeService.Stop()
            themeService.WaitForStatus(ServiceControllerStatus.Stopped)
        End If

        Using regKey As RegistryKey = Registry.CurrentUser.OpenSubKey(regKeyPath, writable:=True)

            regKey.SetValue("LoadedBefore", "0", RegistryValueKind.String)
            regKey.SetValue("DllName", themeFile, RegistryValueKind.String)
            regKey.SetValue("ColorName", colorName, RegistryValueKind.String)
            regKey.SetValue("SizeName", sizeName, RegistryValueKind.String)

        End Using

        If themeService.Status = ServiceControllerStatus.Stopped Then
            themeService.Start()
            themeService.WaitForStatus(ServiceControllerStatus.Running)
        End If

    End Using

End Sub

In windows 8 I think because DWM composition changes just that code didn't worked anymore.

like image 650
ElektroStudios Avatar asked Jun 23 '15 09:06

ElektroStudios


1 Answers

There is an undocumented function named "SetSystemVisualStyle" described on pinvoke.net that allows you to change the current "msstyles" file. As this function is undocumented it comes with the caveat: "use at your own risk".

The following function signatures are from the site referenced above.

C# signature

[DllImport("UxTheme.Dll", EntryPoint = "#65", CharSet = CharSet.Unicode)]
public static extern int SetSystemVisualStyle(string pszFilename, string pszColor, string pszSize, int dwReserved);

usage:

// This will set your Visual Style to Luna
SetSystemVisualStyle(@"C:\WINDOWS\resources\Themes\Luna\Luna.msstyles", "Metallic", "NormalSize", 0);

VB.Net signature

<DllImport("UxTheme.DLL", BestFitMapping:=False, CallingConvention:=CallingConvention.Winapi, CharSet:=CharSet.Unicode, EntryPoint:="#65")> _
Shared Function SetSystemVisualStyle(ByVal pszFilename As String, ByVal pszColor As String, ByVal pszSize As String, ByVal dwReserved As Integer) As Integer
End Function

The OP asked that the following information be added to this answer.

The function itself does not properlly change some dialog colors and some control styles when a 3rd party theme is applied with a custom msstyles, but doing an experiment by testing all the possible values from 0 to Int32.Max to pass it to the reserved parameter of the SetSystemVisualTheme function, by the moment I discovered that a value of 65 fixes this colorization and styles issue.

like image 54
TnTinMn Avatar answered Oct 18 '22 13:10

TnTinMn