aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2019-02-01 14:32:57 -0800
committerRob Mensching <rob@robmensching.com>2019-02-01 14:37:02 -0800
commite8d9c70934d8cae0d2769ab6ca5ad40d5f506968 (patch)
tree45cb15c5cdaaf2547089a4c01d7a144b2943d79e
parent28779cbbaa5fcba61009d416654345d50a96fa91 (diff)
downloadwix-e8d9c70934d8cae0d2769ab6ca5ad40d5f506968.tar.gz
wix-e8d9c70934d8cae0d2769ab6ca5ad40d5f506968.tar.bz2
wix-e8d9c70934d8cae0d2769ab6ca5ad40d5f506968.zip
Integrate thmviewer.exe and thmviewer.msi
Cannot build MSI until a few fixes are made to WixToolset.Core. Will enable this later.
-rw-r--r--Tools.sln42
-rw-r--r--appveyor.cmd6
-rw-r--r--appveyor.yml2
-rw-r--r--src/Cpp.Build.props104
-rw-r--r--src/Directory.Build.props2
-rw-r--r--src/ThmViewerPackage/Package.wxs36
-rw-r--r--src/ThmViewerPackage/ThmViewerPackage.wixproj35
-rw-r--r--src/ThmViewerPackage/packages.config4
-rw-r--r--src/Wix.Build.props12
-rw-r--r--src/thmviewer/Resources/LoremIpsum.rtfbin0 -> 4870 bytes
-rw-r--r--src/thmviewer/Resources/thm.xml11
-rw-r--r--src/thmviewer/display.cpp353
-rw-r--r--src/thmviewer/load.cpp219
-rw-r--r--src/thmviewer/packages.config5
-rw-r--r--src/thmviewer/precomp.cpp3
-rw-r--r--src/thmviewer/precomp.h69
-rw-r--r--src/thmviewer/resource.h16
-rw-r--r--src/thmviewer/thmviewer.cpp465
-rw-r--r--src/thmviewer/thmviewer.manifest11
-rw-r--r--src/thmviewer/thmviewer.rc12
-rw-r--r--src/thmviewer/thmviewer.vcxproj73
-rw-r--r--src/thmviewer/thmviewer.vcxproj.filters53
22 files changed, 1532 insertions, 1 deletions
diff --git a/Tools.sln b/Tools.sln
index 75f52adc..45deb3c5 100644
--- a/Tools.sln
+++ b/Tools.sln
@@ -24,44 +24,86 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Tools.Core", "sr
24EndProject 24EndProject
25Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.WixCop", "src\test\WixToolsetTest.WixCop\WixToolsetTest.WixCop.csproj", "{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}" 25Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.WixCop", "src\test\WixToolsetTest.WixCop\WixToolsetTest.WixCop.csproj", "{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}"
26EndProject 26EndProject
27Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "thmviewer", "src\thmviewer\thmviewer.vcxproj", "{95228C13-97F5-484A-B4A2-ECF4618B0881}"
28EndProject
27Global 29Global
28 GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 GlobalSection(SolutionConfigurationPlatforms) = preSolution
29 Debug|Any CPU = Debug|Any CPU 31 Debug|Any CPU = Debug|Any CPU
32 Debug|x86 = Debug|x86
30 Release|Any CPU = Release|Any CPU 33 Release|Any CPU = Release|Any CPU
34 Release|x86 = Release|x86
31 EndGlobalSection 35 EndGlobalSection
32 GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.Build.0 = 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
35 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 {4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.Build.0 = 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
37 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.Build.0 = 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
39 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 {DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.Build.0 = 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
41 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.Build.0 = 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
43 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 {65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.Build.0 = 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
45 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.Build.0 = 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
47 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 {938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.Build.0 = 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
49 {0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 {0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 {0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.Build.0 = 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
51 {0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 {0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 {0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.Build.0 = 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
53 {2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 {2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 {2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.Build.0 = 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
55 {2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 {2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 {2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.Build.0 = 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
57 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.Build.0 = 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
59 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU 89 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 {9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.Build.0 = 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
61 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.Build.0 = 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
63 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU 97 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 {F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.Build.0 = 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
65 EndGlobalSection 107 EndGlobalSection
66 GlobalSection(SolutionProperties) = preSolution 108 GlobalSection(SolutionProperties) = preSolution
67 HideSolutionNode = FALSE 109 HideSolutionNode = FALSE
diff --git a/appveyor.cmd b/appveyor.cmd
index d18c8df8..ec97f0b6 100644
--- a/appveyor.cmd
+++ b/appveyor.cmd
@@ -2,6 +2,8 @@
2@pushd %~dp0 2@pushd %~dp0
3@set _P=%~dp0build\Release\publish 3@set _P=%~dp0build\Release\publish
4 4
5nuget restore
6
5dotnet build -c Release src\test\WixToolsetTest.BuildTasks 7dotnet build -c Release src\test\WixToolsetTest.BuildTasks
6dotnet build -c Release src\test\WixToolsetTest.WixCop 8dotnet build -c Release src\test\WixToolsetTest.WixCop
7 9
@@ -15,7 +17,9 @@ dotnet publish -c Release -o %_P%\WixToolset.MSBuild\netcoreapp2.1\ -f netcoreap
15 17
16dotnet pack -c Release src\dotnet-wix 18dotnet pack -c Release src\dotnet-wix
17dotnet pack -c Release src\WixToolset.MSBuild 19dotnet 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
19 23
20@popd 24@popd
21@endlocal 25@endlocal
diff --git a/appveyor.yml b/appveyor.yml
index c1df03cc..8d80c6af 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -33,6 +33,8 @@ skip_tags: true
33artifacts: 33artifacts:
34- path: build\Release\**\*.nupkg 34- path: build\Release\**\*.nupkg
35 name: nuget 35 name: nuget
36- path: build\Release\**\*.msi
37 name: msi
36 38
37notifications: 39notifications:
38- provider: Slack 40- provider: Slack
diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props
new file mode 100644
index 00000000..0e00132b
--- /dev/null
+++ b/src/Cpp.Build.props
@@ -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) &gt; 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>
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index e853e22d..30ce4e48 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -8,6 +8,7 @@
8 <PropertyGroup> 8 <PropertyGroup>
9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> 9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink> 10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11 <MSBuildWarningsAsMessages>MSB3246</MSBuildWarningsAsMessages>
11 12
12 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName> 13 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
13 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath> 14 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
@@ -22,5 +23,6 @@
22 </PropertyGroup> 23 </PropertyGroup>
23 24
24 <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " /> 25 <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " />
26 <Import Project="Wix.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.wixproj' " />
25 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " /> 27 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
26</Project> 28</Project>
diff --git a/src/ThmViewerPackage/Package.wxs b/src/ThmViewerPackage/Package.wxs
new file mode 100644
index 00000000..cfa041ba
--- /dev/null
+++ b/src/ThmViewerPackage/Package.wxs
@@ -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>
diff --git a/src/ThmViewerPackage/ThmViewerPackage.wixproj b/src/ThmViewerPackage/ThmViewerPackage.wixproj
new file mode 100644
index 00000000..ec2400bd
--- /dev/null
+++ b/src/ThmViewerPackage/ThmViewerPackage.wixproj
@@ -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>
diff --git a/src/ThmViewerPackage/packages.config b/src/ThmViewerPackage/packages.config
new file mode 100644
index 00000000..d69ed11e
--- /dev/null
+++ b/src/ThmViewerPackage/packages.config
@@ -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>
diff --git a/src/Wix.Build.props b/src/Wix.Build.props
new file mode 100644
index 00000000..936126e3
--- /dev/null
+++ b/src/Wix.Build.props
@@ -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>
diff --git a/src/thmviewer/Resources/LoremIpsum.rtf b/src/thmviewer/Resources/LoremIpsum.rtf
new file mode 100644
index 00000000..1ab0e65b
--- /dev/null
+++ b/src/thmviewer/Resources/LoremIpsum.rtf
Binary files differ
diff --git a/src/thmviewer/Resources/thm.xml b/src/thmviewer/Resources/thm.xml
new file mode 100644
index 00000000..6394f0f1
--- /dev/null
+++ b/src/thmviewer/Resources/thm.xml
@@ -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>
diff --git a/src/thmviewer/display.cpp b/src/thmviewer/display.cpp
new file mode 100644
index 00000000..cfd1c6b7
--- /dev/null
+++ b/src/thmviewer/display.cpp
@@ -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
5static const LPCWSTR THMVWR_WINDOW_CLASS_DISPLAY = L"ThmViewerDisplay";
6
7struct DISPLAY_THREAD_CONTEXT
8{
9 HWND hWnd;
10 HINSTANCE hInstance;
11
12 HANDLE hInit;
13};
14
15static DWORD WINAPI DisplayThreadProc(
16 __in LPVOID pvContext
17 );
18static LRESULT CALLBACK DisplayWndProc(
19 __in HWND hWnd,
20 __in UINT uMsg,
21 __in WPARAM wParam,
22 __in LPARAM lParam
23 );
24static BOOL DisplayOnCreate(
25 __in THEME* pTheme,
26 __in HWND hWnd
27 );
28
29
30extern "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
56LExit:
57 ReleaseHandle(rgHandles[1]);
58 ReleaseHandle(rgHandles[0]);
59 return hr;
60}
61
62static 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
227LExit:
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
252static 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
324static 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
351LExit:
352 return SUCCEEDED(hr);
353}
diff --git a/src/thmviewer/load.cpp b/src/thmviewer/load.cpp
new file mode 100644
index 00000000..cd48caff
--- /dev/null
+++ b/src/thmviewer/load.cpp
@@ -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
5struct LOAD_THREAD_CONTEXT
6{
7 HWND hWnd;
8 LPCWSTR wzThemePath;
9 LPCWSTR wzWxlPath;
10
11 HANDLE hInit;
12};
13
14static DWORD WINAPI LoadThreadProc(
15 __in LPVOID pvContext
16 );
17
18
19extern "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
46LExit:
47 ReleaseHandle(rgHandles[1]);
48 ReleaseHandle(rgHandles[0]);
49 return hr;
50}
51
52
53static 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
167LExit:
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
181extern "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
198LExit:
199 ReleaseMem(pHandle);
200 return hr;
201}
202
203extern "C" void IncrementHandleTheme(
204 __in HANDLE_THEME* pHandle
205 )
206{
207 ::InterlockedIncrement(reinterpret_cast<LONG*>(&pHandle->cReferences));
208}
209
210extern "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}
diff --git a/src/thmviewer/packages.config b/src/thmviewer/packages.config
new file mode 100644
index 00000000..05bd6cc6
--- /dev/null
+++ b/src/thmviewer/packages.config
@@ -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
diff --git a/src/thmviewer/precomp.cpp b/src/thmviewer/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/thmviewer/precomp.cpp
@@ -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"
diff --git a/src/thmviewer/precomp.h b/src/thmviewer/precomp.h
new file mode 100644
index 00000000..1d186cf4
--- /dev/null
+++ b/src/thmviewer/precomp.h
@@ -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
33struct HANDLE_THEME
34{
35 DWORD cReferences;
36 THEME* pTheme;
37};
38
39enum 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
47extern "C" HRESULT DisplayStart(
48 __in HINSTANCE hInstance,
49 __in HWND hWnd,
50 __out HANDLE *phThread,
51 __out DWORD* pdwThreadId
52 );
53extern "C" HRESULT LoadStart(
54 __in_z LPCWSTR wzThemePath,
55 __in_z LPCWSTR wzWxlPath,
56 __in HWND hWnd,
57 __out HANDLE* phThread
58 );
59
60extern "C" HRESULT AllocHandleTheme(
61 __in THEME* pTheme,
62 __out HANDLE_THEME** ppHandle
63 );
64extern "C" void IncrementHandleTheme(
65 __in HANDLE_THEME* pHandle
66 );
67extern "C" void DecrementHandleTheme(
68 __in HANDLE_THEME* pHandle
69 );
diff --git a/src/thmviewer/resource.h b/src/thmviewer/resource.h
new file mode 100644
index 00000000..4acc32cc
--- /dev/null
+++ b/src/thmviewer/resource.h
@@ -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
diff --git a/src/thmviewer/thmviewer.cpp b/src/thmviewer/thmviewer.cpp
new file mode 100644
index 00000000..511272a9
--- /dev/null
+++ b/src/thmviewer/thmviewer.cpp
@@ -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
5static const LPCWSTR THMVWR_WINDOW_CLASS_MAIN = L"ThmViewerMain";
6
7static THEME* vpTheme = NULL;
8static DWORD vdwDisplayThreadId = 0;
9
10enum THMVWR_CONTROL
11{
12 // Non-paged controls
13 THMVWR_CONTROL_TREE = THEME_FIRST_ASSIGN_CONTROL_ID,
14};
15
16static THEME_ASSIGN_CONTROL_ID vrgInitControls[] = {
17 { THMVWR_CONTROL_TREE, L"Tree" },
18};
19
20// Internal functions
21
22static HRESULT ProcessCommandLine(
23 __in_z_opt LPCWSTR wzCommandLine,
24 __out_z LPWSTR* psczThemeFile,
25 __out_z LPWSTR* psczWxlFile
26 );
27static HRESULT CreateTheme(
28 __in HINSTANCE hInstance,
29 __out THEME** ppTheme
30 );
31static HRESULT CreateMainWindowClass(
32 __in HINSTANCE hInstance,
33 __in THEME* pTheme,
34 __out ATOM* pAtom
35 );
36static LRESULT CALLBACK MainWndProc(
37 __in HWND hWnd,
38 __in UINT uMsg,
39 __in WPARAM wParam,
40 __in LPARAM lParam
41 );
42static void OnThemeLoadError(
43 __in THEME* pTheme,
44 __in HRESULT hrFailure
45 );
46static void OnNewTheme(
47 __in THEME* pTheme,
48 __in HWND hWnd,
49 __in HANDLE_THEME* pHandle
50 );
51
52
53int 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
139LExit:
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//
180static 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
225LExit:
226 if (argv)
227 {
228 AppFreeCommandLineArgs(argv);
229 }
230
231 return hr;
232}
233
234static 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
247LExit:
248 return hr;
249}
250
251static 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
276LExit:
277 return hr;
278}
279
280static 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
354static 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
385LExit:
386 ReleaseStr(sczMessage);
387}
388
389
390static 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}
diff --git a/src/thmviewer/thmviewer.manifest b/src/thmviewer/thmviewer.manifest
new file mode 100644
index 00000000..6bd63a5e
--- /dev/null
+++ b/src/thmviewer/thmviewer.manifest
@@ -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>
diff --git a/src/thmviewer/thmviewer.rc b/src/thmviewer/thmviewer.rc
new file mode 100644
index 00000000..dc6d7242
--- /dev/null
+++ b/src/thmviewer/thmviewer.rc
@@ -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
10LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
11THMVWR_RES_THEME_FILE RCDATA "Resources\\thm.xml"
12THMVWR_RES_RICHEDIT_FILE RCDATA "Resources\\LoremIpsum.rtf"
diff --git a/src/thmviewer/thmviewer.vcxproj b/src/thmviewer/thmviewer.vcxproj
new file mode 100644
index 00000000..0c4612f2
--- /dev/null
+++ b/src/thmviewer/thmviewer.vcxproj
@@ -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>
diff --git a/src/thmviewer/thmviewer.vcxproj.filters b/src/thmviewer/thmviewer.vcxproj.filters
new file mode 100644
index 00000000..79e08275
--- /dev/null
+++ b/src/thmviewer/thmviewer.vcxproj.filters
@@ -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