Integrate thmviewer.exe and thmviewer.msi

Cannot build MSI until a few fixes are made to WixToolset.Core. Will
enable this later.
This commit is contained in:
Rob Mensching 2019-02-01 14:32:57 -08:00 коммит произвёл Rob Mensching
Родитель 28779cbbaa
Коммит e8d9c70934
22 изменённых файлов: 1532 добавлений и 1 удалений

Просмотреть файл

@ -24,44 +24,86 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Tools.Core", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.WixCop", "src\test\WixToolsetTest.WixCop\WixToolsetTest.WixCop.csproj", "{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "thmviewer", "src\thmviewer\thmviewer.vcxproj", "{95228C13-97F5-484A-B4A2-ECF4618B0881}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|x86.ActiveCfg = Debug|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Debug|x86.Build.0 = Debug|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|Any CPU.Build.0 = Release|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|x86.ActiveCfg = Release|Any CPU
{4B0098A4-B581-4D04-BA1E-6DC2370A7D43}.Release|x86.Build.0 = Release|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|x86.ActiveCfg = Debug|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Debug|x86.Build.0 = Debug|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|Any CPU.Build.0 = Release|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|x86.ActiveCfg = Release|Any CPU
{DA5CA026-6165-48C4-BDA5-BB4B17D56A18}.Release|x86.Build.0 = Release|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|x86.ActiveCfg = Debug|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Debug|x86.Build.0 = Debug|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|Any CPU.Build.0 = Release|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|x86.ActiveCfg = Release|Any CPU
{65141CE1-0BDD-41EF-8043-35B96C423CB6}.Release|x86.Build.0 = Release|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|Any CPU.Build.0 = Debug|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|x86.ActiveCfg = Debug|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Debug|x86.Build.0 = Debug|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.ActiveCfg = Release|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|Any CPU.Build.0 = Release|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|x86.ActiveCfg = Release|Any CPU
{938BCA04-610B-4B99-9CB7-02BF7397A972}.Release|x86.Build.0 = Release|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|x86.ActiveCfg = Debug|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Debug|x86.Build.0 = Debug|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|Any CPU.Build.0 = Release|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|x86.ActiveCfg = Release|Any CPU
{0DF5D4CF-8457-469D-8288-13775E984F70}.Release|x86.Build.0 = Release|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Debug|x86.Build.0 = Debug|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|Any CPU.Build.0 = Release|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|x86.ActiveCfg = Release|Any CPU
{2E54120B-8958-40B1-A7FC-851446994CD8}.Release|x86.Build.0 = Release|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|x86.ActiveCfg = Debug|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Debug|x86.Build.0 = Debug|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|Any CPU.Build.0 = Release|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|x86.ActiveCfg = Release|Any CPU
{9C3B486F-AE0E-43BA-823A-30808B73C6B4}.Release|x86.Build.0 = Release|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|x86.ActiveCfg = Debug|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Debug|x86.Build.0 = Debug|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|Any CPU.Build.0 = Release|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|x86.ActiveCfg = Release|Any CPU
{F1A8112B-95A1-4AF7-81CB-523BE7DB8E5C}.Release|x86.Build.0 = Release|Any CPU
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|Any CPU.ActiveCfg = Debug|Win32
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|x86.ActiveCfg = Debug|Win32
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Debug|x86.Build.0 = Debug|Win32
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|Any CPU.ActiveCfg = Release|Win32
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|x86.ActiveCfg = Release|Win32
{95228C13-97F5-484A-B4A2-ECF4618B0881}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -2,6 +2,8 @@
@pushd %~dp0
@set _P=%~dp0build\Release\publish
nuget restore
dotnet build -c Release src\test\WixToolsetTest.BuildTasks
dotnet build -c Release src\test\WixToolsetTest.WixCop
@ -15,7 +17,9 @@ dotnet publish -c Release -o %_P%\WixToolset.MSBuild\netcoreapp2.1\ -f netcoreap
dotnet pack -c Release src\dotnet-wix
dotnet pack -c Release src\WixToolset.MSBuild
@rem dotnet pack -c Release src\WixToolset.Core.InternalPackage
@rem Enable this build when WixToolset.Core is fixed to build the setup code correctly.
@rem msbuild -p:Configuration=Release .\src\ThmViewerPackage\ThmViewerPackage.wixproj
@popd
@endlocal

Просмотреть файл

@ -33,6 +33,8 @@ skip_tags: true
artifacts:
- path: build\Release\**\*.nupkg
name: nuget
- path: build\Release\**\*.msi
name: msi
notifications:
- provider: Slack

104
src/Cpp.Build.props Normal file
Просмотреть файл

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<Project>
<PropertyGroup>
<Platform Condition=" '$(Platform)' == '' OR '$(Platform)' == 'AnyCPU' ">Win32</Platform>
<IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(OutputPath)$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'=='15.0'">
<WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
<WarningLevel>Level4</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
<CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
<TreatWarningAsError>true</TreatWarningAsError>
<ExceptionHandling>false</ExceptionHandling>
<AdditionalOptions>-YlprecompDefine</AdditionalOptions>
<AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
<MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<Lib>
<AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
<Link>
<SubSystem>$(ProjectSubSystem)</SubSystem>
<ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
<NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
<ClCompile>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
<ClCompile>
<CallingConvention>CDecl</CallingConvention>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
<ClCompile>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
<ClCompile>
<Optimization>Disabled</Optimization>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
<ClCompile>
<BasicRuntimeChecks></BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
<ClCompile>
<Optimization>MinSpace</Optimization>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
<ClCompile>
<BasicRuntimeChecks></BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
<Link>
<KeyFile>$(LinkKeyFile)</KeyFile>
<DelaySign>$(LinkDelaySign)</DelaySign>
</Link>
</ItemDefinitionGroup>
</Project>

Просмотреть файл

@ -8,6 +8,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
<MSBuildWarningsAsMessages>MSB3246</MSBuildWarningsAsMessages>
<ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
<BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
@ -22,5 +23,6 @@
</PropertyGroup>
<Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " />
<Import Project="Wix.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.wixproj' " />
<Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
</Project>

Просмотреть файл

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Product Name="WiX Toolset Theme Viewer" Manufacturer="WiX Toolset" Language="1033"
Version="!(bind.fileVersion.ThmViewerFile)" UpgradeCode="59c4b122-5167-445b-8fc4-09dcd4eced89">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="Main">
<ComponentGroupRef Id="Components" />
</Feature>
</Product>
<Fragment>
<ComponentGroup Id="Components" Directory="INSTALLFOLDER:\bin\">
<Component>
<File Id="ThmViewerFile" Source="thmviewer.exe" />
<Shortcut Name="!(bind.property.ProductName)" Directory="ShortcutFolder" Advertise="yes" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiX Toolset v4.0\" />
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ShortcutFolder" Name="WiX Toolset\" />
</Directory>
</Directory>
</Fragment>
</Wix>

Просмотреть файл

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\Directory.Build.props" />
<PropertyGroup>
<ProjectGuid>59c4b122-5167-445b-8fc4-09dcd4eced89</ProjectGuid>
<OutputName>thmviewer</OutputName>
<OutputType>Package</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="Package.wxs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\thmviewer\thmviewer.vcxproj">
<Name>thmviewer</Name>
<Project>{95228C13-97F5-484A-B4A2-ECF4618B0881}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(OutputPath)\net461\wix.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<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>
</PropertyGroup>
<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'))" />
</Target>
<Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" />
</Project>

Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Nerdbank.GitVersioning" version="2.1.65" developmentDependency="true" targetFramework="net40" />
</packages>

12
src/Wix.Build.props Normal file
Просмотреть файл

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<Project>
<PropertyGroup>
<DefineConstants>$(DefineConstants);CompanyName=$(Company)</DefineConstants>
</PropertyGroup>
<ItemGroup>
<BindInputPaths Include="$(OutputPath)Win32\" />
</ItemGroup>
</Project>

Двоичные данные
src/thmviewer/Resources/LoremIpsum.rtf Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<Theme>
<Font Id="0" Height="-48" Weight="700" Foreground="$windowtext">Consolas</Font>
<Font Id="1" Height="-12" Weight="700" Foreground="$windowtext" Background="$window">Consolas</Font>
<Window Width="220" Height="200" FontId="0" Caption="Theme Viewer">
<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"/>
</Window>
</Theme>

353
src/thmviewer/display.cpp Normal file
Просмотреть файл

@ -0,0 +1,353 @@
// 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.
#include "precomp.h"
static const LPCWSTR THMVWR_WINDOW_CLASS_DISPLAY = L"ThmViewerDisplay";
struct DISPLAY_THREAD_CONTEXT
{
HWND hWnd;
HINSTANCE hInstance;
HANDLE hInit;
};
static DWORD WINAPI DisplayThreadProc(
__in LPVOID pvContext
);
static LRESULT CALLBACK DisplayWndProc(
__in HWND hWnd,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
);
static BOOL DisplayOnCreate(
__in THEME* pTheme,
__in HWND hWnd
);
extern "C" HRESULT DisplayStart(
__in HINSTANCE hInstance,
__in HWND hWnd,
__out HANDLE *phThread,
__out DWORD* pdwThreadId
)
{
HRESULT hr = S_OK;
HANDLE rgHandles[2] = { };
DISPLAY_THREAD_CONTEXT context = { };
rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
context.hWnd = hWnd;
context.hInstance = hInstance;
context.hInit = rgHandles[0];
rgHandles[1] = ::CreateThread(NULL, 0, DisplayThreadProc, reinterpret_cast<LPVOID>(&context), 0, pdwThreadId);
ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create display thread.");
::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
*phThread = rgHandles[1];
rgHandles[1] = NULL;
LExit:
ReleaseHandle(rgHandles[1]);
ReleaseHandle(rgHandles[0]);
return hr;
}
static DWORD WINAPI DisplayThreadProc(
__in LPVOID pvContext
)
{
HRESULT hr = S_OK;
DISPLAY_THREAD_CONTEXT* pContext = static_cast<DISPLAY_THREAD_CONTEXT*>(pvContext);
HINSTANCE hInstance = pContext->hInstance;
HWND hwndParent = pContext->hWnd;
// We can signal the initialization event as soon as we have copied the context
// values into local variables.
::SetEvent(pContext->hInit);
BOOL fComInitialized = FALSE;
HANDLE_THEME* pCurrentHandle = NULL;
ATOM atomWc = 0;
WNDCLASSW wc = { }; // the following are constant for the display window class.
wc.lpfnWndProc = DisplayWndProc;
wc.hInstance = hInstance;
wc.lpszClassName = THMVWR_WINDOW_CLASS_DISPLAY;
HWND hWnd = NULL;
RECT rc = { };
int x = CW_USEDEFAULT;
int y = CW_USEDEFAULT;
BOOL fRedoMsg = FALSE;
BOOL fRet = FALSE;
MSG msg = { };
BOOL fCreateIfNecessary = FALSE;
hr = ::CoInitialize(NULL);
ExitOnFailure(hr, "Failed to initialize COM on display thread.");
fComInitialized = TRUE;
// As long as the parent window is alive and kicking, keep this thread going (with or without a theme to display ).
while (::IsWindow(hwndParent))
{
if (pCurrentHandle && fCreateIfNecessary)
{
THEME* pTheme = pCurrentHandle->pTheme;
if (CW_USEDEFAULT == x && CW_USEDEFAULT == y && ::GetWindowRect(hwndParent, &rc))
{
x = rc.left;
y = rc.bottom + 20;
}
hWnd = ::CreateWindowExW(0, wc.lpszClassName, pTheme->sczCaption, pTheme->dwStyle, x, y, pTheme->nWidth, pTheme->nHeight, hwndParent, NULL, hInstance, pCurrentHandle);
ExitOnNullWithLastError(hWnd, hr, "Failed to create display window.");
fCreateIfNecessary = FALSE;
}
// message pump
while (fRedoMsg || 0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
{
if (fRedoMsg)
{
fRedoMsg = FALSE;
}
if (-1 == fRet)
{
hr = E_UNEXPECTED;
ExitOnFailure(hr, "Unexpected return value from display message pump.");
}
else if (NULL == msg.hwnd) // Thread message.
{
if (WM_THMVWR_NEW_THEME == msg.message)
{
// If there is already a handle, release it.
if (pCurrentHandle)
{
DecrementHandleTheme(pCurrentHandle);
pCurrentHandle = NULL;
}
// If the window was created, remember its window location before we destroy
// it so so we can open the new window in the same place.
if (::IsWindow(hWnd))
{
::GetWindowRect(hWnd, &rc);
x = rc.left;
y = rc.top;
::DestroyWindow(hWnd);
}
// If the display window class was registered, unregister it so we can
// reuse the same window class name for the new theme.
if (atomWc)
{
if (!::UnregisterClassW(reinterpret_cast<LPCWSTR>(atomWc), hInstance))
{
DWORD er = ::GetLastError();
er = er;
}
atomWc = 0;
}
// If we were provided a new theme handle, create a new window class to
// support it.
pCurrentHandle = reinterpret_cast<HANDLE_THEME*>(msg.lParam);
if (pCurrentHandle)
{
wc.hIcon = reinterpret_cast<HICON>(pCurrentHandle->pTheme->hIcon);
wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
if (0 < pCurrentHandle->pTheme->cFonts)
{
wc.hbrBackground = pCurrentHandle->pTheme->rgFonts[pCurrentHandle->pTheme->dwFontId].hBackground;
}
atomWc = ::RegisterClassW(&wc);
if (!atomWc)
{
ExitWithLastError(hr, "Failed to register display window class.");
}
}
}
else if (WM_THMVWR_SHOWPAGE == msg.message)
{
if (pCurrentHandle && ::IsWindow(hWnd) && pCurrentHandle->pTheme->hwndParent == hWnd)
{
DWORD dwPageId = static_cast<DWORD>(msg.lParam);
int nCmdShow = static_cast<int>(msg.wParam);
// First show/hide the controls not associated with a page.
for (DWORD i = 0; i < pCurrentHandle->pTheme->cControls; ++i)
{
THEME_CONTROL* pControl = pCurrentHandle->pTheme->rgControls + i;
if (!pControl->wPageId)
{
ThemeShowControl(pCurrentHandle->pTheme, pControl->wId, nCmdShow);
}
}
// If a page id was provided also, show/hide those controls
if (dwPageId)
{
// Ignore error since we aren't using variables and it can only fail when using variables.
ThemeShowPage(pCurrentHandle->pTheme, dwPageId, nCmdShow);
}
}
else // display window isn't visible or it doesn't match the current handle.
{
// Keep the current message around to try again after we break out of this loop
// and create the window.
fRedoMsg = TRUE;
fCreateIfNecessary = TRUE;
break;
}
}
}
else if (!ThemeHandleKeyboardMessage(pCurrentHandle->pTheme, hwndParent, &msg)) // Window message.
{
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
}
}
}
LExit:
if (::IsWindow(hWnd))
{
::DestroyWindow(hWnd);
}
if (atomWc)
{
if (!::UnregisterClassW(THMVWR_WINDOW_CLASS_DISPLAY, hInstance))
{
DWORD er = ::GetLastError();
er = er;
}
}
DecrementHandleTheme(pCurrentHandle);
if (fComInitialized)
{
::CoUninitialize();
}
return hr;
}
static LRESULT CALLBACK DisplayWndProc(
__in HWND hWnd,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
static DWORD dwProgress = 0;
HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
switch (uMsg)
{
case WM_NCCREATE:
{
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
IncrementHandleTheme(reinterpret_cast<HANDLE_THEME*>(lpcs->lpCreateParams));
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
}
break;
case WM_CREATE:
if (!DisplayOnCreate(pHandleTheme->pTheme, hWnd))
{
return -1;
}
break;
case WM_TIMER:
if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress)))
{
dwProgress += rand() % 10 + 1;
if (dwProgress > 100)
{
dwProgress = 0;
}
return 0;
}
break;
case WM_COMMAND:
{
WCHAR wzText[1024];
::StringCchPrintfW(wzText, countof(wzText), L"Command %u\r\n", LOWORD(wParam));
OutputDebugStringW(wzText);
//::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
}
break;
case WM_SYSCOMMAND:
{
WCHAR wzText[1024];
::StringCchPrintfW(wzText, countof(wzText), L"SysCommand %u\r\n", LOWORD(wParam));
OutputDebugStringW(wzText);
//::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
}
break;
case WM_DESTROY:
ThemeUnloadControls(pHandleTheme->pTheme);
::PostQuitMessage(0);
break;
case WM_NCDESTROY:
DecrementHandleTheme(pHandleTheme);
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
break;
}
return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam);
}
static BOOL DisplayOnCreate(
__in THEME* pTheme,
__in HWND hWnd
)
{
HRESULT hr = S_OK;
hr = ThemeLoadControls(pTheme, hWnd, NULL, 0);
ExitOnFailure(hr, "Failed to load theme controls");
// Pre-populate some control types with data.
for (DWORD i = 0; i < pTheme->cControls; ++i)
{
THEME_CONTROL* pControl = pTheme->rgControls + i;
if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
{
hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL));
ExitOnFailure(hr, "Failed to load richedit text.");
}
else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
{
DWORD dwId = ::SetTimer(hWnd, pControl->wId, 500, NULL);
dwId = dwId; // prevents warning in "ship" build.
Assert(dwId == pControl->wId);
}
}
LExit:
return SUCCEEDED(hr);
}

219
src/thmviewer/load.cpp Normal file
Просмотреть файл

@ -0,0 +1,219 @@
// 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.
#include "precomp.h"
struct LOAD_THREAD_CONTEXT
{
HWND hWnd;
LPCWSTR wzThemePath;
LPCWSTR wzWxlPath;
HANDLE hInit;
};
static DWORD WINAPI LoadThreadProc(
__in LPVOID pvContext
);
extern "C" HRESULT LoadStart(
__in_z LPCWSTR wzThemePath,
__in_z_opt LPCWSTR wzWxlPath,
__in HWND hWnd,
__out HANDLE* phThread
)
{
HRESULT hr = S_OK;
HANDLE rgHandles[2] = { };
LOAD_THREAD_CONTEXT context = { };
rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
context.hWnd = hWnd;
context.wzThemePath = wzThemePath;
context.wzWxlPath = wzWxlPath;
context.hInit = rgHandles[0];
rgHandles[1] = ::CreateThread(NULL, 0, LoadThreadProc, &context, 0, NULL);
ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create load thread.");
::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
*phThread = rgHandles[1];
rgHandles[1] = NULL;
LExit:
ReleaseHandle(rgHandles[1]);
ReleaseHandle(rgHandles[0]);
return hr;
}
static DWORD WINAPI LoadThreadProc(
__in LPVOID pvContext
)
{
HRESULT hr = S_OK;
WIX_LOCALIZATION* pWixLoc = NULL;
LPWSTR sczThemePath = NULL;
LPWSTR sczWxlPath = NULL;
BOOL fComInitialized = FALSE;
HANDLE hDirectory = INVALID_HANDLE_VALUE;
LPWSTR sczDirectory = NULL;
LPWSTR wzFileName = NULL;
THEME* pTheme = NULL;
HANDLE_THEME* pHandle = NULL;
LOAD_THREAD_CONTEXT* pContext = static_cast<LOAD_THREAD_CONTEXT*>(pvContext);
HWND hWnd = pContext->hWnd;
hr = StrAllocString(&sczThemePath, pContext->wzThemePath, 0);
ExitOnFailure(hr, "Failed to copy path to initial theme file.");
if (pContext->wzWxlPath)
{
hr = StrAllocString(&sczWxlPath, pContext->wzWxlPath, 0);
ExitOnFailure(hr, "Failed to copy .wxl path to initial file.");
}
// We can signal the initialization event as soon as we have copied the context
// values into local variables.
::SetEvent(pContext->hInit);
hr = ::CoInitialize(NULL);
ExitOnFailure(hr, "Failed to initialize COM on load thread.");
fComInitialized = TRUE;
// Open a handle to the directory so we can put a notification on it.
hr = PathGetDirectory(sczThemePath, &sczDirectory);
ExitOnFailure(hr, "Failed to get path directory.");
wzFileName = PathFile(sczThemePath);
hDirectory = ::CreateFileW(sczDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == hDirectory)
{
ExitWithLastError(hr, "Failed to open directory: %ls", sczDirectory);
}
BOOL fUpdated = FALSE;
do
{
// Get the last modified time on the file we're loading for verification that the
// file actually gets changed down below.
FILETIME ftModified = { };
FileGetTime(sczThemePath, NULL, NULL, &ftModified);
// Try to load the theme file.
hr = ThemeLoadFromFile(sczThemePath, &pTheme);
if (FAILED(hr))
{
::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_ERROR, 0, hr);
}
else
{
if (sczWxlPath)
{
hr = LocLoadFromFile(sczWxlPath, &pWixLoc);
ExitOnFailure(hr, "Failed to load loc file from path: %ls", sczWxlPath);
hr = ThemeLocalize(pTheme, pWixLoc);
ExitOnFailure(hr, "Failed to localize theme: %ls", sczWxlPath);
}
hr = AllocHandleTheme(pTheme, &pHandle);
ExitOnFailure(hr, "Failed to allocate handle to theme");
::SendMessageW(hWnd, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
pHandle = NULL;
}
fUpdated = FALSE;
do
{
DWORD rgbNotifications[1024];
DWORD cbRead = 0;
if (!::ReadDirectoryChangesW(hDirectory, rgbNotifications, sizeof(rgbNotifications), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &cbRead, NULL, NULL))
{
ExitWithLastError(hr, "Failed while watching directory: %ls", sczDirectory);
}
// Wait for half a second to let all the file handles get closed to minimize access
// denied errors.
::Sleep(500);
FILE_NOTIFY_INFORMATION* pNotification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(rgbNotifications);
while (pNotification)
{
// If our file was updated, check to see if the modified time really changed. The notifications
// are often trigger happy thinking the file changed two or three times in a row. Maybe it's AV
// software creating the problems but actually checking the modified date works well.
if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pNotification->FileName, pNotification->FileNameLength / sizeof(WCHAR), wzFileName, -1))
{
FILETIME ft = { };
FileGetTime(sczThemePath, NULL, NULL, &ft);
fUpdated = (ftModified.dwHighDateTime < ft.dwHighDateTime) || (ftModified.dwHighDateTime == ft.dwHighDateTime && ftModified.dwLowDateTime < ft.dwLowDateTime);
break;
}
pNotification = pNotification->NextEntryOffset ? reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(pNotification) + pNotification->NextEntryOffset) : NULL;
}
} while (!fUpdated);
} while(fUpdated);
LExit:
if (fComInitialized)
{
::CoUninitialize();
}
LocFree(pWixLoc);
ReleaseFileHandle(hDirectory);
ReleaseStr(sczDirectory);
ReleaseStr(sczThemePath);
ReleaseStr(sczWxlPath);
return hr;
}
extern "C" HRESULT AllocHandleTheme(
__in THEME* pTheme,
__out HANDLE_THEME** ppHandle
)
{
HRESULT hr = S_OK;
HANDLE_THEME* pHandle = NULL;
pHandle = static_cast<HANDLE_THEME*>(MemAlloc(sizeof(HANDLE_THEME), TRUE));
ExitOnNull(pHandle, hr, E_OUTOFMEMORY, "Failed to allocate theme handle.");
pHandle->cReferences = 1;
pHandle->pTheme = pTheme;
*ppHandle = pHandle;
pHandle = NULL;
LExit:
ReleaseMem(pHandle);
return hr;
}
extern "C" void IncrementHandleTheme(
__in HANDLE_THEME* pHandle
)
{
::InterlockedIncrement(reinterpret_cast<LONG*>(&pHandle->cReferences));
}
extern "C" void DecrementHandleTheme(
__in HANDLE_THEME* pHandle
)
{
if (pHandle && 0 == ::InterlockedDecrement(reinterpret_cast<LONG*>(&pHandle->cReferences)))
{
ThemeFree(pHandle->pTheme);
MemFree(pHandle);
}
}

Просмотреть файл

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Nerdbank.GitVersioning" version="2.1.65" targetFramework="native" developmentDependency="true" />
<package id="WixToolset.DUtil" version="4.0.6" targetFramework="native" />
</packages>

Просмотреть файл

@ -0,0 +1,3 @@
// 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.
#include "precomp.h"

69
src/thmviewer/precomp.h Normal file
Просмотреть файл

@ -0,0 +1,69 @@
#pragma once
// 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.
#include <windows.h>
#include <msiquery.h>
#include <objbase.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <stdlib.h>
#include <strsafe.h>
#pragma warning(push)
#pragma warning(disable:4458)
#include <gdiplus.h>
#pragma warning(pop)
#include "dutil.h"
#include "apputil.h"
#include "memutil.h"
#include "dirutil.h"
#include "fileutil.h"
#include "locutil.h"
#include "logutil.h"
#include "pathutil.h"
#include "resrutil.h"
#include "shelutil.h"
#include "strutil.h"
#include "thmutil.h"
#include "resource.h"
struct HANDLE_THEME
{
DWORD cReferences;
THEME* pTheme;
};
enum WM_THMVWR
{
WM_THMVWR_SHOWPAGE = WM_APP,
WM_THMVWR_PARSE_FILE,
WM_THMVWR_NEW_THEME,
WM_THMVWR_THEME_LOAD_ERROR,
};
extern "C" HRESULT DisplayStart(
__in HINSTANCE hInstance,
__in HWND hWnd,
__out HANDLE *phThread,
__out DWORD* pdwThreadId
);
extern "C" HRESULT LoadStart(
__in_z LPCWSTR wzThemePath,
__in_z LPCWSTR wzWxlPath,
__in HWND hWnd,
__out HANDLE* phThread
);
extern "C" HRESULT AllocHandleTheme(
__in THEME* pTheme,
__out HANDLE_THEME** ppHandle
);
extern "C" void IncrementHandleTheme(
__in HANDLE_THEME* pHandle
);
extern "C" void DecrementHandleTheme(
__in HANDLE_THEME* pHandle
);

16
src/thmviewer/resource.h Normal file
Просмотреть файл

@ -0,0 +1,16 @@
// 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.
#define IDC_STATIC -1
#define THMVWR_RES_THEME_FILE 1
#define THMVWR_RES_RICHEDIT_FILE 2
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

465
src/thmviewer/thmviewer.cpp Normal file
Просмотреть файл

@ -0,0 +1,465 @@
// 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.
#include "precomp.h"
static const LPCWSTR THMVWR_WINDOW_CLASS_MAIN = L"ThmViewerMain";
static THEME* vpTheme = NULL;
static DWORD vdwDisplayThreadId = 0;
enum THMVWR_CONTROL
{
// Non-paged controls
THMVWR_CONTROL_TREE = THEME_FIRST_ASSIGN_CONTROL_ID,
};
static THEME_ASSIGN_CONTROL_ID vrgInitControls[] = {
{ THMVWR_CONTROL_TREE, L"Tree" },
};
// Internal functions
static HRESULT ProcessCommandLine(
__in_z_opt LPCWSTR wzCommandLine,
__out_z LPWSTR* psczThemeFile,
__out_z LPWSTR* psczWxlFile
);
static HRESULT CreateTheme(
__in HINSTANCE hInstance,
__out THEME** ppTheme
);
static HRESULT CreateMainWindowClass(
__in HINSTANCE hInstance,
__in THEME* pTheme,
__out ATOM* pAtom
);
static LRESULT CALLBACK MainWndProc(
__in HWND hWnd,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
);
static void OnThemeLoadError(
__in THEME* pTheme,
__in HRESULT hrFailure
);
static void OnNewTheme(
__in THEME* pTheme,
__in HWND hWnd,
__in HANDLE_THEME* pHandle
);
int WINAPI wWinMain(
__in HINSTANCE hInstance,
__in_opt HINSTANCE /* hPrevInstance */,
__in_z LPWSTR lpCmdLine,
__in int /*nCmdShow*/
)
{
::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
HRESULT hr = S_OK;
BOOL fComInitialized = FALSE;
LPWSTR sczThemeFile = NULL;
LPWSTR sczWxlFile = NULL;
ATOM atom = 0;
HWND hWnd = NULL;
HANDLE hDisplayThread = NULL;
HANDLE hLoadThread = NULL;
BOOL fRet = FALSE;
MSG msg = { };
hr = ::CoInitialize(NULL);
ExitOnFailure(hr, "Failed to initialize COM.");
fComInitialized = TRUE;
hr = ProcessCommandLine(lpCmdLine, &sczThemeFile, &sczWxlFile);
ExitOnFailure(hr, "Failed to process command line.");
hr = CreateTheme(hInstance, &vpTheme);
ExitOnFailure(hr, "Failed to create theme.");
hr = CreateMainWindowClass(hInstance, vpTheme, &atom);
ExitOnFailure(hr, "Failed to create main window.");
hWnd = ::CreateWindowExW(0, reinterpret_cast<LPCWSTR>(atom), vpTheme->sczCaption, vpTheme->dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, vpTheme->nWidth, vpTheme->nHeight, HWND_DESKTOP, NULL, hInstance, NULL);
ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
if (!sczThemeFile)
{
// Prompt for a path to the theme file.
OPENFILENAMEW ofn = { };
WCHAR wzFile[MAX_PATH] = { };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = wzFile;
ofn.nMaxFile = countof(wzFile);
ofn.lpstrFilter = L"Theme Files\0*.thm\0XML Files\0*.xml\0All Files\0*.*\0";
ofn.nFilterIndex = 1;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
ofn.lpstrTitle = vpTheme->sczCaption;
if (::GetOpenFileNameW(&ofn))
{
hr = StrAllocString(&sczThemeFile, wzFile, 0);
ExitOnFailure(hr, "Failed to copy opened file to theme file.");
}
else
{
::MessageBoxW(hWnd, L"Must specify a path to theme file.", vpTheme->sczCaption, MB_OK | MB_ICONERROR);
ExitFunction1(hr = E_INVALIDARG);
}
}
hr = DisplayStart(hInstance, hWnd, &hDisplayThread, &vdwDisplayThreadId);
ExitOnFailure(hr, "Failed to start display.");
hr = LoadStart(sczThemeFile, sczWxlFile, hWnd, &hLoadThread);
ExitOnFailure(hr, "Failed to start load.");
// message pump
while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
{
if (-1 == fRet)
{
hr = E_UNEXPECTED;
ExitOnFailure(hr, "Unexpected return value from message pump.");
}
else if (!ThemeHandleKeyboardMessage(vpTheme, msg.hwnd, &msg))
{
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
}
}
LExit:
if (::IsWindow(hWnd))
{
::DestroyWindow(hWnd);
}
if (hDisplayThread)
{
::PostThreadMessageW(vdwDisplayThreadId, WM_QUIT, 0, 0);
::WaitForSingleObject(hDisplayThread, 10000);
::CloseHandle(hDisplayThread);
}
// TODO: come up with a good way to kill the load thread, probably need to switch
// the ReadDirectoryW() to overlapped mode.
ReleaseHandle(hLoadThread);
if (atom && !::UnregisterClassW(reinterpret_cast<LPCWSTR>(atom), hInstance))
{
DWORD er = ::GetLastError();
er = er;
}
ThemeFree(vpTheme);
ThemeUninitialize();
// uninitialize COM
if (fComInitialized)
{
::CoUninitialize();
}
ReleaseStr(sczThemeFile);
ReleaseStr(sczWxlFile);
return hr;
}
//
// ProcessCommandLine - process the provided command line arguments.
//
static HRESULT ProcessCommandLine(
__in_z_opt LPCWSTR wzCommandLine,
__out_z LPWSTR* psczThemeFile,
__out_z LPWSTR* psczWxlFile
)
{
HRESULT hr = S_OK;
int argc = 0;
LPWSTR* argv = NULL;
if (wzCommandLine && *wzCommandLine)
{
hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
ExitOnFailure(hr, "Failed to parse command line.");
for (int i = 0; i < argc; ++i)
{
if (argv[i][0] == L'-' || argv[i][0] == L'/')
{
if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
{
if (i + 1 >= argc)
{
ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a language.");
}
++i;
}
}
else
{
LPCWSTR wzExtension = PathExtension(argv[i]);
if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzExtension, -1, L".wxl", -1))
{
hr = StrAllocString(psczWxlFile, argv[i], 0);
}
else
{
hr = StrAllocString(psczThemeFile, argv[i], 0);
}
ExitOnFailure(hr, "Failed to copy path to file.");
}
}
}
LExit:
if (argv)
{
AppFreeCommandLineArgs(argv);
}
return hr;
}
static HRESULT CreateTheme(
__in HINSTANCE hInstance,
__out THEME** ppTheme
)
{
HRESULT hr = S_OK;
hr = ThemeInitialize(hInstance);
ExitOnFailure(hr, "Failed to initialize theme manager.");
hr = ThemeLoadFromResource(hInstance, MAKEINTRESOURCEA(THMVWR_RES_THEME_FILE), ppTheme);
ExitOnFailure(hr, "Failed to load theme from thmviewer.thm.");
LExit:
return hr;
}
static HRESULT CreateMainWindowClass(
__in HINSTANCE hInstance,
__in THEME* pTheme,
__out ATOM* pAtom
)
{
HRESULT hr = S_OK;
ATOM atom = 0;
WNDCLASSW wc = { };
wc.lpfnWndProc = MainWndProc;
wc.hInstance = hInstance;
wc.hIcon = reinterpret_cast<HICON>(pTheme->hIcon);
wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
wc.hbrBackground = pTheme->rgFonts[pTheme->dwFontId].hBackground;
wc.lpszMenuName = NULL;
wc.lpszClassName = THMVWR_WINDOW_CLASS_MAIN;
atom = ::RegisterClassW(&wc);
if (!atom)
{
ExitWithLastError(hr, "Failed to register main windowclass .");
}
*pAtom = atom;
LExit:
return hr;
}
static LRESULT CALLBACK MainWndProc(
__in HWND hWnd,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
switch (uMsg)
{
case WM_NCCREATE:
{
//LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
//pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams);
//::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
}
break;
case WM_NCDESTROY:
DecrementHandleTheme(pHandleTheme);
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
break;
case WM_CREATE:
{
HRESULT hr = ThemeLoadControls(vpTheme, hWnd, vrgInitControls, countof(vrgInitControls));
if (FAILED(hr))
{
return -1;
}
}
break;
case WM_THMVWR_THEME_LOAD_ERROR:
OnThemeLoadError(vpTheme, lParam);
return 0;
case WM_THMVWR_NEW_THEME:
OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam));
return 0;
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_NOTIFY:
{
NMHDR* pnmhdr = reinterpret_cast<NMHDR*>(lParam);
switch (pnmhdr->code)
{
case TVN_SELCHANGEDW:
{
NMTREEVIEWW* ptv = reinterpret_cast<NMTREEVIEWW*>(lParam);
::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_HIDE, ptv->itemOld.lParam);
::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, ptv->itemNew.lParam);
}
break;
//case NM_DBLCLK:
// TVITEM item = { };
// item.mask = TVIF_PARAM;
// item.hItem = TreeView_GetSelection(pnmhdr->hwndFrom);
// TreeView_GetItem(pnmhdr->hwndFrom, &item);
// ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, item.lParam);
// return 1;
}
}
break;
}
return ThemeDefWindowProc(vpTheme, hWnd, uMsg, wParam, lParam);
}
static void OnThemeLoadError(
__in THEME* pTheme,
__in HRESULT hrFailure
)
{
HRESULT hr = S_OK;
LPWSTR sczMessage = NULL;
TVINSERTSTRUCTW tvi = { };
// Add the application node.
tvi.hParent = NULL;
tvi.hInsertAfter = TVI_ROOT;
tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
tvi.item.lParam = 0;
tvi.item.pszText = L"Failed to load theme.";
tvi.hParent = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x.", hrFailure);
ExitOnFailure(hr, "Failed to format error message.");
tvi.item.pszText = sczMessage;
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
hr = StrAllocFromError(&sczMessage, hrFailure, NULL);
ExitOnFailure(hr, "Failed to format error message text.");
tvi.item.pszText = sczMessage;
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
LExit:
ReleaseStr(sczMessage);
}
static void OnNewTheme(
__in THEME* pTheme,
__in HWND hWnd,
__in HANDLE_THEME* pHandle
)
{
HANDLE_THEME* pOldHandle = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
THEME* pNewTheme = pHandle->pTheme;
WCHAR wzSelectedPage[MAX_PATH] = { };
HTREEITEM htiSelected = NULL;
TVINSERTSTRUCTW tvi = { };
TVITEMW item = { };
if (pOldHandle)
{
DecrementHandleTheme(pOldHandle);
pOldHandle = NULL;
}
// Pass the new theme handle to the display thread so it can get the display window prepared
// to show the new theme.
IncrementHandleTheme(pHandle);
::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pHandle));
// Remember the currently selected item by name so we can try to automatically select it later.
// Otherwise, the user would see their window destroyed after every save of their theme file and
// have to click to get the window back.
item.mask = TVIF_TEXT;
item.pszText = wzSelectedPage;
item.cchTextMax = countof(wzSelectedPage);
item.hItem = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_GETNEXTITEM, TVGN_CARET, NULL));
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item));
// Remove the previous items in the tree.
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_DELETEITEM, 0, reinterpret_cast<LPARAM>(TVI_ROOT));
// Add the application node.
tvi.hParent = NULL;
tvi.hInsertAfter = TVI_ROOT;
tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
tvi.item.lParam = 0;
tvi.item.pszText = pHandle && pHandle->pTheme && pHandle->pTheme->sczCaption ? pHandle->pTheme->sczCaption : L"Window";
// Add the pages.
tvi.hParent = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
tvi.hInsertAfter = TVI_SORT;
for (DWORD i = 0; i < pNewTheme->cPages; ++i)
{
THEME_PAGE* pPage = pNewTheme->rgPages + i;
if (pPage->sczName && *pPage->sczName)
{
tvi.item.pszText = pPage->sczName;
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.
HTREEITEM hti = reinterpret_cast<HTREEITEM>(ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pPage->sczName, -1, wzSelectedPage, -1))
{
htiSelected = hti;
}
}
}
if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Application", -1, wzSelectedPage, -1))
{
htiSelected = tvi.hParent;
}
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
if (htiSelected)
{
ThemeSendControlMessage(pTheme, THMVWR_CONTROL_TREE, TVM_SELECTITEM, TVGN_CARET, reinterpret_cast<LPARAM>(htiSelected));
}
}

Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="thmviewer.exe" version="1.0.0.0" processorArchitecture="x86" type="win32"/>
<description>WiX Toolset Theme Viewer</description>
<dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /></dependentAssembly></dependency>
<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>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo>
</assembly>

Просмотреть файл

@ -0,0 +1,12 @@
// 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.
#include <winver.h>
#include <windows.h>
#include "resource.h"
//#define MANIFEST_RESOURCE_ID 1
//MANIFEST_RESOURCE_ID RT_MANIFEST "thmviewer.manifest"
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
THMVWR_RES_THEME_FILE RCDATA "Resources\\thm.xml"
THMVWR_RES_RICHEDIT_FILE RCDATA "Resources\\LoremIpsum.rtf"

Просмотреть файл

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{95228C13-97F5-484A-B4A2-ECF4618B0881}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<Description>WiX Toolset Theme Viewer</Description>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
<Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" />
</ImportGroup>
<PropertyGroup>
<ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="display.cpp" />
<ClCompile Include="load.cpp" />
<ClCompile Include="precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="thmviewer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="precomp.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\LoremIpsum.rtf" />
<None Include="Resources\thm.xml" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="thmviewer.rc" />
</ItemGroup>
<ItemGroup>
<Manifest Include="thmviewer.manifest" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<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>
</PropertyGroup>
<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'))" />
<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'))" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="thmviewer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="display.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="load.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="thmviewer.build" />
<None Include="Resources\thm.xml">
<Filter>Resource Files</Filter>
</None>
<None Include="Resources\LoremIpsum.rtf">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="precomp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="thmviewer.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="thmviewer.manifest" />
</ItemGroup>
</Project>