@joebigelow / wix / commits / e8d9c709

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) &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>
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