Monday, August 09, 2004

 

Use XP Theme

对于Exe文件实现XP Theme的简单方式有两种:

1.
插入如下的一个Manifest文件

[code]
?xml version="1.0" encoding="UTF-8" standalone="yes"?>
assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="" type="win32" />
description>
/description>
dependency>
dependentAssembly>
assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="x86"/>
/dependentAssembly>
/dependency>
/assembly>
[/code]

要么你的程序需要在同目录下附带一个appName.exe.manifest,要么将这个manifest嵌入你的app。最简单的就是用vs.net以资源文件方式打开你的程序集,然后将manifest文件导入,导入类型为 RT_MANIFEST ,resource ID 设为1-101之间的一个整数,然后save。

Great for 1.0, however in 1.1, all you have to do is call Application.EnableVisualStyles() prior to launching your form.

或者使用如下的函数

作者:Holger Sauer
出处:http://search.cpan.org/src/AUTRIJUS/PAR-0.78/contrib/file2resourceadder.c

[code]
//**************************************
//INCLUDE files for :File2ResourceAdder
//**************************************
windows.h
winbase.h
stdio.h
string.h
time.h

//**************************************
// Name: File2ResourceAdder
// Description:File2ResourceAdder adds files found in the current directory to a Windows PE-file with resources (e.g. EXE/DLL, no RES/RC) as binary data (RT_RCDATA) with the file name (without extension) as the resource name.
It's a commandline tool for windows.
// By: Holger Sauer
//
//
// Inputs:None
//
// Returns:None
//
//Assumes:File2ResourceAdder
The File2ResourceAdder (also called "F2RA" within this document) searches the
current(!) directory and adds all files it finds as resources to a MS Windows
resource file (e.g. DLL or EXE, but no RES or RC). The new resources will have
as their name the filename (without extension) in capital letters and their type
will be RT_RCDATA as their content is stored as binary data.
During testing F2RA it appeared, that it could only handle about 30-40 files
one time, so please split your files if it's too big. It also can only handle
files smaller than 64kB.
To use F2RA, please run the executable program with the target resource file
as its first parameter.
File2ResourceAdder was written in April 2003 by Holger Sauer (www.h-sauer.de)
All rights are reserved. It is published under the GNU GPL 2.
//
//Side Effects:It does only add about 30-40 files at once, so please split your directories, it you have more files. The file size must also fit.
//This code is copyrighted and has limited warranties.
//Please see http://www.Planet-Source-Code.com/xq/ASP/txtCodeId.6051/lngWId.3/qx/vb/scripts/ShowCode.htm
//for details.
//**************************************

/*
File2ResourceAdder
Copyright (C) 2003 Holger Sauer, Leipzig/Germany
--------------------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
--------------------------------------------------------------------------------
File name:ResCreator.c
Description: File2ResourceAdder - commandline version
Created: 08.04.2003
Updated: 10.04.2003
*/
#define _WIN32_WINNT 0x0400
#include "windows.h"
#include "winbase.h"
#include
#include
#include
#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC CLK_TCK
#endif
// delay some milliseconds
void delay(unsigned short msec)


{
clock_t t0;
unsigned long diff = 0L;
for (t0 = clock(); diff < (unsigned long)msec; )


{
diff = (unsigned long)(clock() - t0);
diff *= 1000L;
diff /= CLOCKS_PER_SEC;
}
}
// this has to be high enough for number of elements in split array!
#define MAXELEMENTS 20
// split string elements
char **split_string (char *delim, char *str)


{
char *token;
static char *list[MAXELEMENTS];
int i = 0;
token = strtok(str, delim);
list[i++] = token;
while( ((token = strtok(NULL, delim)) != NULL) &&
(i < MAXELEMENTS))


{
/*buffer overflow is bad.*/
list[i++] = token;
}
return(list);
}
// do resource-adding for given file
void DoItWithFile(char *FileName, DWORD FileSize, HANDLE hUpdateRes)


{
BOOL result;
FILE *stream;
char *lpResBuffer;
long lngResLength = 0;
long lngSize = 0;
char **splitstring;
if ((stream = fopen(FileName, "r")) == NULL)


{
printf("Cannot open source file %s for reading.", FileName);
}
else


{
// get file size
rewind (stream);
lpResBuffer = (char*) malloc (FileSize + 128);
if (lpResBuffer == NULL)


{
printf("Could not reserve memory for temporary file buffer.\n");
}
else


{
lngSize = 0;
// load file content
while (!feof(stream))


{
if (lngSize < (FileSize + 128))


{
lpResBuffer[lngSize] = fgetc(stream);
lngSize++;
}
}
lpResBuffer[lngSize+1] = 0;
// change file names etc.
splitstring = split_string(".",_strupr(FileName));
printf("Resource: %s (Size: %d Extension: %s).\n", splitstring[0], lngSize, splitstring[1]);
// update resource file
result = UpdateResource(hUpdateRes,
RT_RCDATA,
splitstring[0],
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
lpResBuffer,
lngSize);
if (!result)


{
printf("Could not add resource.");
}
free(lpResBuffer);
}
fclose(stream);
}
}
// do main stuff
int main(int argc, char *argv[])


{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
HANDLE hUpdateRes;
char *DLLFile;
BOOL fFinished = FALSE;
long lngFFLength = 0;
int i = 0;
printf("File2ResourceAdder\nWritten by Holger Sauer (www.h-sauer.de)\n\n");
if (argc < 2)


{
printf("Not enough parameters given.\n");
//printf("X: " RT_HTML ".\n");
}
else


{
if ((strcmp(argv[1],"-h") == 0) || (strcmp(argv[1],"-?") == 0) || (strcmp(argv[1],"/?") == 0) || (strcmp(argv[1],"/h") == 0))


{
printf("%s [-?|FileName]\n"
"options: -?- display this help screen\n"
" FileName - name of file the resources are to be added\n"
"File2ResourceAdder searches the current directory\n"
"and adds all files as resources to a given file.\n", argv[0]);
}
else


{
// open resource file
DLLFile = argv[1];
hUpdateRes = BeginUpdateResource(DLLFile, FALSE);
if ((hUpdateRes == NULL) || (hUpdateRes == 0))


{
printf("Error opening target file %s.\n", DLLFile);
}
else if (hUpdateRes < 0)


{
printf("Error opening target file %s.\n", DLLFile);
}
else


{
printf("Used target file: %s.\n",argv[1]);
hFind = FindFirstFile("*.*", &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)


{
printf ("Invalid file handle. Error is %d\n", GetLastError ());
}
else


{
while (!fFinished)


{
if ((strcmp(FindFileData.cFileName,".") == 0) || (strcmp(FindFileData.cFileName,"..") == 0))


{
// skip system directories
}
else if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)


{
// skip directories
}
else


{
lngFFLength = (FindFileData.nFileSizeHigh * (MAXDWORD)) + FindFileData.nFileSizeLow;
DoItWithFile(FindFileData.cFileName, lngFFLength, hUpdateRes);
}
if (!FindNextFile(hFind, &FindFileData))


{
if (GetLastError() == ERROR_NO_MORE_FILES)


{
fFinished = TRUE;
printf("Done.");
}
else


{
printf("Cannot find next file.\n");
}
}
}
}
}
if (!EndUpdateResource(hUpdateRes, FALSE))


{
printf("Could not write changes to file.");
}
else


{
printf("Changes written to target file.\n");
}
}
}
return (0);
}
[/code]

2.
在main函数的第一行写入
Application.EnableVisualStyles();

对于Dll文件则可参见

How to apply Windows SP themes to Office COM add-ins
http://support.microsoft.com/default.aspx?scid=kb;en-us;830033

Microsoft Visual Basic 6.0
Visual Basic 6.0 does not support themes. Add-ins cannot be themed by using Visual Basic 6.0.

back to the top
Microsoft Visual C++ 6.0
To use Visual C++ 6.0 to enable a COM add-in to opt-in to Windows XP themes, follow these steps: 1. Create a manifest file that contains the following information. Customize the information for your specific add-in:


processorArchitecture="*"
type="win32"
name="MyOfficeNetAddin"
version="1.0.0.0"/>
My Office Addin built with .Net


type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.1.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>





2. Do one of the following: • Include the following preprocessor instruction: #define ISOLATION_AWARE_ENABLED


• Compile with:/D ISOLATION_AWARE_ENABLED



3. Add the manifest file to your resource file, as in the following example:#include "windows.h"
ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "mydllname.dl.manifest"


back to the top
Microsoft Visual Studio .NET and Managed Languages
To use Visual Studio .NET and the .NET Framework to enable Windows XP themes for a COM add-in, follow these steps.

Note In this example, C# is used to enable a themed activation context on a Windows form. Also, for Windows themes to be enabled for buttons, check boxes, radio buttons, and group boxes, the FlatStyle property of those objects must be set to System. 1. Include the following information in a .cs file. Customize the information for your specific add-in:using System.Runtime.InteropServices;
using System;
using System.Security;
using System.Security.Permissions;
using System.Collections;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace MyOfficeNetAddin
{
///
/// This class is intended to use with the C# 'using' statement in
/// to activate an activation context for turning on visual theming at
/// the beginning of a scope, and have it automatically deactivated
/// when the scope is exited.
///


[ SuppressUnmanagedCodeSecurity ]
internal class EnableThemingInScope : IDisposable
{
// Private data
private uint cookie;
private static ACTCTX enableThemingActivationContext;
private static IntPtr hActCtx;
private static bool contextCreationSucceeded = false;

public EnableThemingInScope(bool enable)
{
cookie = 0;
if (enable && OSFeature.Feature.IsPresent(OSFeature.Themes))
{
if (EnsureActivateContextCreated())
{
if (!ActivateActCtx(hActCtx, out cookie))
{
// Be sure cookie always zero if activation failed
cookie = 0;
}
}
}
}

~EnableThemingInScope()
{
Dispose(false);
}

void IDisposable.Dispose()
{
Dispose(true);
}

private void Dispose(bool disposing)
{
if (cookie != 0)
{
if (DeactivateActCtx(0, cookie))
{
// deactivation succeeded...
cookie = 0;
}
}
}

private bool EnsureActivateContextCreated()
{
lock (typeof(EnableThemingInScope))
{
if (!contextCreationSucceeded)
{
// Pull manifest from the .NET Framework install
// directory

string assemblyLoc = null;

FileIOPermission fiop = new FileIOPermission(PermissionState.None);
fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
fiop.Assert();
try
{
assemblyLoc = typeof(Object).Assembly.Location;
}
finally
{
CodeAccessPermission.RevertAssert();
}

string manifestLoc = null;
string installDir = null;
if (assemblyLoc != null)
{
installDir = Path.GetDirectoryName(assemblyLoc);
const string manifestName = "XPThemes.manifest";
manifestLoc = Path.Combine(installDir, manifestName);
}

if (manifestLoc != null && installDir != null)
{
enableThemingActivationContext = new ACTCTX();
enableThemingActivationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX));
enableThemingActivationContext.lpSource = manifestLoc;

// Set the lpAssemblyDirectory to the install
// directory to prevent Win32 Side by Side from
// looking for comctl32 in the application
// directory, which could cause a bogus dll to be
// placed there and open a security hole.
enableThemingActivationContext.lpAssemblyDirectory = installDir;
enableThemingActivationContext.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;

// Note this will fail gracefully if file specified
// by manifestLoc doesn't exist.
hActCtx = CreateActCtx(ref enableThemingActivationContext);
contextCreationSucceeded = (hActCtx != new IntPtr(-1));
}
}

// If we return false, we'll try again on the next call into
// EnsureActivateContextCreated(), which is fine.
return contextCreationSucceeded;
}
}

// All the pinvoke goo...
[DllImport("Kernel32.dll")]
private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
[DllImport("Kernel32.dll")]
private extern static bool ActivateActCtx(IntPtr hActCtx, out uint lpCookie);
[DllImport("Kernel32.dll")]
private extern static bool DeactivateActCtx(uint dwFlags, uint lpCookie);

private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;

private struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public string lpAssemblyDirectory;
public string lpResourceName;
public string lpApplicationName;
}
}
}


2. Create your form with the following wrapper. This procedure pushes a themed activation context before creating any controls:using( new EnableThemingInScope( true ) )
{
Form1 form1 = new Form1();
form1.CreateControl();
}

在给出了直接的答案之后,我们来来看看背后的一些技术细节

首先,下面的文档是不能不看的
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwxp/html/xptheming.asp

其次
Windows XP uses a C-style DLL to expose its theme functionality named UxTheme.dll (the "Ux" stands for User eXperience). The entry-points for this DLL are displayed below, along with some information about the type of function behind each entry-point.

http://www.xaml.net/articles/dll-api.aspx?dll=uxtheme

[ ] UXTHEME.DLL

Import Ordinal Hint Function Entry Point
------ ----------- ----------- ------------------------------------- -----------

Export Ordinal Hint Function Entry Point
------ ----------- ----------- ------------------------------------- -----------
[CR ] 5 (0x0005) 0 (0x0000) CloseThemeData 0x000042D0
[CR ] 6 (0x0006) 1 (0x0001) DrawThemeBackground 0x0000222C
[CR ] 12 (0x000C) 2 (0x0002) DrawThemeEdge 0x0001B989
[C ] 37 (0x0025) 3 (0x0003) DrawThemeIcon 0x00026C6A
[CR ] 38 (0x0026) 4 (0x0004) DrawThemeParentBackground 0x0000737F
[CR ] 39 (0x0027) 5 (0x0005) DrawThemeText 0x00002665
[CR ] 40 (0x0028) 6 (0x0006) EnableThemeDialogTexture 0x00009EBF
[C ] 41 (0x0029) 7 (0x0007) EnableTheming 0x000275CB
[CR ] 42 (0x002A) 8 (0x0008) GetCurrentThemeName 0x0000A6EC
[C ] 49 (0x0031) 9 (0x0009) GetThemeAppProperties 0x000278F4
[CR ] 50 (0x0032) 10 (0x000A) GetThemeBackgroundContentRect 0x000032CE
[CR ] 51 (0x0033) 11 (0x000B) GetThemeBackgroundExtent 0x0000754D
[C ] 52 (0x0034) 12 (0x000C) GetThemeBackgroundRegion 0x00007118
[C ] 53 (0x0035) 13 (0x000D) GetThemeBool 0x0000A1CC
[CR ] 54 (0x0036) 14 (0x000E) GetThemeColor 0x00003BF2
[C ] 55 (0x0037) 15 (0x000F) GetThemeDocumentationProperty 0x0001DC98
[C ] 56 (0x0038) 16 (0x0010) GetThemeEnumValue 0x00003BF2
[C ] 57 (0x0039) 17 (0x0011) GetThemeFilename 0x0002678B
[CR ] 58 (0x003A) 18 (0x0012) GetThemeFont 0x0000A0D1
[CR ] 59 (0x003B) 19 (0x0013) GetThemeInt 0x00003BF2
[C ] 64 (0x0040) 20 (0x0014) GetThemeIntList 0x0002685B
[CR ] 65 (0x0041) 21 (0x0015) GetThemeMargins 0x00007B98
[C ] 66 (0x0042) 22 (0x0016) GetThemeMetric 0x00026723
[CR ] 67 (0x0043) 23 (0x0017) GetThemePartSize 0x00003CEC
[C ] 68 (0x0044) 24 (0x0018) GetThemePosition 0x000267F6
[C ] 69 (0x0045) 25 (0x0019) GetThemePropertyOrigin 0x000266BE
[C ] 70 (0x0046) 26 (0x001A) GetThemeRect 0x0000A22A
[C ] 71 (0x0047) 27 (0x001B) GetThemeString 0x0002678B
[C ] 72 (0x0048) 28 (0x001C) GetThemeSysBool 0x000276F8
[C ] 73 (0x0049) 29 (0x001D) GetThemeSysColor 0x00027784
[C ] 74 (0x004A) 30 (0x001E) GetThemeSysColorBrush 0x000275F3
[C ] 75 (0x004B) 31 (0x001F) GetThemeSysFont 0x00026F7C
[C ] 76 (0x004C) 32 (0x0020) GetThemeSysInt 0x00027158
[C ] 77 (0x004D) 33 (0x0021) GetThemeSysSize 0x00027874
[C ] 78 (0x004E) 34 (0x0022) GetThemeSysString 0x000270BF
[CR ] 79 (0x004F) 35 (0x0023) GetThemeTextExtent 0x0000235F
[CR ] 80 (0x0050) 36 (0x0024) GetThemeTextMetrics 0x00007625
[C ] 81 (0x0051) 37 (0x0025) GetWindowTheme 0x00027673
[CR ] 82 (0x0052) 38 (0x0026) HitTestThemeBackground 0x0001C406
[CR ] 83 (0x0053) 39 (0x0027) IsAppThemed 0x00009976
[C ] 84 (0x0054) 40 (0x0028) IsThemeActive 0x0000A594
[CR ] 85 (0x0055) 41 (0x0029) IsThemeBackgroundPartiallyTransparent 0x000037E7
[C ] 86 (0x0056) 42 (0x002A) IsThemeDialogTextureEnabled 0x000276BC
[C ] 87 (0x0057) 43 (0x002B) IsThemePartDefined 0x00008467
[CR ] 88 (0x0058) 44 (0x002C) OpenThemeData 0x000097F3
[C ] 89 (0x0059) 45 (0x002D) SetThemeAppProperties 0x0001CFBF
[CR ] 90 (0x005A) 46 (0x002E) SetWindowTheme 0x00009D92
......
(按:以下N/A)

如果想直接使用其中的某个函数,可以采用下面的C++包装
Add XP Visual Style Support to OWNERDRAW Controls
By David Y. Zhao
http://www.codeproject.com/w2k/xpvisualstyle.asp

而在此之上再P/Incode包装一次,就可以用在.Net中,比如
Get the real XP look with Tab Pages
By Pierre Arnaud
http://www.codeproject.com/cs/miscctrl/themedtabpage.asp

A Managed C++ Wrapper Around the Windows XP Theme API
By Don Kackman
http://www.thecodeproject.com/managedcpp/ManagedUxTheme.asp

我个人不推荐
Adding XP Visual Style Support to OWNERDRAW Controls Using HTHEME Wrapper
By Pål K Tønder
http://www.codeproject.com/w2k/themewrapper.asp

接下来,NET Client Team的Raghavendra Prabhu写了一个Visual Styles的blog专栏,他简洁的回答了不少重要的问题,例如

http://blogs.msdn.com/rprabhu/archive/2003/09/28/56540.aspx

Q What does Application.EnableVisualStyles actually do?

Windows XP ships with two versions of the Common Controls Library (comctl32.dll) - versions 5.8 and 6.0. v5.8 renders controls in the "Classic" style that you get on Windows NT/2000 and Windows 9x. v6.0 renders controls using the XP Visual Styles look and feel. Since most Windows Forms controls are based on comctl32, how they are rendered depends on which version of comctl32 is used to do the rendering. By default, v5.8 is used to render the client area of the app and v6.0 is used to render the non-client area. That is why you see the title bar and window borders automatically render "themed", while the controls (like Button, TextBox, ListView, ComboBox and so on) have the classic look by default.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sbscs/setup/application_manifests.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sbscs/setup/activation_context_reference.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sbscs/setup/activation_context_reference.asp

In v1.0 of the Framework, the way to get visual styles in a Windows Forms app was to ship a manifest file with the app, that has information in it to indicate that v6.0 of comctl32 should be used for rendering. While this works fine, many developers felt it cumbersome to author, maintain and deploy manifest files. They felt the need to be able to do this programmatically. Now, the Platform SDK does provide API to do this. Basically, you need to create and activate an Activation Context that has pretty much the same DLL redirection information in it as the manifest file. The Activation Context API can be used to do this in a way suitable to your application.

If you take a look at these API, you will probably notice that they aren't very easy to use. While the advanced developers may like to tinker around with activation contexts, it is probably not something a developer who wants some "quick and dirty" code to get visual styles will do. So the Windows Forms team decided to wrap these API and expose a simple method that developers could call, that would isolate them from these complexities. So, essentially, when you call Application.EnableVisualStyles, we set up an activation context around the application's message loop, so that comctl32 function calls can be properly redirected to comctl32 v6.0. That way, you don't need to include a manifest with your app.

Q Why should Application.EnableVisualStyles be called before Application.Run?

As mentioned above, the activation context that we create is setup around the application's message loop. When EnableVisualStyles is called, we set a flag to indicate that a context needs to be activated. This flag is used to determine whether the activation context code is called or not during message loop setup. Application.Run sets up the message loop, so naturally the call to EnableVisualStyles should precede it.

Q I sometimes have weird problems with Application.EnableVisualStyles. Some controls don't seem to pick up theming; images that are assigned to TreeView nodes or ListView items sometimes don't render at all. However, when I use a manifest, I don't see these problems.

Yes, this is a known issue with the implementation of Application.EnableVisualStyles. The reason for this is as follows. As I mentioned before, the activation context is setup around the application's message loop. So any comctl32 handles created after the message loop is setup will be correctly redirected to v6.0. However, when you have the following code in Main:

Application.EnableVisualStyles();
Application.Run(new Form1());

Form1 is constructed before the message loop is setup. Therefore, any handle created in Form1's constructor code will result in that control being bound to v5.8 instead of v6.0, since the activation context has not yet been setup. This isn't as bad as it may sound, since in most cases, we delay handle creation as much as possible, so control handles aren't typically created in InitializeComponent. One rather common exception is the Imagelist - it turns out that in many cases, the Imagelist's handle is created in InitializeComponent, so you get a v5.8 imagelist that doesn't interop well with v6.0 controls on XP (on Windows Server 2003, the v5.8 imagelist does interop well with v6.0 controls, so you won't see this problem on that platform), the result being that the images don't show up. There are also some cases where some other control handles may get created early, in which case, those controls too will get bound to v5.8 and will not appear themed. We are obviously planning to fix this in the future, but for now, there are several workarounds that customers have used successfully. I list some of them here for reference:

Include a call to Application.DoEvents() before Application.Run().
Move the erring code (that causes handle creation to happen) from InitializeComponent to the Form's Load method.
Use a manifest!
Note that the above discussion applies only to the app's "Main Form", i.e., the Form that is passed to the Application.Run. Forms that are instantiated after the message loop is setup will be constructed after the context is activated, so code in their constructors shouldn't face this problem.

Q I am writing a Windows Forms control and I wish to make use of Visual Styles to ensure that my control's look and feel is consistent with the rest of the application. How can I do this?

Currently, there are no managed classes in the Framework to do this. You will need to p/invoke and use the UxTheme API. These API give you the ability to render common control parts, get information about visual style recommended colors, find out if a visual style is active and so on.

http://blogs.msdn.com/rprabhu/archive/2004/02/15/73443.aspx

[I thought I would write this piece as a response to Cory Smith's recent posts (here is the first post and then the followup) on the topic
http://addressof.com/blog/
] (按:其实我也是先看到Cory Smith的帖子)

If you are a control author or simply custom painting controls in your app, you have probably felt the need for a flag that tells you whether to render with visual styles or not. You could use this flag to switch between your visual styles rendering code and fallback code that works when visual styles aren't available or enabled.

In v1.1, the Framework unfortunately does not provide a property you could query to determine this. You need to p/invoke into UxTheme API - Cory has posted a code snippet that shows how to do this.

[code]
Private Structure DLLVERSIONINFO

Public cbSize As Integer

Public dwMajorVersion As Integer

Public dwMinorVersion As Integer

Public dwBuildNumber As Integer

Public dwPlatformID As Integer

End Structure

Private Declare Function DllGetVersion Lib "comctl32.dll" (ByRef version As DLLVERSIONINFO) As Integer

Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal path As String) As IntPtr

Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal library As IntPtr, ByVal procName As String) As IntPtr

Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal library As IntPtr) As Boolean

Private Declare Function IsThemeActive Lib "uxtheme.dll" () As Boolean

Private Declare Function IsAppThemed Lib "uxtheme.dll" () As Boolean

Private Function IsVisualStylesEnabled() As Boolean

Dim os As OperatingSystem = System.Environment.OSVersion

If os.Platform = PlatformID.Win32NT AndAlso (((os.Version.Major = 5) And (os.Version.Minor >= 1)) Or (os.Version.Major > 5)) Then

Dim uxTheme As IntPtr = LoadLibrary("uxtheme.dll")

If Not uxTheme.Equals(IntPtr.Zero) Then

Dim handle As IntPtr = GetProcAddress(uxTheme, "IsThemeActive")

If handle.Equals(IntPtr.Zero) Then

' an error occured, use GetLastError

Else

If IsThemeActive() AndAlso IsAppThemed Then

Dim version As DLLVERSIONINFO

version.cbSize = Len(version)

If DllGetVersion(version) = 0 Then

If version.dwMajorVersion > 5 Then

Return True

End If

End If

End If

End If

Else

' an error occured, use GetLastError

End If

FreeLibrary(uxTheme)

End If

Return False

End Function
[/code]

或者C++代码

[code]
///
/// Checks whether Visual Styles are enabled
///
protected bool VisualStylesEnabled
{
get
{
OperatingSystem os = System.Environment.OSVersion;

// check if the OS is XP or higher
if (os.Platform == PlatformID.Win32NT &&
((os.Version.Major == 5 && os.Version.Minor >= 1) ||
os.Version.Major > 5))
{
// are themes enabled
if (UxTheme.IsThemeActive() && UxTheme.IsAppThemed())
{
DLLVERSIONINFO version = new DLLVERSIONINFO();
version.cbSize = Marshal.SizeOf(typeof(DLLVERSIONINFO));

// are we using Common Controls v6
if (DllGetVersion(ref version) == 0)
{
return (version.dwMajorVersion > 5);
}
}
}

return false;
}
}
[/code]

Here are the conditions you need to check:

Does the current OS support visual styles? Use OSFeature.IsPresent(OSFeature.Themes) (OSFeature is a class in System.Windows.Forms)
Is a visual style currently active and applied to my application? This has to do with the fact that a user can turn off visual styles by either switching to Windows Classic mode or stopping/disabling the Themes service. Use a combination of IsAppThemed and IsThemeActive to determine this.
Here is the third condition and this is important - Is my app using ComCtl 6 to render its controls? This is equivalent to asking if Application.EnableVisualStyles() has been called or a manifest has been provided (see my earlier post on this for more information). To check this, you can p/invoke DllGetVersion and check if the version of comctl32.dll is 6 or greater.
No doubt, this is much too complicated an answer for a simple question - do I render with Visual Styles? In Whidbey, we plan to provide a single property you can query - something like Application.RenderWithVisualStyles (we are currently working out the naming and placement of this property).

(按:有人提过一个简单的方法
Detecting XP Themes
By Alastair Dallas
http://www.thecodeproject.com/csharp/xptheme.asp

查看
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ThemeManager

ThemeActive is "1" if Windows XP, "0" if Windows Classic.

If ThemeActive is "1", ColorName will be "NormalColor" for blue, "HomeStead" for olive green, or "Metallic" for silver. (All of these data values are type REG_SZ, or strings, by the way.)

[code]
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

class Class1 {
static void Main()

{
Console.WriteLine("CurrentTheme returns {0}", CurrentTheme());
Console.WriteLine("UxTheme::IsThemeActive returns {0}", IsThemeActive());
Console.WriteLine("UxTheme::IsAppThemed returns {0}", IsAppThemed());
Console.Read();

}
public enum Theme

{
WindowsClassic,
XPBlue,
XPGreen,
XPSilver

}
public static Theme CurrentTheme()

{
RegistryKey key =
Registry.CurrentUser.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\ThemeManager");
if (key != null)
{
if ("1" == (string) key.GetValue("ThemeActive"))
{
string s = (string) key.GetValue("ColorName");
if (s != null)
{
if (String.Compare(s, "NormalColor", true) == 0)
return Theme.XPBlue;
if (String.Compare(s, "HomeStead", true) == 0)
return Theme.XPGreen;
if (String.Compare(s, "Metallic", true) == 0)
return Theme.XPSilver;
}
}
}
return Theme.WindowsClassic;

}
[DllImport("UxTheme")]

static extern bool IsThemeActive();
[DllImport("UxTheme")]

static extern bool IsAppThemed();
} // end Class1
References

[/code]

但是这种方法不一定保证正确,注册表的问题太多啦。而且
IsThemeActive returns the systemwide setting. But since you can disable theming for a particular application, you should call IsAppThemed instead to see, whether the current application is themed or not.

The MSDN Library is very unclear in describing differences between IsThemeActive / IsAppThemed. Both methods cannot detect, if visual styles are supported by the programm itself (via a manifest), but at least IsAppThemed returns false if Visual Styles is disabled in compatibility setting for the application.


(又及:Delphi的实现方法可见
http://www.delphi-gems.com/ThemeManager.php
我不用Delphi或者BCB,不过据说这里包装的依然不完全
另外
Support for Windows Visual Styles (Themes) API in your controls
by Akzhan Abdulin
from http://www.akzhan.midi.ru/devcorner/articles/Windows%20Visual%20Styles%20(Themes)%20API%20Support%20in%20Your%20controls%20Eng.htm
也值得一看



<< Home

This page is powered by Blogger. Isn't yours?