Integrate thmviewer.exe and thmviewer.msi
Cannot build MSI until a few fixes are made to WixToolset.Core. Will enable this later.
Rob Mensching committed
Feb 1, 2019 at 14:32 UTC
e8d9c70934d8cae0d2769ab6ca5ad40d5f506968
22 files changed
+1532
-1
Tools.sln
+42
@@ -24,44 +24,86 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Tools.Core", "sr
24
EndProject
25
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.WixCop", "src\test\WixToolsetTest.WixCop\WixToolsetTest.WixCop.csproj", "{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}"
26
EndProject
27
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "thmviewer", "src\thmviewer\thmviewer.vcxproj", "{95228C13-97F5-484A-B4A2-ECF4618B0881}"
28
+EndProject
29
Global
30
GlobalSection(SolutionConfigurationPlatforms) = preSolution
31
Debug|Any CPU = Debug|Any CPU
32
+ Debug|x86 = Debug|x86
33
Release|Any CPU = Release|Any CPU
34
+ Release|x86 = Release|x86
35
EndGlobalSection
36
GlobalSection(ProjectConfigurationPlatforms) = postSolution
37
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.Build.0 = Debug|Any CPU
39
+ {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|x86.ActiveCfg = Debug|Any CPU
40
+ {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|x86.Build.0 = Debug|Any CPU
41
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.ActiveCfg = Release|Any CPU
42
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.Build.0 = Release|Any CPU
43
+ {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|x86.ActiveCfg = Release|Any CPU
44
+ {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|x86.Build.0 = Release|Any CPU
45
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.Build.0 = Debug|Any CPU
47
+ {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|x86.ActiveCfg = Debug|Any CPU
48
+ {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|x86.Build.0 = Debug|Any CPU
49
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.ActiveCfg = Release|Any CPU
50
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.Build.0 = Release|Any CPU
51
+ {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|x86.ActiveCfg = Release|Any CPU
52
+ {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|x86.Build.0 = Release|Any CPU
53
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
55
+ {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|x86.ActiveCfg = Debug|Any CPU
56
+ {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|x86.Build.0 = Debug|Any CPU
57
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
58
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.Build.0 = Release|Any CPU
59
+ {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|x86.ActiveCfg = Release|Any CPU
60
+ {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|x86.Build.0 = Release|Any CPU
61
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.Build.0 = Debug|Any CPU
63
+ {938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|x86.ActiveCfg = Debug|Any CPU
64
+ {938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|x86.Build.0 = Debug|Any CPU
65
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.ActiveCfg = Release|Any CPU
66
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.Build.0 = Release|Any CPU
67
+ {938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|x86.ActiveCfg = Release|Any CPU
68
+ {938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|x86.Build.0 = Release|Any CPU
69
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
70
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.Build.0 = Debug|Any CPU
71
+ {0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|x86.ActiveCfg = Debug|Any CPU
72
+ {0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|x86.Build.0 = Debug|Any CPU
73
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.ActiveCfg = Release|Any CPU
74
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.Build.0 = Release|Any CPU
75
+ {0DF5D4CF-8457-469D-8288-13775E984F70}.Release|x86.ActiveCfg = Release|Any CPU
76
+ {0DF5D4CF-8457-469D-8288-13775E984F70}.Release|x86.Build.0 = Release|Any CPU
77
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
79
+ {2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|x86.ActiveCfg = Debug|Any CPU
80
+ {2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|x86.Build.0 = Debug|Any CPU
81
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
82
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.Build.0 = Release|Any CPU
83
+ {2E54120B-8958-40B1-A7FC-851446994CD8}.Release|x86.ActiveCfg = Release|Any CPU
84
+ {2E54120B-8958-40B1-A7FC-851446994CD8}.Release|x86.Build.0 = Release|Any CPU
85
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
87
+ {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|x86.ActiveCfg = Debug|Any CPU
88
+ {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|x86.Build.0 = Debug|Any CPU
89
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
90
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.Build.0 = Release|Any CPU
91
+ {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|x86.ActiveCfg = Release|Any CPU
92
+ {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|x86.Build.0 = Release|Any CPU
93
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
95
+ {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|x86.ActiveCfg = Debug|Any CPU
96
+ {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|x86.Build.0 = Debug|Any CPU
97
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
98
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.Build.0 = Release|Any CPU
99
+ {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|x86.ActiveCfg = Release|Any CPU
100
+ {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|x86.Build.0 = Release|Any CPU
101
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|Any CPU.ActiveCfg = Debug|Win32
102
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|x86.ActiveCfg = Debug|Win32
103
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|x86.Build.0 = Debug|Win32
104
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|Any CPU.ActiveCfg = Release|Win32
105
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|x86.ActiveCfg = Release|Win32
106
+ {95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|x86.Build.0 = Release|Win32
107
EndGlobalSection
108
GlobalSection(SolutionProperties) = preSolution
109
HideSolutionNode = FALSE
appveyor.cmd
+5
-1
@@ -2,6 +2,8 @@
2
@pushd %~dp0
3
@set _P=%~dp0build\Release\publish
4
5
+nuget restore
6
+
7
dotnet build -c Release src\test\WixToolsetTest.BuildTasks
8
dotnet build -c Release src\test\WixToolsetTest.WixCop
9
@@ -15,7 +17,9 @@ dotnet publish -c Release -o %_P%\WixToolset.MSBuild\netcoreapp2.1\ -f netcoreap
17
18
dotnet pack -c Release src\dotnet-wix
19
dotnet pack -c Release src\WixToolset.MSBuild
18
-@rem dotnet pack -c Release src\WixToolset.Core.InternalPackage
20
+
21
+@rem Enable this build when WixToolset.Core is fixed to build the setup code correctly.
22
+@rem msbuild -p:Configuration=Release .\src\ThmViewerPackage\ThmViewerPackage.wixproj
23
24
@popd
25
@endlocal
appveyor.yml
+2
@@ -33,6 +33,8 @@ skip_tags: true
33
artifacts:
34
- path: build\Release\**\*.nupkg
35
name: nuget
36
+- path: build\Release\**\*.msi
37
+ name: msi
38
39
notifications:
40
- provider: Slack
src/Cpp.Build.props
new
+104
@@ -0,0 +1,104 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+<Project>
5
+ <PropertyGroup>
6
+ <Platform Condition=" '$(Platform)' == '' OR '$(Platform)' == 'AnyCPU' ">Win32</Platform>
7
+ <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\</IntDir>
8
+ <OutDir>$(OutputPath)$(Platform)\</OutDir>
9
+ </PropertyGroup>
10
+
11
+ <PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'=='15.0'">
12
+ <WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
13
+ </PropertyGroup>
14
+
15
+ <ItemDefinitionGroup>
16
+ <ClCompile>
17
+ <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
18
+ <WarningLevel>Level4</WarningLevel>
19
+ <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
20
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
21
+ <PrecompiledHeader>Use</PrecompiledHeader>
22
+ <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
23
+ <CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
24
+ <TreatWarningAsError>true</TreatWarningAsError>
25
+ <ExceptionHandling>false</ExceptionHandling>
26
+ <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
27
+ <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
28
+ <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) > 4 ">true</MultiProcessorCompilation>
29
+ </ClCompile>
30
+ <ResourceCompile>
31
+ <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
32
+ <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
33
+ </ResourceCompile>
34
+ <Lib>
35
+ <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
36
+ </Lib>
37
+ <Link>
38
+ <SubSystem>$(ProjectSubSystem)</SubSystem>
39
+ <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
40
+ <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
41
+ <GenerateDebugInformation>true</GenerateDebugInformation>
42
+ <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
43
+ <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
44
+ <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
45
+ </Link>
46
+ </ItemDefinitionGroup>
47
+
48
+ <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
49
+ <ClCompile>
50
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
51
+ </ClCompile>
52
+ </ItemDefinitionGroup>
53
+ <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
54
+ <ClCompile>
55
+ <CallingConvention>CDecl</CallingConvention>
56
+ </ClCompile>
57
+ </ItemDefinitionGroup>
58
+ <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
59
+ <ClCompile>
60
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
61
+ <OmitDefaultLibName>true</OmitDefaultLibName>
62
+ <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
63
+ </ClCompile>
64
+ </ItemDefinitionGroup>
65
+ <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
66
+ <ClCompile>
67
+ <Optimization>Disabled</Optimization>
68
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
69
+ <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
70
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
71
+ </ClCompile>
72
+ </ItemDefinitionGroup>
73
+ <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
74
+ <ClCompile>
75
+ <BasicRuntimeChecks></BasicRuntimeChecks>
76
+ <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
77
+ </ClCompile>
78
+ </ItemDefinitionGroup>
79
+ <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
80
+ <ClCompile>
81
+ <Optimization>MinSpace</Optimization>
82
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
83
+ <FunctionLevelLinking>true</FunctionLevelLinking>
84
+ <IntrinsicFunctions>true</IntrinsicFunctions>
85
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
86
+ </ClCompile>
87
+ <Link>
88
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
89
+ <OptimizeReferences>true</OptimizeReferences>
90
+ </Link>
91
+ </ItemDefinitionGroup>
92
+ <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
93
+ <ClCompile>
94
+ <BasicRuntimeChecks></BasicRuntimeChecks>
95
+ <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
96
+ </ClCompile>
97
+ </ItemDefinitionGroup>
98
+ <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
99
+ <Link>
100
+ <KeyFile>$(LinkKeyFile)</KeyFile>
101
+ <DelaySign>$(LinkDelaySign)</DelaySign>
102
+ </Link>
103
+ </ItemDefinitionGroup>
104
+</Project>
src/Directory.Build.props
+2
@@ -8,6 +8,7 @@
8
<PropertyGroup>
9
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10
<EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11
+ <MSBuildWarningsAsMessages>MSB3246</MSBuildWarningsAsMessages>
12
13
<ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
14
<BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
@@ -22,5 +23,6 @@
23
</PropertyGroup>
24
25
<Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " />
26
+ <Import Project="Wix.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.wixproj' " />
27
<Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
28
</Project>
src/ThmViewerPackage/Package.wxs
new
+36
@@ -0,0 +1,36 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
4
+ <Product Name="WiX Toolset Theme Viewer" Manufacturer="WiX Toolset" Language="1033"
5
+ Version="!(bind.fileVersion.ThmViewerFile)" UpgradeCode="59c4b122-5167-445b-8fc4-09dcd4eced89">
6
+ <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
7
+
8
+ <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
9
+ <MediaTemplate />
10
+
11
+ <Feature Id="Main">
12
+ <ComponentGroupRef Id="Components" />
13
+ </Feature>
14
+ </Product>
15
+
16
+ <Fragment>
17
+ <ComponentGroup Id="Components" Directory="INSTALLFOLDER:\bin\">
18
+ <Component>
19
+ <File Id="ThmViewerFile" Source="thmviewer.exe" />
20
+ <Shortcut Name="!(bind.property.ProductName)" Directory="ShortcutFolder" Advertise="yes" />
21
+ </Component>
22
+ </ComponentGroup>
23
+ </Fragment>
24
+
25
+ <Fragment>
26
+ <Directory Id="TARGETDIR" Name="SourceDir">
27
+ <Directory Id="ProgramFilesFolder">
28
+ <Directory Id="INSTALLFOLDER" Name="WiX Toolset v4.0\" />
29
+ </Directory>
30
+ <Directory Id="ProgramMenuFolder">
31
+ <Directory Id="ShortcutFolder" Name="WiX Toolset\" />
32
+ </Directory>
33
+ </Directory>
34
+ </Fragment>
35
+
36
+</Wix>
src/ThmViewerPackage/ThmViewerPackage.wixproj
new
+35
@@ -0,0 +1,35 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <Import Project="..\Directory.Build.props" />
4
+
5
+ <PropertyGroup>
6
+ <ProjectGuid>59c4b122-5167-445b-8fc4-09dcd4eced89</ProjectGuid>
7
+ <OutputName>thmviewer</OutputName>
8
+ <OutputType>Package</OutputType>
9
+ </PropertyGroup>
10
+
11
+ <ItemGroup>
12
+ <Compile Include="Package.wxs" />
13
+ </ItemGroup>
14
+
15
+ <ItemGroup>
16
+ <None Include="packages.config" />
17
+ </ItemGroup>
18
+
19
+ <ItemGroup>
20
+ <ProjectReference Include="..\thmviewer\thmviewer.vcxproj">
21
+ <Name>thmviewer</Name>
22
+ <Project>{95228C13-97F5-484A-B4A2-ECF4618B0881}</Project>
23
+ </ProjectReference>
24
+ </ItemGroup>
25
+
26
+ <Import Project="$(OutputPath)\net461\wix.targets" />
27
+
28
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
29
+ <PropertyGroup>
30
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
31
+ </PropertyGroup>
32
+ <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets'))" />
33
+ </Target>
34
+ <Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" />
35
+</Project>
src/ThmViewerPackage/packages.config
new
+4
@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<packages>
3
+ <package id="Nerdbank.GitVersioning" version="2.1.65" developmentDependency="true" targetFramework="net40" />
4
+</packages>
src/Wix.Build.props
new
+12
@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+<Project>
5
+ <PropertyGroup>
6
+ <DefineConstants>$(DefineConstants);CompanyName=$(Company)</DefineConstants>
7
+ </PropertyGroup>
8
+
9
+ <ItemGroup>
10
+ <BindInputPaths Include="$(OutputPath)Win32\" />
11
+ </ItemGroup>
12
+</Project>
src/thmviewer/Resources/LoremIpsum.rtf
Binary files /dev/null and b/src/thmviewer/Resources/LoremIpsum.rtf differ
src/thmviewer/Resources/thm.xml
new
+11
@@ -0,0 +1,11 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+
5
+<Theme>
6
+ <Font Id="0" Height="-48" Weight="700" Foreground="$windowtext">Consolas</Font>
7
+ <Font Id="1" Height="-12" Weight="700" Foreground="$windowtext" Background="$window">Consolas</Font>
8
+ <Window Width="220" Height="200" FontId="0" Caption="Theme Viewer">
9
+ <TreeView Name="Tree" X="0" Y="0" Width="0" Height="0" FontId="1" Visible="yes" FullRowSelect="yes" HasButtons="yes" AlwaysShowSelect="yes" HasLines="yes" LinesAtRoot="yes"/>
10
+ </Window>
11
+</Theme>
src/thmviewer/display.cpp
new
+353
@@ -0,0 +1,353 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+static const LPCWSTR THMVWR_WINDOW_CLASS_DISPLAY = L"ThmViewerDisplay";
6
+
7
+struct DISPLAY_THREAD_CONTEXT
8
+{
9
+ HWND hWnd;
10
+ HINSTANCE hInstance;
11
+
12
+ HANDLE hInit;
13
+};
14
+
15
+static DWORD WINAPI DisplayThreadProc(
16
+ __in LPVOID pvContext
17
+ );
18
+static LRESULT CALLBACK DisplayWndProc(
19
+ __in HWND hWnd,
20
+ __in UINT uMsg,
21
+ __in WPARAM wParam,
22
+ __in LPARAM lParam
23
+ );
24
+static BOOL DisplayOnCreate(
25
+ __in THEME* pTheme,
26
+ __in HWND hWnd
27
+ );
28
+
29
+
30
+extern "C" HRESULT DisplayStart(
31
+ __in HINSTANCE hInstance,
32
+ __in HWND hWnd,
33
+ __out HANDLE *phThread,
34
+ __out DWORD* pdwThreadId
35
+ )
36
+{
37
+ HRESULT hr = S_OK;
38
+ HANDLE rgHandles[2] = { };
39
+ DISPLAY_THREAD_CONTEXT context = { };
40
+
41
+ rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
42
+ ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
43
+
44
+ context.hWnd = hWnd;
45
+ context.hInstance = hInstance;
46
+ context.hInit = rgHandles[0];
47
+
48
+ rgHandles[1] = ::CreateThread(NULL, 0, DisplayThreadProc, reinterpret_cast<LPVOID>(&context), 0, pdwThreadId);
49
+ ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create display thread.");
50
+
51
+ ::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
52
+
53
+ *phThread = rgHandles[1];
54
+ rgHandles[1] = NULL;
55
+
56
+LExit:
57
+ ReleaseHandle(rgHandles[1]);
58
+ ReleaseHandle(rgHandles[0]);
59
+ return hr;
60
+}
61
+
62
+static DWORD WINAPI DisplayThreadProc(
63
+ __in LPVOID pvContext
64
+ )
65
+{
66
+ HRESULT hr = S_OK;
67
+
68
+ DISPLAY_THREAD_CONTEXT* pContext = static_cast<DISPLAY_THREAD_CONTEXT*>(pvContext);
69
+ HINSTANCE hInstance = pContext->hInstance;
70
+ HWND hwndParent = pContext->hWnd;
71
+
72
+ // We can signal the initialization event as soon as we have copied the context
73
+ // values into local variables.
74
+ ::SetEvent(pContext->hInit);
75
+
76
+ BOOL fComInitialized = FALSE;
77
+
78
+ HANDLE_THEME* pCurrentHandle = NULL;
79
+ ATOM atomWc = 0;
80
+ WNDCLASSW wc = { }; // the following are constant for the display window class.
81
+ wc.lpfnWndProc = DisplayWndProc;
82
+ wc.hInstance = hInstance;
83
+ wc.lpszClassName = THMVWR_WINDOW_CLASS_DISPLAY;
84
+
85
+ HWND hWnd = NULL;
86
+ RECT rc = { };
87
+ int x = CW_USEDEFAULT;
88
+ int y = CW_USEDEFAULT;
89
+
90
+ BOOL fRedoMsg = FALSE;
91
+ BOOL fRet = FALSE;
92
+ MSG msg = { };
93
+
94
+ BOOL fCreateIfNecessary = FALSE;
95
+
96
+ hr = ::CoInitialize(NULL);
97
+ ExitOnFailure(hr, "Failed to initialize COM on display thread.");
98
+ fComInitialized = TRUE;
99
+
100
+ // As long as the parent window is alive and kicking, keep this thread going (with or without a theme to display ).
101
+ while (::IsWindow(hwndParent))
102
+ {
103
+ if (pCurrentHandle && fCreateIfNecessary)
104
+ {
105
+ THEME* pTheme = pCurrentHandle->pTheme;
106
+
107
+ if (CW_USEDEFAULT == x && CW_USEDEFAULT == y && ::GetWindowRect(hwndParent, &rc))
108
+ {
109
+ x = rc.left;
110
+ y = rc.bottom + 20;
111
+ }
112
+
113
+ hWnd = ::CreateWindowExW(0, wc.lpszClassName, pTheme->sczCaption, pTheme->dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, pCurrentHandle);
114
+ ExitOnNullWithLastError(hWnd, hr, "Failed to create display window.");
115
+
116
+ fCreateIfNecessary = FALSE;
117
+ }
118
+
119
+ // message pump
120
+ while (fRedoMsg || 0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
121
+ {
122
+ if (fRedoMsg)
123
+ {
124
+ fRedoMsg = FALSE;
125
+ }
126
+
127
+ if (-1 == fRet)
128
+ {
129
+ hr = E_UNEXPECTED;
130
+ ExitOnFailure(hr, "Unexpected return value from display message pump.");
131
+ }
132
+ else if (NULL == msg.hwnd) // Thread message.
133
+ {
134
+ if (WM_THMVWR_NEW_THEME == msg.message)
135
+ {
136
+ // If there is already a handle, release it.
137
+ if (pCurrentHandle)
138
+ {
139
+ DecrementHandleTheme(pCurrentHandle);
140
+ pCurrentHandle = NULL;
141
+ }
142
+
143
+ // If the window was created, remember its window location before we destroy
144
+ // it so so we can open the new window in the same place.
145
+ if (::IsWindow(hWnd))
146
+ {
147
+ ::GetWindowRect(hWnd, &rc);
148
+ x = rc.left;
149
+ y = rc.top;
150
+
151
+ ::DestroyWindow(hWnd);
152
+ }
153
+
154
+ // If the display window class was registered, unregister it so we can
155
+ // reuse the same window class name for the new theme.
156
+ if (atomWc)
157
+ {
158
+ if (!::UnregisterClassW(reinterpret_cast<LPCWSTR>(atomWc), hInstance))
159
+ {
160
+ DWORD er = ::GetLastError();
161
+ er = er;
162
+ }
163
+
164
+ atomWc = 0;
165
+ }
166
+
167
+ // If we were provided a new theme handle, create a new window class to
168
+ // support it.
169
+ pCurrentHandle = reinterpret_cast<HANDLE_THEME*>(msg.lParam);
170
+ if (pCurrentHandle)
171
+ {
172
+ wc.hIcon = reinterpret_cast<HICON>(pCurrentHandle->pTheme->hIcon);
173
+ wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
174
+ if (0 < pCurrentHandle->pTheme->cFonts)
175
+ {
176
+ wc.hbrBackground = pCurrentHandle->pTheme->rgFonts[pCurrentHandle->pTheme->dwFontId].hBackground;
177
+ }
178
+ atomWc = ::RegisterClassW(&wc);
179
+ if (!atomWc)
180
+ {
181
+ ExitWithLastError(hr, "Failed to register display window class.");
182
+ }
183
+ }
184
+ }
185
+ else if (WM_THMVWR_SHOWPAGE == msg.message)
186
+ {
187
+ if (pCurrentHandle && ::IsWindow(hWnd) && pCurrentHandle->pTheme->hwndParent == hWnd)
188
+ {
189
+ DWORD dwPageId = static_cast<DWORD>(msg.lParam);
190
+ int nCmdShow = static_cast<int>(msg.wParam);
191
+
192
+ // First show/hide the controls not associated with a page.
193
+ for (DWORD i = 0; i < pCurrentHandle->pTheme->cControls; ++i)
194
+ {
195
+ THEME_CONTROL* pControl = pCurrentHandle->pTheme->rgControls + i;
196
+ if (!pControl->wPageId)
197
+ {
198
+ ThemeShowControl(pCurrentHandle->pTheme, pControl->wId, nCmdShow);
199
+ }
200
+ }
201
+
202
+ // If a page id was provided also, show/hide those controls
203
+ if (dwPageId)
204
+ {
205
+ // Ignore error since we aren't using variables and it can only fail when using variables.
206
+ ThemeShowPage(pCurrentHandle->pTheme, dwPageId, nCmdShow);
207
+ }
208
+ }
209
+ else // display window isn't visible or it doesn't match the current handle.
210
+ {
211
+ // Keep the current message around to try again after we break out of this loop
212
+ // and create the window.
213
+ fRedoMsg = TRUE;
214
+ fCreateIfNecessary = TRUE;
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ else if (!ThemeHandleKeyboardMessage(pCurrentHandle->pTheme, hwndParent, &msg)) // Window message.
220
+ {
221
+ ::TranslateMessage(&msg);
222
+ ::DispatchMessageW(&msg);
223
+ }
224
+ }
225
+ }
226
+
227
+LExit:
228
+ if (::IsWindow(hWnd))
229
+ {
230
+ ::DestroyWindow(hWnd);
231
+ }
232
+
233
+ if (atomWc)
234
+ {
235
+ if (!::UnregisterClassW(THMVWR_WINDOW_CLASS_DISPLAY, hInstance))
236
+ {
237
+ DWORD er = ::GetLastError();
238
+ er = er;
239
+ }
240
+ }
241
+
242
+ DecrementHandleTheme(pCurrentHandle);
243
+
244
+ if (fComInitialized)
245
+ {
246
+ ::CoUninitialize();
247
+ }
248
+
249
+ return hr;
250
+}
251
+
252
+static LRESULT CALLBACK DisplayWndProc(
253
+ __in HWND hWnd,
254
+ __in UINT uMsg,
255
+ __in WPARAM wParam,
256
+ __in LPARAM lParam
257
+ )
258
+{
259
+ static DWORD dwProgress = 0;
260
+ HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
261
+
262
+ switch (uMsg)
263
+ {
264
+ case WM_NCCREATE:
265
+ {
266
+ LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
267
+ IncrementHandleTheme(reinterpret_cast<HANDLE_THEME*>(lpcs->lpCreateParams));
268
+ ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
269
+ }
270
+ break;
271
+
272
+ case WM_CREATE:
273
+ if (!DisplayOnCreate(pHandleTheme->pTheme, hWnd))
274
+ {
275
+ return -1;
276
+ }
277
+ break;
278
+
279
+ case WM_TIMER:
280
+ if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress)))
281
+ {
282
+ dwProgress += rand() % 10 + 1;
283
+ if (dwProgress > 100)
284
+ {
285
+ dwProgress = 0;
286
+ }
287
+
288
+ return 0;
289
+ }
290
+ break;
291
+
292
+ case WM_COMMAND:
293
+ {
294
+ WCHAR wzText[1024];
295
+ ::StringCchPrintfW(wzText, countof(wzText), L"Command %u\r\n", LOWORD(wParam));
296
+ OutputDebugStringW(wzText);
297
+ //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
298
+ }
299
+ break;
300
+
301
+ case WM_SYSCOMMAND:
302
+ {
303
+ WCHAR wzText[1024];
304
+ ::StringCchPrintfW(wzText, countof(wzText), L"SysCommand %u\r\n", LOWORD(wParam));
305
+ OutputDebugStringW(wzText);
306
+ //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
307
+ }
308
+ break;
309
+
310
+ case WM_DESTROY:
311
+ ThemeUnloadControls(pHandleTheme->pTheme);
312
+ ::PostQuitMessage(0);
313
+ break;
314
+
315
+ case WM_NCDESTROY:
316
+ DecrementHandleTheme(pHandleTheme);
317
+ ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
318
+ break;
319
+ }
320
+
321
+ return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam);
322
+}
323
+
324
+static BOOL DisplayOnCreate(
325
+ __in THEME* pTheme,
326
+ __in HWND hWnd
327
+ )
328
+{
329
+ HRESULT hr = S_OK;
330
+
331
+ hr = ThemeLoadControls(pTheme, hWnd, NULL, 0);
332
+ ExitOnFailure(hr, "Failed to load theme controls");
333
+
334
+ // Pre-populate some control types with data.
335
+ for (DWORD i = 0; i < pTheme->cControls; ++i)
336
+ {
337
+ THEME_CONTROL* pControl = pTheme->rgControls + i;
338
+ if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
339
+ {
340
+ hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL));
341
+ ExitOnFailure(hr, "Failed to load richedit text.");
342
+ }
343
+ else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
344
+ {
345
+ DWORD dwId = ::SetTimer(hWnd, pControl->wId, 500, NULL);
346
+ dwId = dwId; // prevents warning in "ship" build.
347
+ Assert(dwId == pControl->wId);
348
+ }
349
+ }
350
+
351
+LExit:
352
+ return SUCCEEDED(hr);
353
+}
src/thmviewer/load.cpp
new
+219
@@ -0,0 +1,219 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+struct LOAD_THREAD_CONTEXT
6
+{
7
+ HWND hWnd;
8
+ LPCWSTR wzThemePath;
9
+ LPCWSTR wzWxlPath;
10
+
11
+ HANDLE hInit;
12
+};
13
+
14
+static DWORD WINAPI LoadThreadProc(
15
+ __in LPVOID pvContext
16
+ );
17
+
18
+
19
+extern "C" HRESULT LoadStart(
20
+ __in_z LPCWSTR wzThemePath,
21
+ __in_z_opt LPCWSTR wzWxlPath,
22
+ __in HWND hWnd,
23
+ __out HANDLE* phThread
24
+ )
25
+{
26
+ HRESULT hr = S_OK;
27
+ HANDLE rgHandles[2] = { };
28
+ LOAD_THREAD_CONTEXT context = { };
29
+
30
+ rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
31
+ ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
32
+
33
+ context.hWnd = hWnd;
34
+ context.wzThemePath = wzThemePath;
35
+ context.wzWxlPath = wzWxlPath;
36
+ context.hInit = rgHandles[0];
37
+
38
+ rgHandles[1] = ::CreateThread(NULL, 0, LoadThreadProc, &context, 0, NULL);
39
+ ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create load thread.");
40
+
41
+ ::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
42
+
43
+ *phThread = rgHandles[1];
44
+ rgHandles[1] = NULL;
45
+
46
+LExit:
47
+ ReleaseHandle(rgHandles[1]);
48
+ ReleaseHandle(rgHandles[0]);
49
+ return hr;
50
+}
51
+
52
+
53
+static DWORD WINAPI LoadThreadProc(
54
+ __in LPVOID pvContext
55
+ )
56
+{
57
+ HRESULT hr = S_OK;
58
+ WIX_LOCALIZATION* pWixLoc = NULL;
59
+ LPWSTR sczThemePath = NULL;
60
+ LPWSTR sczWxlPath = NULL;
61
+ BOOL fComInitialized = FALSE;
62
+ HANDLE hDirectory = INVALID_HANDLE_VALUE;
63
+ LPWSTR sczDirectory = NULL;
64
+ LPWSTR wzFileName = NULL;
65
+
66
+ THEME* pTheme = NULL;
67
+ HANDLE_THEME* pHandle = NULL;
68
+
69
+ LOAD_THREAD_CONTEXT* pContext = static_cast<LOAD_THREAD_CONTEXT*>(pvContext);
70
+ HWND hWnd = pContext->hWnd;
71
+
72
+ hr = StrAllocString(&sczThemePath, pContext->wzThemePath, 0);
73
+ ExitOnFailure(hr, "Failed to copy path to initial theme file.");
74
+
75
+ if (pContext->wzWxlPath)
76
+ {
77
+ hr = StrAllocString(&sczWxlPath, pContext->wzWxlPath, 0);
78
+ ExitOnFailure(hr, "Failed to copy .wxl path to initial file.");
79
+ }
80
+
81
+ // We can signal the initialization event as soon as we have copied the context
82
+ // values into local variables.
83
+ ::SetEvent(pContext->hInit);
84
+
85
+ hr = ::CoInitialize(NULL);
86
+ ExitOnFailure(hr, "Failed to initialize COM on load thread.");
87
+ fComInitialized = TRUE;
88
+
89
+ // Open a handle to the directory so we can put a notification on it.
90
+ hr = PathGetDirectory(sczThemePath, &sczDirectory);
91
+ ExitOnFailure(hr, "Failed to get path directory.");
92
+
93
+ wzFileName = PathFile(sczThemePath);
94
+
95
+ hDirectory = ::CreateFileW(sczDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
96
+ if (INVALID_HANDLE_VALUE == hDirectory)
97
+ {
98
+ ExitWithLastError(hr, "Failed to open directory: %ls", sczDirectory);
99
+ }
100
+
101
+ BOOL fUpdated = FALSE;
102
+ do
103
+ {
104
+ // Get the last modified time on the file we're loading for verification that the
105
+ // file actually gets changed down below.
106
+ FILETIME ftModified = { };
107
+ FileGetTime(sczThemePath, NULL, NULL, &ftModified);
108
+
109
+ // Try to load the theme file.
110
+ hr = ThemeLoadFromFile(sczThemePath, &pTheme);
111
+ if (FAILED(hr))
112
+ {
113
+ ::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_ERROR, 0, hr);
114
+ }
115
+ else
116
+ {
117
+ if (sczWxlPath)
118
+ {
119
+ hr = LocLoadFromFile(sczWxlPath, &pWixLoc);
120
+ ExitOnFailure(hr, "Failed to load loc file from path: %ls", sczWxlPath);
121
+
122
+ hr = ThemeLocalize(pTheme, pWixLoc);
123
+ ExitOnFailure(hr, "Failed to localize theme: %ls", sczWxlPath);
124
+ }
125
+
126
+ hr = AllocHandleTheme(pTheme, &pHandle);
127
+ ExitOnFailure(hr, "Failed to allocate handle to theme");
128
+
129
+ ::SendMessageW(hWnd, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
130
+ pHandle = NULL;
131
+ }
132
+
133
+ fUpdated = FALSE;
134
+ do
135
+ {
136
+ DWORD rgbNotifications[1024];
137
+ DWORD cbRead = 0;
138
+ if (!::ReadDirectoryChangesW(hDirectory, rgbNotifications, sizeof(rgbNotifications), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &cbRead, NULL, NULL))
139
+ {
140
+ ExitWithLastError(hr, "Failed while watching directory: %ls", sczDirectory);
141
+ }
142
+
143
+ // Wait for half a second to let all the file handles get closed to minimize access
144
+ // denied errors.
145
+ ::Sleep(500);
146
+
147
+ FILE_NOTIFY_INFORMATION* pNotification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(rgbNotifications);
148
+ while (pNotification)
149
+ {
150
+ // If our file was updated, check to see if the modified time really changed. The notifications
151
+ // are often trigger happy thinking the file changed two or three times in a row. Maybe it's AV
152
+ // software creating the problems but actually checking the modified date works well.
153
+ if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pNotification->FileName, pNotification->FileNameLength / sizeof(WCHAR), wzFileName, -1))
154
+ {
155
+ FILETIME ft = { };
156
+ FileGetTime(sczThemePath, NULL, NULL, &ft);
157
+
158
+ fUpdated = (ftModified.dwHighDateTime < ft.dwHighDateTime) || (ftModified.dwHighDateTime == ft.dwHighDateTime && ftModified.dwLowDateTime < ft.dwLowDateTime);
159
+ break;
160
+ }
161
+
162
+ pNotification = pNotification->NextEntryOffset ? reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(pNotification) + pNotification->NextEntryOffset) : NULL;
163
+ }
164
+ } while (!fUpdated);
165
+ } while(fUpdated);
166
+
167
+LExit:
168
+ if (fComInitialized)
169
+ {
170
+ ::CoUninitialize();
171
+ }
172
+
173
+ LocFree(pWixLoc);
174
+ ReleaseFileHandle(hDirectory);
175
+ ReleaseStr(sczDirectory);
176
+ ReleaseStr(sczThemePath);
177
+ ReleaseStr(sczWxlPath);
178
+ return hr;
179
+}
180
+
181
+extern "C" HRESULT AllocHandleTheme(
182
+ __in THEME* pTheme,
183
+ __out HANDLE_THEME** ppHandle
184
+ )
185
+{
186
+ HRESULT hr = S_OK;
187
+ HANDLE_THEME* pHandle = NULL;
188
+
189
+ pHandle = static_cast<HANDLE_THEME*>(MemAlloc(sizeof(HANDLE_THEME), TRUE));
190
+ ExitOnNull(pHandle, hr, E_OUTOFMEMORY, "Failed to allocate theme handle.");
191
+
192
+ pHandle->cReferences = 1;
193
+ pHandle->pTheme = pTheme;
194
+
195
+ *ppHandle = pHandle;
196
+ pHandle = NULL;
197
+
198
+LExit:
199
+ ReleaseMem(pHandle);
200
+ return hr;
201
+}
202
+
203
+extern "C" void IncrementHandleTheme(
204
+ __in HANDLE_THEME* pHandle
205
+ )
206
+{
207
+ ::InterlockedIncrement(reinterpret_cast<LONG*>(&pHandle->cReferences));
208
+}
209
+
210
+extern "C" void DecrementHandleTheme(
211
+ __in HANDLE_THEME* pHandle
212
+ )
213
+{
214
+ if (pHandle && 0 == ::InterlockedDecrement(reinterpret_cast<LONG*>(&pHandle->cReferences)))
215
+ {
216
+ ThemeFree(pHandle->pTheme);
217
+ MemFree(pHandle);
218
+ }
219
+}
src/thmviewer/packages.config
new
+5
@@ -0,0 +1,5 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<packages>
3
+ <package id="Nerdbank.GitVersioning" version="2.1.65" targetFramework="native" developmentDependency="true" />
4
+ <package id="WixToolset.DUtil" version="4.0.6" targetFramework="native" />
5
+</packages>
\ No newline at end of file
src/thmviewer/precomp.cpp
new
+3
@@ -0,0 +1,3 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
src/thmviewer/precomp.h
new
+69
@@ -0,0 +1,69 @@
1
+#pragma once
2
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
+
4
+
5
+#include <windows.h>
6
+#include <msiquery.h>
7
+#include <objbase.h>
8
+#include <shlobj.h>
9
+#include <shlwapi.h>
10
+#include <stdlib.h>
11
+#include <strsafe.h>
12
+
13
+#pragma warning(push)
14
+#pragma warning(disable:4458)
15
+#include <gdiplus.h>
16
+#pragma warning(pop)
17
+
18
+#include "dutil.h"
19
+#include "apputil.h"
20
+#include "memutil.h"
21
+#include "dirutil.h"
22
+#include "fileutil.h"
23
+#include "locutil.h"
24
+#include "logutil.h"
25
+#include "pathutil.h"
26
+#include "resrutil.h"
27
+#include "shelutil.h"
28
+#include "strutil.h"
29
+#include "thmutil.h"
30
+
31
+#include "resource.h"
32
+
33
+struct HANDLE_THEME
34
+{
35
+ DWORD cReferences;
36
+ THEME* pTheme;
37
+};
38
+
39
+enum WM_THMVWR
40
+{
41
+ WM_THMVWR_SHOWPAGE = WM_APP,
42
+ WM_THMVWR_PARSE_FILE,
43
+ WM_THMVWR_NEW_THEME,
44
+ WM_THMVWR_THEME_LOAD_ERROR,
45
+};
46
+
47
+extern "C" HRESULT DisplayStart(
48
+ __in HINSTANCE hInstance,
49
+ __in HWND hWnd,
50
+ __out HANDLE *phThread,
51
+ __out DWORD* pdwThreadId
52
+ );
53
+extern "C" HRESULT LoadStart(
54
+ __in_z LPCWSTR wzThemePath,
55
+ __in_z LPCWSTR wzWxlPath,
56
+ __in HWND hWnd,
57
+ __out HANDLE* phThread
58
+ );
59
+
60
+extern "C" HRESULT AllocHandleTheme(
61
+ __in THEME* pTheme,
62
+ __out HANDLE_THEME** ppHandle
63
+ );
64
+extern "C" void IncrementHandleTheme(
65
+ __in HANDLE_THEME* pHandle
66
+ );
67
+extern "C" void DecrementHandleTheme(
68
+ __in HANDLE_THEME* pHandle
69
+ );
src/thmviewer/resource.h
new
+16
@@ -0,0 +1,16 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#define IDC_STATIC -1
4
+#define THMVWR_RES_THEME_FILE 1
5
+#define THMVWR_RES_RICHEDIT_FILE 2
6
+
7
+// Next default values for new objects
8
+//
9
+#ifdef APSTUDIO_INVOKED
10
+#ifndef APSTUDIO_READONLY_SYMBOLS
11
+#define _APS_NEXT_RESOURCE_VALUE 102
12
+#define _APS_NEXT_COMMAND_VALUE 40001
13
+#define _APS_NEXT_CONTROL_VALUE 1003
14
+#define _APS_NEXT_SYMED_VALUE 101
15
+#endif
16
+#endif
src/thmviewer/thmviewer.cpp
new
+465
@@ -0,0 +1,465 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+static const LPCWSTR THMVWR_WINDOW_CLASS_MAIN = L"ThmViewerMain";
6
+
7
+static THEME* vpTheme = NULL;
8
+static DWORD vdwDisplayThreadId = 0;
9
+
10
+enum THMVWR_CONTROL
11
+{
12
+ // Non-paged controls
13
+ THMVWR_CONTROL_TREE = THEME_FIRST_ASSIGN_CONTROL_ID,
14
+};
15
+
16
+static THEME_ASSIGN_CONTROL_ID vrgInitControls[] = {
17
+ { THMVWR_CONTROL_TREE, L"Tree" },
18
+};
19
+
20
+// Internal functions
21
+
22
+static HRESULT ProcessCommandLine(
23
+ __in_z_opt LPCWSTR wzCommandLine,
24
+ __out_z LPWSTR* psczThemeFile,
25
+ __out_z LPWSTR* psczWxlFile
26
+ );
27
+static HRESULT CreateTheme(
28
+ __in HINSTANCE hInstance,
29
+ __out THEME** ppTheme
30
+ );
31
+static HRESULT CreateMainWindowClass(
32
+ __in HINSTANCE hInstance,
33
+ __in THEME* pTheme,
34
+ __out ATOM* pAtom
35
+ );
36
+static LRESULT CALLBACK MainWndProc(
37
+ __in HWND hWnd,
38
+ __in UINT uMsg,
39
+ __in WPARAM wParam,
40
+ __in LPARAM lParam
41
+ );
42
+static void OnThemeLoadError(
43
+ __in THEME* pTheme,
44
+ __in HRESULT hrFailure
45
+ );
46
+static void OnNewTheme(
47
+ __in THEME* pTheme,
48
+ __in HWND hWnd,
49
+ __in HANDLE_THEME* pHandle
50
+ );
51
+
52
+
53
+int WINAPI wWinMain(
54
+ __in HINSTANCE hInstance,
55
+ __in_opt HINSTANCE /* hPrevInstance */,
56
+ __in_z LPWSTR lpCmdLine,
57
+ __in int /*nCmdShow*/
58
+ )
59
+{
60
+ ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
61
+
62
+ HRESULT hr = S_OK;
63
+ BOOL fComInitialized = FALSE;
64
+ LPWSTR sczThemeFile = NULL;
65
+ LPWSTR sczWxlFile = NULL;
66
+ ATOM atom = 0;
67
+ HWND hWnd = NULL;
68
+
69
+ HANDLE hDisplayThread = NULL;
70
+ HANDLE hLoadThread = NULL;
71
+
72
+ BOOL fRet = FALSE;
73
+ MSG msg = { };
74
+
75
+ hr = ::CoInitialize(NULL);
76
+ ExitOnFailure(hr, "Failed to initialize COM.");
77
+ fComInitialized = TRUE;
78
+
79
+ hr = ProcessCommandLine(lpCmdLine, &sczThemeFile, &sczWxlFile);
80
+ ExitOnFailure(hr, "Failed to process command line.");
81
+
82
+ hr = CreateTheme(hInstance, &vpTheme);
83
+ ExitOnFailure(hr, "Failed to create theme.");
84
+
85
+ hr = CreateMainWindowClass(hInstance, vpTheme, &atom);
86
+ ExitOnFailure(hr, "Failed to create main window.");
87
+
88
+ hWnd = ::CreateWindowExW(0, reinterpret_cast<LPCWSTR>(atom), vpTheme->sczCaption, vpTheme->dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, vpTheme->nWidth, vpTheme->nHeight, HWND_DESKTOP, NULL, hInstance, NULL);
89
+ ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
90
+
91
+ if (!sczThemeFile)
92
+ {
93
+ // Prompt for a path to the theme file.
94
+ OPENFILENAMEW ofn = { };
95
+ WCHAR wzFile[MAX_PATH] = { };
96
+
97
+ ofn.lStructSize = sizeof(ofn);
98
+ ofn.hwndOwner = hWnd;
99
+ ofn.lpstrFile = wzFile;
100
+ ofn.nMaxFile = countof(wzFile);
101
+ ofn.lpstrFilter = L"Theme Files\0*.thm\0XML Files\0*.xml\0All Files\0*.*\0";
102
+ ofn.nFilterIndex = 1;
103
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
104
+ ofn.lpstrTitle = vpTheme->sczCaption;
105
+
106
+ if (::GetOpenFileNameW(&ofn))
107
+ {
108
+ hr = StrAllocString(&sczThemeFile, wzFile, 0);
109
+ ExitOnFailure(hr, "Failed to copy opened file to theme file.");
110
+ }
111
+ else
112
+ {
113
+ ::MessageBoxW(hWnd, L"Must specify a path to theme file.", vpTheme->sczCaption, MB_OK | MB_ICONERROR);
114
+ ExitFunction1(hr = E_INVALIDARG);
115
+ }
116
+ }
117
+
118
+ hr = DisplayStart(hInstance, hWnd, &hDisplayThread, &vdwDisplayThreadId);
119
+ ExitOnFailure(hr, "Failed to start display.");
120
+
121
+ hr = LoadStart(sczThemeFile, sczWxlFile, hWnd, &hLoadThread);
122
+ ExitOnFailure(hr, "Failed to start load.");
123
+
124
+ // message pump
125
+ while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
126
+ {
127
+ if (-1 == fRet)
128
+ {
129
+ hr = E_UNEXPECTED;
130
+ ExitOnFailure(hr, "Unexpected return value from message pump.");
131
+ }
132
+ else if (!ThemeHandleKeyboardMessage(vpTheme, msg.hwnd, &msg))
133
+ {
134
+ ::TranslateMessage(&msg);
135
+ ::DispatchMessageW(&msg);
136
+ }
137
+ }
138
+
139
+LExit:
140
+ if (::IsWindow(hWnd))
141
+ {
142
+ ::DestroyWindow(hWnd);
143
+ }
144
+
145
+ if (hDisplayThread)
146
+ {
147
+ ::PostThreadMessageW(vdwDisplayThreadId, WM_QUIT, 0, 0);
148
+ ::WaitForSingleObject(hDisplayThread, 10000);
149
+ ::CloseHandle(hDisplayThread);
150
+ }
151
+
152
+ // TODO: come up with a good way to kill the load thread, probably need to switch
153
+ // the ReadDirectoryW() to overlapped mode.
154
+ ReleaseHandle(hLoadThread);
155
+
156
+ if (atom && !::UnregisterClassW(reinterpret_cast<LPCWSTR>(atom), hInstance))
157
+ {
158
+ DWORD er = ::GetLastError();
159
+ er = er;
160
+ }
161
+
162
+ ThemeFree(vpTheme);
163
+ ThemeUninitialize();
164
+
165
+ // uninitialize COM
166
+ if (fComInitialized)
167
+ {
168
+ ::CoUninitialize();
169
+ }
170
+
171
+ ReleaseStr(sczThemeFile);
172
+ ReleaseStr(sczWxlFile);
173
+ return hr;
174
+}
175
+
176
+
177
+//
178
+// ProcessCommandLine - process the provided command line arguments.
179
+//
180
+static HRESULT ProcessCommandLine(
181
+ __in_z_opt LPCWSTR wzCommandLine,
182
+ __out_z LPWSTR* psczThemeFile,
183
+ __out_z LPWSTR* psczWxlFile
184
+ )
185
+{
186
+ HRESULT hr = S_OK;
187
+ int argc = 0;
188
+ LPWSTR* argv = NULL;
189
+
190
+ if (wzCommandLine && *wzCommandLine)
191
+ {
192
+ hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
193
+ ExitOnFailure(hr, "Failed to parse command line.");
194
+
195
+ for (int i = 0; i < argc; ++i)
196
+ {
197
+ if (argv[i][0] == L'-' || argv[i][0] == L'/')
198
+ {
199
+ if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
200
+ {
201
+ if (i + 1 >= argc)
202
+ {
203
+ ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a language.");
204
+ }
205
+
206
+ ++i;
207
+ }
208
+ }
209
+ else
210
+ {
211
+ LPCWSTR wzExtension = PathExtension(argv[i]);
212
+ if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzExtension, -1, L".wxl", -1))
213
+ {
214
+ hr = StrAllocString(psczWxlFile, argv[i], 0);
215
+ }
216
+ else
217
+ {
218
+ hr = StrAllocString(psczThemeFile, argv[i], 0);
219
+ }
220
+ ExitOnFailure(hr, "Failed to copy path to file.");
221
+ }
222
+ }
223
+ }
224
+
225
+LExit:
226
+ if (argv)
227
+ {
228
+ AppFreeCommandLineArgs(argv);
229
+ }
230
+
231
+ return hr;
232
+}
233
+
234
+static HRESULT CreateTheme(
235
+ __in HINSTANCE hInstance,
236
+ __out THEME** ppTheme
237
+ )
238
+{
239
+ HRESULT hr = S_OK;
240
+
241
+ hr = ThemeInitialize(hInstance);
242
+ ExitOnFailure(hr, "Failed to initialize theme manager.");
243
+
244
+ hr = ThemeLoadFromResource(hInstance, MAKEINTRESOURCEA(THMVWR_RES_THEME_FILE), ppTheme);
245
+ ExitOnFailure(hr, "Failed to load theme from thmviewer.thm.");
246
+
247
+LExit:
248
+ return hr;
249
+}
250
+
251
+static HRESULT CreateMainWindowClass(
252
+ __in HINSTANCE hInstance,
253
+ __in THEME* pTheme,
254
+ __out ATOM* pAtom
255
+ )
256
+{
257
+ HRESULT hr = S_OK;
258
+ ATOM atom = 0;
259
+ WNDCLASSW wc = { };
260
+
261
+ wc.lpfnWndProc = MainWndProc;
262
+ wc.hInstance = hInstance;
263
+ wc.hIcon = reinterpret_cast<HICON>(pTheme->hIcon);
264
+ wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
265
+ wc.hbrBackground = pTheme->rgFonts[pTheme->dwFontId].hBackground;
266
+ wc.lpszMenuName = NULL;
267
+ wc.lpszClassName = THMVWR_WINDOW_CLASS_MAIN;
268
+ atom = ::RegisterClassW(&wc);
269
+ if (!atom)
270
+ {
271
+ ExitWithLastError(hr, "Failed to register main windowclass .");
272
+ }
273
+
274
+ *pAtom = atom;
275
+
276
+LExit:
277
+ return hr;
278
+}
279
+
280
+static LRESULT CALLBACK MainWndProc(
281
+ __in HWND hWnd,
282
+ __in UINT uMsg,
283
+ __in WPARAM wParam,
284
+ __in LPARAM lParam
285
+ )
286
+{
287
+ HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
288
+
289
+ switch (uMsg)
290
+ {
291
+ case WM_NCCREATE:
292
+ {
293
+ //LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
294
+ //pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams);
295
+ //::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
296
+ }
297
+ break;
298
+
299
+ case WM_NCDESTROY:
300
+ DecrementHandleTheme(pHandleTheme);
301
+ ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
302
+ break;
303
+
304
+ case WM_CREATE:
305
+ {
306
+ HRESULT hr = ThemeLoadControls(vpTheme, hWnd, vrgInitControls, countof(vrgInitControls));
307
+ if (FAILED(hr))
308
+ {
309
+ return -1;
310
+ }
311
+ }
312
+ break;
313
+
314
+ case WM_THMVWR_THEME_LOAD_ERROR:
315
+ OnThemeLoadError(vpTheme, lParam);
316
+ return 0;
317
+
318
+ case WM_THMVWR_NEW_THEME:
319
+ OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam));
320
+ return 0;
321
+
322
+ case WM_DESTROY:
323
+ ::PostQuitMessage(0);
324
+ break;
325
+
326
+ case WM_NOTIFY:
327
+ {
328
+ NMHDR* pnmhdr = reinterpret_cast<NMHDR*>(lParam);
329
+ switch (pnmhdr->code)
330
+ {
331
+ case TVN_SELCHANGEDW:
332
+ {
333
+ NMTREEVIEWW* ptv = reinterpret_cast<NMTREEVIEWW*>(lParam);
334
+ ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_HIDE, ptv->itemOld.lParam);
335
+ ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, ptv->itemNew.lParam);
336
+ }
337
+ break;
338
+
339
+ //case NM_DBLCLK:
340
+ // TVITEM item = { };
341
+ // item.mask = TVIF_PARAM;
342
+ // item.hItem = TreeView_GetSelection(pnmhdr->hwndFrom);
343
+ // TreeView_GetItem(pnmhdr->hwndFrom, &item);
344
+ // ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, item.lParam);
345
+ // return 1;
346
+ }
347
+ }
348
+ break;
349
+ }
350
+
351
+ return ThemeDefWindowProc(vpTheme, hWnd, uMsg, wParam, lParam);
352
+}
353
+
354
+static void OnThemeLoadError(
355
+ __in THEME* pTheme,
356
+ __in HRESULT hrFailure
357
+ )
358
+{
359
+ HRESULT hr = S_OK;
360
+ LPWSTR sczMessage = NULL;
361
+ TVINSERTSTRUCTW tvi = { };
362
+
363
+ // Add the application node.
364
+ tvi.hParent = NULL;
365
+ tvi.hInsertAfter = TVI_ROOT;
366
+ tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
367
+ tvi.item.lParam = 0;
368
+ tvi.item.pszText = L"Failed to load theme.";
369
+ tvi.hParent = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
370
+
371
+ hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x.", hrFailure);
372
+ ExitOnFailure(hr, "Failed to format error message.");
373
+
374
+ tvi.item.pszText = sczMessage;
375
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
376
+
377
+ hr = StrAllocFromError(&sczMessage, hrFailure, NULL);
378
+ ExitOnFailure(hr, "Failed to format error message text.");
379
+
380
+ tvi.item.pszText = sczMessage;
381
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
382
+
383
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
384
+
385
+LExit:
386
+ ReleaseStr(sczMessage);
387
+}
388
+
389
+
390
+static void OnNewTheme(
391
+ __in THEME* pTheme,
392
+ __in HWND hWnd,
393
+ __in HANDLE_THEME* pHandle
394
+ )
395
+{
396
+ HANDLE_THEME* pOldHandle = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
397
+ THEME* pNewTheme = pHandle->pTheme;
398
+
399
+ WCHAR wzSelectedPage[MAX_PATH] = { };
400
+ HTREEITEM htiSelected = NULL;
401
+ TVINSERTSTRUCTW tvi = { };
402
+ TVITEMW item = { };
403
+
404
+ if (pOldHandle)
405
+ {
406
+ DecrementHandleTheme(pOldHandle);
407
+ pOldHandle = NULL;
408
+ }
409
+
410
+ // Pass the new theme handle to the display thread so it can get the display window prepared
411
+ // to show the new theme.
412
+ IncrementHandleTheme(pHandle);
413
+ ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
414
+
415
+ ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pHandle));
416
+
417
+ // Remember the currently selected item by name so we can try to automatically select it later.
418
+ // Otherwise, the user would see their window destroyed after every save of their theme file and
419
+ // have to click to get the window back.
420
+ item.mask = TVIF_TEXT;
421
+ item.pszText = wzSelectedPage;
422
+ item.cchTextMax = countof(wzSelectedPage);
423
+ item.hItem = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_GETNEXTITEM, TVGN_CARET, NULL));
424
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item));
425
+
426
+ // Remove the previous items in the tree.
427
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_DELETEITEM, 0, reinterpret_cast<LPARAM>(TVI_ROOT));
428
+
429
+ // Add the application node.
430
+ tvi.hParent = NULL;
431
+ tvi.hInsertAfter = TVI_ROOT;
432
+ tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
433
+ tvi.item.lParam = 0;
434
+ tvi.item.pszText = pHandle && pHandle->pTheme && pHandle->pTheme->sczCaption ? pHandle->pTheme->sczCaption : L"Window";
435
+
436
+ // Add the pages.
437
+ tvi.hParent = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
438
+ tvi.hInsertAfter = TVI_SORT;
439
+ for (DWORD i = 0; i < pNewTheme->cPages; ++i)
440
+ {
441
+ THEME_PAGE* pPage = pNewTheme->rgPages + i;
442
+ if (pPage->sczName && *pPage->sczName)
443
+ {
444
+ tvi.item.pszText = pPage->sczName;
445
+ tvi.item.lParam = i + 1; //prgdwPageIds[i]; - TODO: do the right thing here by calling ThemeGetPageIds(), should not assume we know how the page ids will be calculated.
446
+
447
+ HTREEITEM hti = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
448
+ if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pPage->sczName, -1, wzSelectedPage, -1))
449
+ {
450
+ htiSelected = hti;
451
+ }
452
+ }
453
+ }
454
+
455
+ if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Application", -1, wzSelectedPage, -1))
456
+ {
457
+ htiSelected = tvi.hParent;
458
+ }
459
+
460
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
461
+ if (htiSelected)
462
+ {
463
+ ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_SELECTITEM, TVGN_CARET, reinterpret_cast<LPARAM>(htiSelected));
464
+ }
465
+}
src/thmviewer/thmviewer.manifest
new
+11
@@ -0,0 +1,11 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+
5
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
6
+ <assemblyIdentity name="thmviewer.exe" version="1.0.0.0" processorArchitecture="x86" type="win32"/>
7
+ <description>WiX Toolset Theme Viewer</description>
8
+ <dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /></dependentAssembly></dependency>
9
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application><supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/><supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/></application></compatibility>
10
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo>
11
+</assembly>
src/thmviewer/thmviewer.rc
new
+12
@@ -0,0 +1,12 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include <winver.h>
4
+#include <windows.h>
5
+#include "resource.h"
6
+
7
+//#define MANIFEST_RESOURCE_ID 1
8
+//MANIFEST_RESOURCE_ID RT_MANIFEST "thmviewer.manifest"
9
+
10
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
11
+THMVWR_RES_THEME_FILE RCDATA "Resources\\thm.xml"
12
+THMVWR_RES_RICHEDIT_FILE RCDATA "Resources\\LoremIpsum.rtf"
src/thmviewer/thmviewer.vcxproj
new
+73
@@ -0,0 +1,73 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
+
4
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5
+ <Import Project="..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" />
6
+
7
+ <ItemGroup Label="ProjectConfigurations">
8
+ <ProjectConfiguration Include="Debug|Win32">
9
+ <Configuration>Debug</Configuration>
10
+ <Platform>Win32</Platform>
11
+ </ProjectConfiguration>
12
+ <ProjectConfiguration Include="Release|Win32">
13
+ <Configuration>Release</Configuration>
14
+ <Platform>Win32</Platform>
15
+ </ProjectConfiguration>
16
+ </ItemGroup>
17
+
18
+ <PropertyGroup Label="Globals">
19
+ <ProjectGuid>{95228C13-97F5-484A-B4A2-ECF4618B0881}</ProjectGuid>
20
+ <Keyword>Win32Proj</Keyword>
21
+ <ConfigurationType>Application</ConfigurationType>
22
+ <PlatformToolset>v141</PlatformToolset>
23
+ <CharacterSet>Unicode</CharacterSet>
24
+ <Description>WiX Toolset Theme Viewer</Description>
25
+ </PropertyGroup>
26
+
27
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
28
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
29
+
30
+ <ImportGroup Label="ExtensionSettings">
31
+ </ImportGroup>
32
+
33
+ <ImportGroup Label="Shared">
34
+ <Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" />
35
+ </ImportGroup>
36
+
37
+ <PropertyGroup>
38
+ <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
39
+ </PropertyGroup>
40
+
41
+ <ItemGroup>
42
+ <ClCompile Include="display.cpp" />
43
+ <ClCompile Include="load.cpp" />
44
+ <ClCompile Include="precomp.cpp">
45
+ <PrecompiledHeader>Create</PrecompiledHeader>
46
+ </ClCompile>
47
+ <ClCompile Include="thmviewer.cpp" />
48
+ </ItemGroup>
49
+ <ItemGroup>
50
+ <ClInclude Include="precomp.h" />
51
+ <ClInclude Include="resource.h" />
52
+ </ItemGroup>
53
+ <ItemGroup>
54
+ <None Include="Resources\LoremIpsum.rtf" />
55
+ <None Include="Resources\thm.xml" />
56
+ </ItemGroup>
57
+ <ItemGroup>
58
+ <ResourceCompile Include="thmviewer.rc" />
59
+ </ItemGroup>
60
+ <ItemGroup>
61
+ <Manifest Include="thmviewer.manifest" />
62
+ </ItemGroup>
63
+
64
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
65
+
66
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
67
+ <PropertyGroup>
68
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
69
+ </PropertyGroup>
70
+ <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets'))" />
71
+ <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props'))" />
72
+ </Target>
73
+</Project>
src/thmviewer/thmviewer.vcxproj.filters
new
+53
@@ -0,0 +1,53 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <ItemGroup>
4
+ <Filter Include="Source Files">
5
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7
+ </Filter>
8
+ <Filter Include="Header Files">
9
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11
+ </Filter>
12
+ <Filter Include="Resource Files">
13
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
15
+ </Filter>
16
+ </ItemGroup>
17
+ <ItemGroup>
18
+ <ClCompile Include="thmviewer.cpp">
19
+ <Filter>Source Files</Filter>
20
+ </ClCompile>
21
+ <ClCompile Include="display.cpp">
22
+ <Filter>Source Files</Filter>
23
+ </ClCompile>
24
+ <ClCompile Include="load.cpp">
25
+ <Filter>Source Files</Filter>
26
+ </ClCompile>
27
+ </ItemGroup>
28
+ <ItemGroup>
29
+ <None Include="thmviewer.build" />
30
+ <None Include="Resources\thm.xml">
31
+ <Filter>Resource Files</Filter>
32
+ </None>
33
+ <None Include="Resources\LoremIpsum.rtf">
34
+ <Filter>Resource Files</Filter>
35
+ </None>
36
+ </ItemGroup>
37
+ <ItemGroup>
38
+ <ClInclude Include="precomp.h">
39
+ <Filter>Header Files</Filter>
40
+ </ClInclude>
41
+ <ClInclude Include="resource.h">
42
+ <Filter>Header Files</Filter>
43
+ </ClInclude>
44
+ </ItemGroup>
45
+ <ItemGroup>
46
+ <ResourceCompile Include="thmviewer.rc">
47
+ <Filter>Resource Files</Filter>
48
+ </ResourceCompile>
49
+ </ItemGroup>
50
+ <ItemGroup>
51
+ <Manifest Include="thmviewer.manifest" />
52
+ </ItemGroup>
53
+</Project>
\ No newline at end of file