[0.16]: Merge Preview Handlers in Master (#1516)

* Added project template for common library

* Added reference to stylecop.json

* Fixed xml documetation file path for common project

* Added reference to stylecop.json

* Added COM interface interpolations to C#

* Changed namespace to Common

* Added xml doc to com interfaces

* Removed AnyCPU configuration from solution file

* Added Preview Hander and form User Control Implementation

* Fix stylecop warnings

* Added test control and handler

* Added Xaml description for preview handler

* Added Xml documenatation

* Updated the control to form

* Added registration and unregistration logic for the handler

* Moved the files in separate folder and fix PR comments

* updated the name of previewhandler class to base class

* Added the DoPreview to PreviewHandlerControl Interface

* Modified the Dopreview and Unload as virtual method

* Uncommented the DocumentText to help bug repro

* HTML Parsing Extension for preview pane markdown renderer (#1108)

* Added Extension for html post processing

* Added unit test poroject for preview pane

* Added pipline test and base test function

* Added Tests for extension

* Added tests for url slashes

* Added tests for url and figure caption

* Markdown preview pane (#1128)

* Added Extension for html post processing

* Added unit test poroject for preview pane

* Added pipline test and base test function

* Added Tests for extension

* Added handler and control for markdown

* Tests added

* Locally working version for markdown

* Working image relative url's in markdown

* Added CSS to preview display

* Updates CSS for code block

* Removed html file write comment in markdown control

* Updated assembly version and web browser control test

* Add Svg preview handler (#1129)

* Added a new project for Svg preview handler

* Added initial implementation of Svg Preview Handler

* Fixed output path

* Added Unit Test Project

* Added StreamWrapper and Update Svg Control

* Updated Svg Handler Guid

* Removed migration backup folder

* Removed Fluent Assertions NuGet

* Added Comments for StreamWrapper

* Removed the manual GC collect

* Added unit tests for Svg preview Handler

* Updated the xml doc for stream wrapper

* User/lamotile/add_powerpreviewsettings (#1075)

* Added powersettings to PowerToys Settings UI

* added settings library

* updated settings-web

* updated project oncfiguration

* updated project onfiguration

* updated project .sln file

* removed .etl file and added it to git-ignore

* separated the PowerPreviewModule into split classes .cpp and .h

* moved PowerPreviewModule implemnetations to .cpp file

* fixed StringTable formatter

* fixed spacing in resource.h

* added m_ to member varibales

* initiliaze m_isPreviewEnabled in the base class

* removed duplication of objects by using pass by refference and std::move

* made the getters const

* updated naming convention

* Split test calsses

* Add const string

* Replaced move with const string

* Made attributes private

* Made attributes private

* removed unused constructor

* Update resource.h

formatted resource.h

* Adding unit tests for preview handler common (#1156)

* Changed the name of the Common library to PreviewHandlerCommon

* Added unit tests project for PreviewHandlerCommon

* Updated ComInterop accessor type

* Added unit tests for PreviewHandlerbase

* Added tests for file and stream based handler

* Added unit tests for StreamWrapper

* Added form handler unit tests

* Added Unit tests for FormHandlerControl

* Added file header

* Add Powerpreview project

* Add spacing in sln file

* swapped string refferences (#1199)

* added registry methods and enable/disable preview handlers (#1230)

* added registry methods and enable/disable preview handlers

* formatted .rc file.

* formatted resource file

* formatted .rc file.

* formatted settings.cpp

* formatted settings.h

* formatted SVGPreviewSettingsClassTest.cpp

* Formatted MarkDownPreviewSettingsClassTest.cpp

* using wide strings

* formatted settings.h

* FileExplorerPreviewSettingsTest.cpp formatting

* fixed typo and formatting

* closing Registry and fixing typos

* formmarted code using ctrl+k+d

* fixed naming

* fixed typo

* changed if/else reverse order

* Markdown preview pane  (#1220)

* Added rich text bar for information display

* Added infobar

* Added tests for extension and markdown control

* Added xml docstring for markdown preview handler control

* Updated assembly file for markdown preview pane

* Updated removal of script tag without modifying CSS

* Added info bar text string to resource file.

* Removed error with infobar display on first rendering

* Updated assembly version

* Remove script and image element from Svg (#1231)

* Added implementation to remove script and image tag

* Added Unit tests for SvgPreviewHandlerHelper

* Updated Unit tests for SvgPreviewControl

* Moved the hardcoded string to resource file

* Changed the LocalMachine to CurrentUser for preview handler registration

* Added unit tests for multiple blocked elements tags

* User/lamotile/update settings objects (#1263)

* added registry methods and enable/disable preview handlers

* formatted .rc file.

* formatted resource file

* formatted .rc file.

* formatted settings.cpp

* formatted settings.h

* formatted SVGPreviewSettingsClassTest.cpp

* Formatted MarkDownPreviewSettingsClassTest.cpp

* using wide strings

* formatted settings.h

* FileExplorerPreviewSettingsTest.cpp formatting

* fixed typo and formatting

* closing Registry and fixing typos

* formmarted code using ctrl+k+d

* fixed naming

* fixed typo

* changed if/else reverse order

* updated setiings_objects.cpp

* removed changes on files that are not part of this PR

* removed const ref on primative types

* updated pass by ref semantic and removed pas by reff on primative types

* fixed spaces in the commas

* fixed spaces in brackets

* Preview pane telemetry (#1299)

* Added telemetry base class and markdown telemetry class

* Updated docstring for telemetry event.

* Added telemetry to markdown for error

* Added try catch for markdown preview handler and display error bar

* Updated markdown telemetry to make event names global variable

* Updated parameter name to camel casing and telemetry event name naming.

* Corrected assembbly version for svg renderer

* Markdown Image files display (#1303)

* Added telemetry base class and markdown telemetry class

* Updated docstring for telemetry event.

* Added telemetry to markdown for error

* Added try catch for markdown preview handler and display error bar

* Updated markdown telemetry to make event names global variable

* Updated Markdown preview to display without vertical scrollbar and removed xml doc to html agility pack.

* Updated parameter name to camel casing and telemetry event name naming.

* Corrected assembbly version for svg renderer

* Removed duplicate function

* Add telemetry for Svg preview handler (#1314)

* Added telemetry events for Svg Preview Handler

* Added unit test in case preview handler throws

* Updated the Error event name

* Remove the not required return statement

* User/lamotile/add read me (#1332)

* add readme

* moved images

* re-added images'

* Fixed gramma

* Update figure number refference

* improve preview handler intergration (#1319)

* improve preview handler intergration

* Fixed typo

* updated typo

* updated enable() function

* updated is enabled()

* re-updated is enabled()

* added this-> key word

* (0.16) - Install preview handler with msix (#1339)

* Removed the registration logic from preview handlers

* Updated the output path

* Added logic to shim the activation of .net assembly

* Updated manifest file

* Fix the allowedsilenttakeover filed in manifest

* Updated the appxmanifest file

* Added AllowSilentDefaultTakeOver in manifest

* Fix returned error code by DllGetClassObject

* Moved the CLSID to a common header file

* Added info about where CLSID needs to be updated

* Added a .reg file for the Keys added in registry.dat

* Added comments for DllGetClassObject

* Svg Preview Handler block external component  (#1368)

* Removed the registration logic from preview handlers

* Updated the output path

* Added logic to shim the activation of .net assembly

* Updated manifest file

* Fix the allowedsilenttakeover filed in manifest

* Updated the appxmanifest file

* Added AllowSilentDefaultTakeOver in manifest

* Fix returned error code by DllGetClassObject

* Moved the CLSID to a common header file

* Added info about where CLSID needs to be updated

* Added a .reg file for the Keys added in registry.dat

* Added comments for DllGetClassObject

* Extended WebBrowserSite for setting flags in DISPID_AMBIENT_DLCONTROL

* Added XML Documenatation

* Removed the logic for deleting image and script element from Svg

* Updated Unit Tests

* Updated typecast of uint

* Forwarded calls to Type.InvokeMember

* Resolve PR Comments

* Adding MSI Installation for Preview Handler (#1436)

* Updated wxs for including dll and registry keys for preview handlers

* Changed the casing for registry key

* Resolve PR Comments

* Added comments for File element

* Call GC on preview unloading (#1456)

* Call GC collect on preview unloading

* Added github issue link

* Update Web browser control for Markdown Previewer (#1464)

* Updated Webbrowser control

* Updated Unit tests

* Disabled Navigation for Svg Previewer

* Fix power preview unit tests (#1508)

* Decoupled registry interaction logic

* Updated File explorer settings

* Updated unit tests for PowerPreview Settings

* Added Asserrtion for Scope of registry key

* Updated the registry value to match with installation registry value

* Sync master settings.cpp

* Merge settings changes from PreviewHandlers

* Remove newline changes added into in example_powertoy\trace.cpp

* Chaned .net framework to 4.7.2

* Updated Csproj files to auto generate Assembly.info files

* Updated msi installer to use version variable for preview handlers assembly

* Removed the signing of Assembly and updated wxs to not include PublicKeyToken

* Updated the Path in Packaginglayout.xml to modules from Gac and the registry hive binary

* Regenerated updated JS and html file

* Resolve PR Comments

* Readded the wprp file

Co-authored-by: Divyansh <divyan@microsoft.com>
Co-authored-by: Divyansh <somm14divi@gmail.com>
Co-authored-by: Lavius Motileng <58791731+laviusmotileng-ms@users.noreply.github.com>
This commit is contained in:
udit3333 2020-03-11 15:53:49 -07:00 committed by GitHub
parent f170cef506
commit 83ce8a2197
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 8787 additions and 1850 deletions

3
.gitignore vendored
View file

@ -332,3 +332,6 @@ ASALocalRun/
# Temp build files
src/settings/settings-html/200.html
src/settings/settings-html/404.html
# Temp telemetry files.
src/common/Telemetry/*.etl

View file

@ -1,4 +1,4 @@


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.452
@ -130,6 +130,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msi_to_msix_upgrade_lib", "src\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "previewpane", "previewpane", "{2F305555-C296-497E-AC20-5FA1B237996A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PreviewHandlerCommon", "src\modules\previewpane\Common\PreviewHandlerCommon.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownPreviewHandler", "src\modules\previewpane\MarkDownPreviewHandler\MarkdownPreviewHandler.csproj", "{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-MarkdownPreviewHandler", "src\modules\previewpane\PreviewPaneUnitTests\UnitTests-MarkdownPreviewHandler.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgPreviewHandler", "src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj", "{DA425894-6E13-404F-8DCB-78584EC0557A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-SvgPreviewHandler", "src\modules\previewpane\UnitTests-SvgPreviewHandler\UnitTests-SvgPreviewHandler.csproj", "{060D75DA-2D1C-48E6-A4A1-6F0718B64661}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-PreviewHandlerCommon", "src\modules\previewpane\UnitTests-PreviewHandlerCommon\UnitTests-PreviewHandlerCommon.csproj", "{748417CA-F17E-487F-9411-CAFB6D3F4877}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreview", "src\modules\previewpane\powerpreview\powerpreview.vcxproj", "{217DF501-135C-4E38-BFC8-99D4821032EA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreviewTest", "src\modules\previewpane\powerpreviewTest\powerpreviewTest.vcxproj", "{47310AB4-9034-4BD1-8D8B-E88AD21A171B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -228,6 +246,38 @@ Global
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.Build.0 = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.ActiveCfg = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.Build.0 = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.ActiveCfg = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.ActiveCfg = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.Build.0 = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.ActiveCfg = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.Build.0 = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.ActiveCfg = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.Build.0 = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.ActiveCfg = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.Build.0 = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.ActiveCfg = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.Build.0 = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.ActiveCfg = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.Build.0 = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.ActiveCfg = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.Build.0 = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.ActiveCfg = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.Build.0 = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.ActiveCfg = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.Build.0 = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.ActiveCfg = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -257,6 +307,15 @@ Global
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{2F305555-C296-497E-AC20-5FA1B237996A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{AF2349B8-E5B6-4004-9502-687C1C7730B1} = {2F305555-C296-497E-AC20-5FA1B237996A}
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {2F305555-C296-497E-AC20-5FA1B237996A}
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} = {2F305555-C296-497E-AC20-5FA1B237996A}
{DA425894-6E13-404F-8DCB-78584EC0557A} = {2F305555-C296-497E-AC20-5FA1B237996A}
{060D75DA-2D1C-48E6-A4A1-6F0718B64661} = {2F305555-C296-497E-AC20-5FA1B237996A}
{748417CA-F17E-487F-9411-CAFB6D3F4877} = {2F305555-C296-497E-AC20-5FA1B237996A}
{217DF501-135C-4E38-BFC8-99D4821032EA} = {2F305555-C296-497E-AC20-5FA1B237996A}
{47310AB4-9034-4BD1-8D8B-E88AD21A171B} = {2F305555-C296-497E-AC20-5FA1B237996A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -26,6 +26,13 @@
<File DestinationPath="modules\System.ValueTuple.dll" SourcePath="..\..\x64\Release\modules\System.ValueTuple.dll"/>
<File DestinationPath="modules\System.Numerics.Vectors.dll" SourcePath="..\..\x64\Release\modules\System.Numerics.Vectors.dll"/>
<File DestinationPath="modules\Microsoft.Bcl.AsyncInterfaces.dll" SourcePath="..\..\x64\Release\modules\Microsoft.Bcl.AsyncInterfaces.dll"/>
<File DestinationPath="modules\powerpreview.dll" SourcePath="..\..\x64\Release\modules\powerpreview.dll"/>
<File DestinationPath="modules\PreviewHandlerCommon.dll" SourcePath="..\..\x64\Release\modules\PreviewHandlerCommon.dll"/>
<File DestinationPath="modules\SvgPreviewHandler.dll" SourcePath="..\..\x64\Release\modules\SvgPreviewHandler.dll"/>
<File DestinationPath="modules\MarkdownPreviewHandler.dll" SourcePath="..\..\x64\Release\modules\MarkdownPreviewHandler.dll"/>
<File DestinationPath="modules\Markdig.Signed.dll" SourcePath="..\..\x64\Release\modules\Markdig.Signed.dll"/>
<File DestinationPath="modules\HtmlAgilityPack.dll" SourcePath="..\..\x64\Release\modules\HtmlAgilityPack.dll"/>
<File DestinationPath="registry.dat" SourcePath="registry.dat"/>
<File DestinationPath="modules\FancyZonesEditor.exe.config" SourcePath="..\..\x64\Release\modules\FancyZonesEditor.exe.config"/>

View file

@ -2,9 +2,12 @@
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" IgnorableNamespaces="desktop4">
<Identity Name="Microsoft.PowerToys" Version="0.15.2.0" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" ProcessorArchitecture="x64" />
@ -48,6 +51,30 @@
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>
<uap:Extension Category="windows.fileTypeAssociation">
<uap3:FileTypeAssociation Name="mdpreviewhandler" desktop2:AllowSilentDefaultTakeOver="true">
<uap:SupportedFileTypes>
<uap:FileType>.md</uap:FileType>
</uap:SupportedFileTypes>
<desktop2:DesktopPreviewHandler Clsid="E0907A95-6F9A-4D1B-A97A-7D9D2648881E"/>
</uap3:FileTypeAssociation>
</uap:Extension>
<uap:Extension Category="windows.fileTypeAssociation">
<uap3:FileTypeAssociation Name="svgpreviewhandler" desktop2:AllowSilentDefaultTakeOver="true">
<uap:SupportedFileTypes>
<uap:FileType>.svg</uap:FileType>
</uap:SupportedFileTypes>
<desktop2:DesktopPreviewHandler Clsid="74619BDA-A66B-451D-864C-A7726F5FE650"/>
</uap3:FileTypeAssociation>
</uap:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="Preview Handler" AppId="E39A92FE-D89A-417B-9B9D-F0B6BD564B36" SystemSurrogate="PreviewHost">
<com:Class Id="74619BDA-A66B-451D-864C-A7726F5FE650" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
<com:Class Id="E0907A95-6F9A-4D1B-A97A-7D9D2648881E" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
<Extension Category="windows.backgroundTasks" EntryPoint="PowerToysNotifications.BackgroundHandler">
<BackgroundTasks>
<Task Type="general" />

BIN
installer/MSIX/registry.dat Normal file

Binary file not shown.

BIN
installer/MSIX/registry.reg Normal file

Binary file not shown.

View file

@ -277,6 +277,80 @@
<RegistryValue Type="string" Value="{0440049F-D1DC-4E46-B27B-98393D79486B}"/>
</RegistryKey>
</Component>
<Component Id="Module_PowerPreview" Guid="FF1700D5-1B07-4E07-9A62-4D206645EEA9" Win64="yes">
<!-- Component to include PowerPreview Module Source dll's -->
<!-- File to include PowerPreview Module native dll -->
<File Source="$(var.BinX64Dir)\modules\powerpreview.dll" KeyPath="yes" />
<!-- File to include common library used by preview handlers -->
<File Source="$(var.BinX64Dir)\modules\PreviewHandlerCommon.dll" />
<!-- File to include dll for Svg Preview Handler -->
<File Id="Svg_PreviewHandler_Dll" Source="$(var.BinX64Dir)\modules\SvgPreviewHandler.dll" />
<!-- Files to include dll's for Markdown Preview Handler and it's dependencies -->
<File Id="Md_PreviewHandler_Dll" Source="$(var.BinX64Dir)\modules\MarkdownPreviewHandler.dll" />
<File Source="$(var.BinX64Dir)\modules\Markdig.Signed.dll" />
<File Source="$(var.BinX64Dir)\modules\HtmlAgilityPack.dll" />
</Component>
<Component Id="Module_PowerPreview_PerUserRegistry" Guid="CD90ADC0-7CD5-4A62-B0AF-23545C1E6DD3" Win64="yes">
<!-- Added a separate component for Per-User registry changes -->
<!-- Registry Key for Class Registration of Svg Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
<RegistryValue Type="string" Value="SvgPreviewHandler.SvgPreviewHandler" />
<RegistryValue Type="string" Name="DisplayName" Value="Svg Preview Handler" />
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value=""/>
<RegistryValue Type="string" Key="InprocServer32" Value="mscoree.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="SvgPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="SvgPreviewHandler.SvgPreviewHandler" />
<RegistryValue Type="string" Key="InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
<RegistryValue Type="string" Key="InprocServer32" Name="CodeBase" Value="file:///[#Svg_PreviewHandler_Dll]" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="SvgPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="SvgPreviewHandler.SvgPreviewHandler" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[#Svg_PreviewHandler_Dll]" />
</RegistryKey>
<!-- Registry Key for Class Registration of Markdown Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
<RegistryValue Type="string" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
<RegistryValue Type="string" Name="DisplayName" Value="Markdown Preview Handler" />
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="mscoree.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="MarkdownPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
<RegistryValue Type="string" Key="InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
<RegistryValue Type="string" Key="InprocServer32" Name="CodeBase" Value="file:///[#Md_PreviewHandler_Dll]" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="MarkdownPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[#Md_PreviewHandler_Dll]" />
</RegistryKey>
<!-- Registry Key for AppID registration -->
<RegistryKey Root="HKCU" Key="Software\Classes\AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
<RegistryValue Type="expandable" Name="DllSurrogate" Value="%SystemRoot%\system32\prevhost.exe" />
</RegistryKey>
<!-- Add Svg preview handler to preview handlers list -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryValue Type="string" Name="{ddee2b8a-6807-48a6-bb20-2338174ff779}" Value="Svg Preview Handler" />
</RegistryKey>
<!-- Add Markdown preview handler to preview handlers list -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryValue Type="string" Name="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" Value="Markdown Preview Handler" />
</RegistryKey>
<!-- Add file type association for Svg Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\.svg\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
</RegistryKey>
<!-- Add file type association for Markdown Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\.md\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
</RegistryKey>
<!-- Update Key to use IE11 for prevhost.exe -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
</RegistryKey>
</Component>
</DirectoryRef>
<DirectoryRef Id="SettingsHtmlInstallFolder" FileSource="$(var.RepoDir)\settings\settings-html\">
<Component Id="settings_html" Guid="87881A99-E917-4B0D-B1D8-5C6EB9709F96" Win64="yes">
@ -323,6 +397,8 @@
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />
<ComponentRef Id="Module_PowerPreview" />
<ComponentRef Id="Module_PowerPreview_PerUserRegistry" />
<ComponentRef Id="settings_exe" />
<ComponentRef Id="settings_html" />
<ComponentRef Id="settings_dark_html" />

View file

@ -433,7 +433,24 @@ namespace UnitTestsCommonLib
compareJsons(expected, actual);
}
TEST_METHOD (SettingsAddStringMultiline)
TEST_METHOD(SettingsAddLargeHeader)
{
const auto value = L"large header sample text ";
Settings settings(nullptr, m_moduleName);
settings.add_header_szLarge(m_defaultSettingsName, m_defaultSettingsDescription, value);
auto expected = m_defaultSettingsJson;
auto expectedProperties = createSettingsProperties(L"header_large");
expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
const auto actual = json::JsonObject::Parse(settings.serialize());
compareJsons(expected, actual);
}
TEST_METHOD(SettingsAddStringMultiline)
{
const auto value = L"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nsunt in culpa qui officia deserunt mollit anim id est laborum.";

View file

@ -109,6 +109,17 @@ namespace PowerToysSettings
m_json.GetNamedObject(L"properties").SetNamedValue(name, ml_string);
}
void Settings::add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value)
{
json::JsonObject string;
string.SetNamedValue(L"display_name", json::value(description));
string.SetNamedValue(L"editor_type", json::value(L"header_large"));
string.SetNamedValue(L"value", json::value(value));
string.SetNamedValue(L"order", json::value(++m_curr_priority));
m_json.GetNamedObject(L"properties").SetNamedValue(name, string);
}
// add_color_picker overloads.
void Settings::add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value)
{

View file

@ -50,6 +50,7 @@ namespace PowerToysSettings
void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value);
void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value);
void add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value);
// Serialize the internal json to a string.
std::wstring serialize();
// Serialize the internal json to the input buffer.

View file

@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using Markdig;
using Markdig.Extensions.Figures;
using Markdig.Extensions.Tables;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace MarkdownPreviewHandler
{
/// <summary>
/// Callback if extension blocks external images.
/// </summary>
public delegate void ImagesBlockedCallBack();
/// <summary>
/// Markdig Extension to process html nodes in markdown AST.
/// </summary>
public class HTMLParsingExtension : IMarkdownExtension
{
/// <summary>
/// Callback if extension blocks external images.
/// </summary>
private readonly ImagesBlockedCallBack imagesBlockedCallBack;
/// <summary>
/// Initializes a new instance of the <see cref="HTMLParsingExtension"/> class.
/// </summary>
/// <param name="imagesBlockedCallBack">Callback function if image is blocked by extension.</param>
/// <param name="baseUrl">Absolute path of markdown file.</param>
public HTMLParsingExtension(ImagesBlockedCallBack imagesBlockedCallBack, string baseUrl = "")
{
this.imagesBlockedCallBack = imagesBlockedCallBack;
this.BaseUrl = baseUrl;
}
/// <summary>
/// Gets or sets path to directory containing markdown file.
/// </summary>
public string BaseUrl { get; set; }
/// <inheritdoc/>
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Make sure we don't have a delegate twice
pipeline.DocumentProcessed -= this.PipelineOnDocumentProcessed;
pipeline.DocumentProcessed += this.PipelineOnDocumentProcessed;
}
/// <inheritdoc/>
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process nodes in markdown AST.
/// </summary>
/// <param name="document">Markdown Document.</param>
public void PipelineOnDocumentProcessed(MarkdownDocument document)
{
foreach (var node in document.Descendants())
{
if (node is Block)
{
if (node is Table)
{
node.GetAttributes().AddClass("table table-striped table-bordered");
}
else if (node is QuoteBlock)
{
node.GetAttributes().AddClass("blockquote");
}
else if (node is Figure)
{
node.GetAttributes().AddClass("figure");
}
else if (node is FigureCaption)
{
node.GetAttributes().AddClass("figure-caption");
}
}
else if (node is Inline)
{
if (node is LinkInline link)
{
if (link.IsImage)
{
link.GetAttributes().AddClass("img-fluid");
}
if (!Uri.TryCreate(link.Url, UriKind.Absolute, out _))
{
link.Url = link.Url.TrimStart('/', '\\');
this.BaseUrl = this.BaseUrl.TrimEnd('/', '\\');
Uri uriLink = new Uri(Path.Combine(this.BaseUrl, link.Url));
link.Url = uriLink.ToString();
}
else
{
if (link.IsImage)
{
link.Url = "#";
this.imagesBlockedCallBack();
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>MarkdownPreviewHandler</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MarkdownPreviewHandler</RootNamespace>
<AssemblyName>MarkdownPreviewHandler</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\MarkdownPreviewPaneDocumentation.xml</DocumentationFile>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\MarkdownPreviewPaneDocumentation.xml</DocumentationFile>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="HTMLParsingExtension.cs" />
<Compile Include="MarkdownPreviewHandler.cs" />
<Compile Include="MarkdownPreviewHandlerControl.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="MarkdownTelemetry.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack">
<Version>1.8.10</Version>
</PackageReference>
<PackageReference Include="Markdig.Signed">
<Version>0.18.0</Version>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj">
<Project>{af2349b8-e5b6-4004-9502-687c1c7730b1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Common;
namespace MarkdownPreviewHandler
{
/// <summary>
/// Implementation of preview handler for markdown files.
/// </summary>
[Guid("45769bcc-e8fd-42d0-947e-02beef77a1f5")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class MarkdownPreviewHandler : FileBasedPreviewHandler
{
private MarkdownPreviewHandlerControl markdownPreviewHandlerControl;
/// <inheritdoc />
public override void DoPreview()
{
this.markdownPreviewHandlerControl.DoPreview(this.FilePath);
}
/// <inheritdoc />
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
this.markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
return this.markdownPreviewHandlerControl;
}
}
}

View file

@ -0,0 +1,179 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Common;
using Markdig;
using MarkdownPreviewHandler.Properties;
using PreviewHandlerCommon;
namespace MarkdownPreviewHandler
{
/// <summary>
/// Win Form Implementation for Markdown Preview Handler.
/// </summary>
public class MarkdownPreviewHandlerControl : FormHandlerControl
{
/// <summary>
/// Extension to modify markdown AST.
/// </summary>
private readonly HTMLParsingExtension extension;
/// <summary>
/// Markdig Pipeline builder.
/// </summary>
private readonly MarkdownPipelineBuilder pipelineBuilder;
/// <summary>
/// Markdown HTML header.
/// </summary>
private readonly string htmlHeader = "<!doctype html><style>body{width:100%;margin:0;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,\"Noto Sans\",sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}.container{padding:5%}body img{max-width:100%;height:auto}body h1,body h2,body h3,body h4,body h5,body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}body h1,body h2{padding-bottom:.3em;border-bottom:1px solid #eaecef}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji}body h3{font-size:1.25em}body h4{font-size:1em}body h5{font-size:.875em}body h6{font-size:.85em;color:#6a737d}pre{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;background-color:#f6f8fa;border-radius:3px;padding:16px;font-size:85%}a{color:#0366d6}strong{font-weight:600}em{font-style:italic}code{padding:.2em .4em;margin:0;font-size:85%;background-color:#f6f8fa;border-radius:3px}hr{border-color:#EEE -moz-use-text-color #FFF;border-style:solid none;border-width:.5px 0;margin:18px 0}table{display:block;width:100%;overflow:auto;border-spacing:0;border-collapse:collapse}tbody{display:table-row-group;vertical-align:middle;border-color:inherit;display:table-row;vertical-align:inherit;border-color:inherit}table tr{background-color:#fff;border-top:1px solid #c6cbd1}tr{display:table-row;vertical-align:inherit;border-color:inherit}table td,table th{padding:6px 13px;border:1px solid #dfe2e5}th{font-weight:600;display:table-cell;vertical-align:inherit;font-weight:bold;text-align:-internal-center}thead{display:table-header-group;vertical-align:middle;border-color:inherit}td{display:table-cell;vertical-align:inherit}code,pre,tt{font-family:SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;color:#24292e;overflow-x:auto}pre code{font-size:inherit;color:inherit;word-break:normal}blockquote{background-color:#fff;border-radius:3px;padding:15px;font-size:14px;display:block;margin-block-start:1em;margin-block-end:1em;margin-inline-start:40px;margin-inline-end:40px;padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}</style><body><div class=\"container\">";
/// <summary>
/// Markdown HTML footer.
/// </summary>
private readonly string htmlFooter = "</div></body></html>";
/// <summary>
/// RichTextBox control to display if external images are blocked.
/// </summary>
private RichTextBox infoBar;
/// <summary>
/// Extended Browser Control to display markdown html.
/// </summary>
private WebBrowserExt browser;
/// <summary>
/// True if external image is blocked, false otherwise.
/// </summary>
private bool infoBarDisplayed = false;
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPreviewHandlerControl"/> class.
/// </summary>
public MarkdownPreviewHandlerControl()
{
this.extension = new HTMLParsingExtension(this.ImagesBlockedCallBack);
this.pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley();
this.pipelineBuilder.Extensions.Add(this.extension);
}
/// <summary>
/// Start the preview on the Control.
/// </summary>
/// <param name="dataSource">Path to the file.</param>
public override void DoPreview<T>(T dataSource)
{
this.InvokeOnControlThread(() =>
{
try
{
this.infoBarDisplayed = false;
StringBuilder sb = new StringBuilder();
string filePath = dataSource as string;
string fileText = File.ReadAllText(filePath);
this.extension.BaseUrl = Path.GetDirectoryName(filePath);
MarkdownPipeline pipeline = this.pipelineBuilder.Build();
string parsedMarkdown = Markdown.ToHtml(fileText, pipeline);
sb.AppendFormat("{0}{1}{2}", this.htmlHeader, parsedMarkdown, this.htmlFooter);
string markdownHTML = sb.ToString();
this.browser = new WebBrowserExt
{
DocumentText = markdownHTML,
Dock = DockStyle.Fill,
IsWebBrowserContextMenuEnabled = false,
ScriptErrorsSuppressed = true,
ScrollBarsEnabled = true,
AllowNavigation = false,
};
this.Controls.Add(this.browser);
if (this.infoBarDisplayed)
{
this.infoBar = this.GetTextBoxControl(Resources.BlockedImageInfoText);
this.Controls.Add(this.infoBar);
}
this.Resize += this.FormResized;
base.DoPreview(dataSource);
MarkdownTelemetry.Log.MarkdownFilePreviewed();
}
catch (Exception e)
{
MarkdownTelemetry.Log.MarkdownFilePreviewError(e.Message);
this.infoBarDisplayed = true;
this.infoBar = this.GetTextBoxControl(Resources.MarkdownNotPreviewedError);
this.Resize += this.FormResized;
this.Controls.Clear();
this.Controls.Add(this.infoBar);
base.DoPreview(dataSource);
}
});
}
/// <summary>
/// Gets a textbox control.
/// </summary>
/// <param name="message">Message to be displayed in textbox.</param>
/// <returns>An object of type <see cref="RichTextBox"/>.</returns>
private RichTextBox GetTextBoxControl(string message)
{
RichTextBox richTextBox = new RichTextBox
{
Text = message,
BackColor = Color.LightYellow,
Multiline = true,
Dock = DockStyle.Top,
ReadOnly = true,
};
richTextBox.ContentsResized += this.RTBContentsResized;
richTextBox.ScrollBars = RichTextBoxScrollBars.None;
richTextBox.BorderStyle = BorderStyle.None;
return richTextBox;
}
/// <summary>
/// Callback when RichTextBox is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the resize event.</param>
private void RTBContentsResized(object sender, ContentsResizedEventArgs e)
{
RichTextBox richTextBox = (RichTextBox)sender;
richTextBox.Height = e.NewRectangle.Height + 5;
}
/// <summary>
/// Callback when form is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the event.</param>
private void FormResized(object sender, EventArgs e)
{
if (this.infoBarDisplayed)
{
this.infoBar.Width = this.Width;
}
}
/// <summary>
/// Callback when image is blocked by extension.
/// </summary>
private void ImagesBlockedCallBack()
{
this.infoBarDisplayed = true;
}
}
}

View file

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using PreviewHandlerCommon.Telemetry;
namespace MarkdownPreviewHandler
{
/// <summary>
/// Telemetry helper class for markdown renderer.
/// </summary>
public class MarkdownTelemetry : TelemetryBase
{
/// <summary>
/// Name for ETW event.
/// </summary>
private const string EventSourceName = "Microsoft.PowerToys";
/// <summary>
/// ETW event name when markdown is previewed.
/// </summary>
private const string MarkdownFilePreviewedEventName = "PowerPreview_MDRenderer_Previewed";
/// <summary>
/// ETW event name when error is thrown during markdown preview.
/// </summary>
private const string MarkdownFilePreviewErrorEventName = "PowerPreview_MDRenderer_Error";
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownTelemetry"/> class.
/// </summary>
public MarkdownTelemetry()
: base(EventSourceName)
{
return;
}
/// <summary>
/// Gets an instance of the <see cref="MarkdownTelemetry"/> class.
/// </summary>
public static MarkdownTelemetry Log { get; } = new MarkdownTelemetry();
/// <summary>
/// Publishes ETW event when markdown is previewed successfully.
/// </summary>
public void MarkdownFilePreviewed()
{
this.Write(MarkdownFilePreviewedEventName, new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,
Tags = ProjectTelemetryTagProductAndServicePerformance,
});
}
/// <summary>
/// Publishes ETW event when markdown could not be previewed.
/// </summary>
public void MarkdownFilePreviewError(string message)
{
this.Write(
MarkdownFilePreviewErrorEventName,
new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,
Tags = ProjectTelemetryTagProductAndServicePerformance,
},
new { Message = message, });
}
}
}

View file

@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MarkdownPreviewHandler.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkdownPreviewHandler.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Some pictures have been blocked to help prevent the sender from identifying this computer. Open this item to view pictures..
/// </summary>
internal static string BlockedImageInfoText {
get {
return ResourceManager.GetString("BlockedImageInfoText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The markdown could not be preview due to an internal error..
/// </summary>
internal static string MarkdownNotPreviewedError {
get {
return ResourceManager.GetString("MarkdownNotPreviewedError", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BlockedImageInfoText" xml:space="preserve">
<value>Some pictures have been blocked to help prevent the sender from identifying this computer. Open this item to view pictures.</value>
<comment>This text is displayed if image is blocked from being displayed.</comment>
</data>
<data name="MarkdownNotPreviewedError" xml:space="preserve">
<value>The markdown could not be preview due to an internal error.</value>
<comment>This text is displayed if markdown fails to preview</comment>
</data>
</root>

View file

@ -0,0 +1,128 @@
using System;
using Markdig;
using MarkdownPreviewHandler;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PreviewPaneUnitTests
{
[TestClass]
public class HTMLParsingExtensionTest
{
private MarkdownPipeline BuidPipeline(IMarkdownExtension extension)
{
MarkdownPipelineBuilder pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions();
pipelineBuilder.Extensions.Add(extension);
return pipelineBuilder.Build();
}
[TestMethod]
public void Extension_UpdatesTablesClass_WhenUsed()
{
// Arrange
String mdString = "| A | B |\n| -- | -- | ";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { });
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<table class=\"table table-striped table-bordered\">\n<thead>\n<tr>\n<th>A</th>\n<th>B</th>\n</tr>\n</thead>\n</table>\n");
}
[TestMethod]
public void Extension_UpdatesBlockQuotesClass_WhenUsed()
{
// Arrange
String mdString = "> Blockquotes.";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(()=> { });
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<blockquote class=\"blockquote\">\n<p>Blockquotes.</p>\n</blockquote>\n");
}
[TestMethod]
public void Extension_UpdatesFigureClassAndRelativeUrltoAbsolute_WhenUsed()
{
// arrange
String mdString = "![text](a.jpg \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:\\Users\\");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
[TestMethod]
public void Extension_CreatesCorrectAbsoluteLinkByTrimmingForwardSlash_WhenUsed()
{
// arrange
String mdString = "![text](\\document\\a.jpg \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:\\Users\\");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/document/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
[TestMethod]
public void Extension_CreatesCorrectAbsoluteLinkByTrimmingBackwardSlash_WhenUsed()
{
// arrange
String mdString = "![text](/document/a.jpg \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:/Users/");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/document/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
[TestMethod]
public void Extension_AddsClassToFigureCaption_WhenUsed()
{
// arrange
String mdString = "^^^ This is a caption";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:/Users/");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<figure class=\"figure\">\n<figcaption class=\"figure-caption\">This is a caption</figcaption>\n</figure>\n");
}
[TestMethod]
public void Extension_RemovesExternalImageUrlAndMakeCallback_WhenUsed()
{
// arrange
int count = 0;
String mdString = "![text](http://dev.nodeca.com \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { count++; });
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(count, 1);
Assert.AreEqual(html, "<p><img src=\"#\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
}
}

View file

@ -0,0 +1,2 @@
![Minion](https://octodex.github.com/images/minion.png)
<script>alert("hello")</script>

View file

@ -0,0 +1 @@
<script>alert("hello")</script>

View file

@ -0,0 +1,96 @@
using System;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml.Linq;
using Markdig;
using MarkdownPreviewHandler;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PreviewHandlerCommon;
namespace PreviewPaneUnitTests
{
[TestClass]
public class MarkdownPreviewHandlerTest
{
[TestMethod]
public void MarkdownPreviewHandlerControl__AddsBrowserToForm_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
// Assert
Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 2);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
}
[TestMethod]
public void MarkdownPreviewHandlerControl__AddsInfoBarToFormIfExternalImageLinkPresent_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
// Assert
Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 2);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
}
[TestMethod]
public void MarkdownPreviewHandlerControl__DoesNotAddInfoBarToFormIfExternalImageLinkNotPresent_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithScript.txt");
// Assert
Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 1);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
}
[TestMethod]
public void MarkdownPreviewHandlerControl__UpdatesWebBrowserSettings_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
// Assert
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
Assert.IsNotNull(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).DocumentText);
Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).Dock, DockStyle.Fill);
Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).IsWebBrowserContextMenuEnabled, false);
Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScriptErrorsSuppressed, true);
Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScrollBarsEnabled, true);
Assert.AreEqual(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).AllowNavigation, false);
}
[TestMethod]
public void MarkdownPreviewHandlerControl__UpdateInfobarSettings_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
// Assert
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
Assert.IsNotNull(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Text);
Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Dock, DockStyle.Top);
Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).BorderStyle, BorderStyle.None);
Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).BackColor, Color.LightYellow);
Assert.AreEqual(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Multiline, true);
}
}
}

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>PreviewPaneUnitTests</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PreviewPaneUnitTests</RootNamespace>
<AssemblyName>PreviewPaneUnitTests</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="HTMLParsingExtensionTest.cs" />
<Compile Include="MarkdownPreviewHandlerTest.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Markdig.Signed">
<Version>0.18.0</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.3.2</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.3.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj">
<Project>{AF2349B8-E5B6-4004-9502-687C1C7730B1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
<ProjectReference Include="..\MarkDownPreviewHandler\MarkdownPreviewHandler.csproj">
<Project>{6a71162e-fc4c-4a2c-b90f-3cf94f59a9bb}</Project>
<Name>MarkdownPreviewHandler</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="HelperFiles\MarkdownWithExternalImage.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="HelperFiles\MarkdownWithscript.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,174 @@
<center>
<img width="200" src="../../../doc/images/Logo.jpg">
# PowerPreview
> Adding Custom Preview Handlers to Windows File Explorer Preview Pane.
[**Overview**](#overview) ·
[**Developing**](#Developing) ·
[**MSIX Integration**](#Install-With-MSIX) ·
[**Contributing »**](#Contributing)
</center>
## Overview
Preview handlers are called when an item is selected to show a lightweight, rich, read-only preview of the file's contents in the view's reading pane. This is done without launching the file's associated application. Figure 1 shows an example of a preview handler that preview a .md file type. Please follow this [documentation](https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/windows-vista-and-office-writing-your-own-preview-handlers) to start developing a preview handler, when done, continue with this documentation to learn how to integrate a preview handler into PowerToys.
<center>
<figure>
<img src="../../../doc/images/preview_pane/markdown.gif" alt="Mark Down Preview Handler Demo" >
<figcaption>Figure 1 : Mark Down Preview Handler Demo</figcaption>
</figure>
</center>
## Developing
We have already done most of the development work in the [PreviewHandlerCommon](./common/cominterop/IPreviewHandler.cs) common project. To add a preview for the file type of .xyz:
- Add a new .NET project in the preview pane folder.
- Add a reference to the `PreviewHandlerCommon` common project.
- Create your preview handler class and extend the FileBasedPreviewHandler class. See an example below:
```csharp
using System;
using System.Runtime.InteropServices;
using Common;
namespace XYZPreviewHandler
{
/// <summary>
/// Implementation of preview handler for .xyz files.
/// GUID = CLSID / CLASS ID.
/// </summary>
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class XYZPreviewHandler : FileBasedPreviewHandler
{
private XYZPreviewHandlerControl xyzPreviewHandlerControl;
/// Call your rendering method here.
public override void DoPreview()
{
this.xyzPreviewHandlerControl.DoPreview(this.FilePath);
}
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
this.xyzPreviewHandlerControl = new xyzPreviewHandlerControl();
return this.xyzPreviewHandlerControl;
}
}
}
```
Create a separate Preview Handler Control class and extend the `FormHandlerControl` Class.
```csharp
using Common;
namespace XYZPreviewHandler
{
public class XYZPreviewHandlerControl : FormHandlerControl
{
public XYZPreviewHandlerControl()
{
// ... do your initializations here.
}
public override void DoPreview<T>(T dataSource)
{
// ... add your preview rendering code here.
}
}
}
```
#### Integrate the Preview Handler into PowerToys Settings:
Navigate to the [powerpreview](../previewpane/powerpreview/powerpreview.h) project and edit the `powerpreview.h` file. Add the following Settings Object instance to `m_previewHandlers` settings objects array in the constructor initialization:
```cpp
// XYZ Preview Handler Settings Object.
FileExplorerPreviewSettings(
false,
L"<--YOUR_TOGGLE_CONTROL_ID-->",
L"<--A description of your preview handler-->",
L"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx", // your preview handler CLSID.
L"<--A display name for your preview handler-->")
```
## Install With MSIX
<---To Do--->
```xml
```
## Managing Preview Handlers
After successful integration, your preview handler should appear in the PowerToys settings UI under the `File Explorer Preview` Tab as shown in Figure 2 below. In here you should be able to enable and disable your preview handler.
<center>
<figure>
<img src="../../../doc/images/preview_pane/settings-ui.png" alt="Settings UI - File Explorer Preview Tab" >
<figcaption>Figure 2 : Settings UI - File Explorer Preview Tab</figcaption>
</figure>
</center>
In the general settings of the Settings UI, you should be able to disable and enable all the preview handlers all at once. See Figure 3.
<center>
<figure>
<img src="../../../doc/images/preview_pane/general-settings.png" alt="Settings UI - General Settings Tab" >
<figcaption>Figure 3 : Settings UI - General Settings Tab</figcaption>
</figure>
</center>
## Contributing
### Coding Guidance
#### Working With Strings
**YOU SHOULD NOT** have hardcoded strings in your C++ code. Instead, use the following guidelines to add strings to your code. Add the ID of your string to the resource file. XXX must be a unique int in the list (mostly the int ID of the last string id plus one):
- `resource.h`:
```cpp
#define IDS_PREVPANE_XYZ_SETTINGS_DISPLAYNAME XXX
```
- `powerpreview.rc` under strings table:
```cpp
IDS_PREVPANE_XYZ_SETTINGS_DISPLAYNAME L"XYZ Preview Handler"
```
- Use the `GET_RESOURCE_STRING(UINT resource_id)` method to consume strings in your code.
```cpp
#include <common.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
std::wstring GET_RESOURCE_STRING(IDS_PREVPANE_XYZ_SETTINGS_DISPLAYNAME)
```
#### More On Coding Guidance
Please review these brief docs below relating to our coding standards etc.
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
* [Coding Style](../../../doc/devdocs/style.md)
* [Code Organization](../../../doc/devdocs/readme.md)

View file

@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SvgPreviewHandler {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SvgPreviewHandler.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Some elements have been blocked to help prevent the sender from identifying your computer. Open this item to view all elements..
/// </summary>
internal static string BlockedElementInfoText {
get {
return ResourceManager.GetString("BlockedElementInfoText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The Svg could not be preview due to an internal error in Svg Preview Handler..
/// </summary>
internal static string SvgNotPreviewedError {
get {
return ResourceManager.GetString("SvgNotPreviewedError", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BlockedElementInfoText" xml:space="preserve">
<value>Some elements have been blocked to help prevent the sender from identifying your computer. Open this item to view all elements.</value>
</data>
<data name="SvgNotPreviewedError" xml:space="preserve">
<value>The Svg could not be preview due to an internal error in Svg Preview Handler.</value>
</data>
</root>

View file

@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Versioning;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using Common;
using Common.Utilities;
using PreviewHandlerCommon;
namespace SvgPreviewHandler
{
/// <summary>
/// Implementation of Control for Svg Preview Handler.
/// </summary>
public class SvgPreviewControl : FormHandlerControl
{
/// <summary>
/// Extended Browser Control to display Svg.
/// </summary>
private WebBrowserExt browser;
/// <summary>
/// Text box to display the information about blocked elements from Svg.
/// </summary>
private RichTextBox textBox;
/// <summary>
/// Represent if an text box info bar is added for showing message.
/// </summary>
private bool infoBarAdded = false;
/// <summary>
/// Start the preview on the Control.
/// </summary>
/// <param name="dataSource">Stream reference to access source file.</param>
public override void DoPreview<T>(T dataSource)
{
this.InvokeOnControlThread(() =>
{
try
{
this.infoBarAdded = false;
string svgData = null;
using (var stream = new StreamWrapper(dataSource as IStream))
{
using (var reader = new StreamReader(stream))
{
svgData = reader.ReadToEnd();
}
}
this.AddBrowserControl(svgData);
this.Resize += this.FormResized;
base.DoPreview(dataSource);
SvgTelemetry.Log.SvgFilePreviewed();
}
catch (Exception ex)
{
SvgTelemetry.Log.SvgFilePreviewError(ex.Message);
this.Controls.Clear();
this.infoBarAdded = true;
this.AddTextBoxControl(Resource.SvgNotPreviewedError);
base.DoPreview(dataSource);
}
});
}
/// <summary>
/// Occurs when RichtextBox is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the ContentsResized event.</param>
private void RTBContentsResized(object sender, ContentsResizedEventArgs e)
{
var richTextBox = sender as RichTextBox;
richTextBox.Height = e.NewRectangle.Height + 5;
}
/// <summary>
/// Occurs when form is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the resize event.</param>
private void FormResized(object sender, EventArgs e)
{
if (this.infoBarAdded)
{
this.textBox.Width = this.Width;
}
}
/// <summary>
/// Adds a Web Browser Control to Control Collection.
/// </summary>
/// <param name="svgData">Svg to display on Browser Control.</param>
private void AddBrowserControl(string svgData)
{
this.browser = new WebBrowserExt();
this.browser.DocumentText = svgData;
this.browser.Dock = DockStyle.Fill;
this.browser.IsWebBrowserContextMenuEnabled = false;
this.browser.ScriptErrorsSuppressed = true;
this.browser.ScrollBarsEnabled = true;
this.browser.AllowNavigation = false;
this.Controls.Add(this.browser);
}
/// <summary>
/// Adds a Text Box in Controls for showing information about blocked elements.
/// </summary>
/// <param name="message">Message to be displayed in textbox.</param>
private void AddTextBoxControl(string message)
{
this.textBox = new RichTextBox();
this.textBox.Text = message;
this.textBox.BackColor = Color.LightYellow;
this.textBox.Multiline = true;
this.textBox.Dock = DockStyle.Top;
this.textBox.ReadOnly = true;
this.textBox.ContentsResized += this.RTBContentsResized;
this.textBox.ScrollBars = RichTextBoxScrollBars.None;
this.textBox.BorderStyle = BorderStyle.None;
this.Controls.Add(this.textBox);
}
}
}

View file

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Common;
namespace SvgPreviewHandler
{
/// <summary>
/// Extends <see cref="StreamBasedPreviewHandler"/> for Svg Preview Handler.
/// </summary>
[Guid("ddee2b8a-6807-48a6-bb20-2338174ff779")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class SvgPreviewHandler : StreamBasedPreviewHandler
{
private SvgPreviewControl svgPreviewControl;
/// <inheritdoc/>
public override void DoPreview()
{
this.svgPreviewControl.DoPreview(this.Stream);
}
/// <inheritdoc/>
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
this.svgPreviewControl = new SvgPreviewControl();
return this.svgPreviewControl;
}
}
}

View file

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>SvgPreviewHandler</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DA425894-6E13-404F-8DCB-78584EC0557A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SvgPreviewHandler</RootNamespace>
<AssemblyName>SvgPreviewHandler</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\SvgPreviewHandler.xml</DocumentationFile>
<WarningLevel>2</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\SvgPreviewHandler.xml</DocumentationFile>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Resource.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Include="SvgPreviewControl.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="SvgPreviewHandler.cs" />
<Compile Include="SvgTelemetry.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj">
<Project>{af2349b8-e5b6-4004-9502-687c1c7730b1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using PreviewHandlerCommon.Telemetry;
namespace SvgPreviewHandler
{
/// <summary>
/// Telemetry helper class for Svg renderer.
/// </summary>
public class SvgTelemetry : TelemetryBase
{
/// <summary>
/// Name for ETW event.
/// </summary>
private const string EventSourceName = "Microsoft.PowerToys";
/// <summary>
/// ETW event name when Svg is previewed.
/// </summary>
private const string SvgFilePreviewedEventName = "PowerPreview_SVGRenderer_Previewed";
/// <summary>
/// ETW event name when error is thrown during Svg preview.
/// </summary>
private const string SvgFilePreviewErrorEventName = "PowerPreview_SVGRenderer_Error";
/// <summary>
/// Initializes a new instance of the <see cref="SvgTelemetry"/> class.
/// </summary>
public SvgTelemetry()
: base(EventSourceName)
{
}
/// <summary>
/// Gets an instance of the <see cref="SvgTelemetry"/> class.
/// </summary>
public static SvgTelemetry Log { get; } = new SvgTelemetry();
/// <summary>
/// Publishes ETW event when svg is previewed successfully.
/// </summary>
public void SvgFilePreviewed()
{
this.Write(SvgFilePreviewedEventName, new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,
Tags = ProjectTelemetryTagProductAndServicePerformance,
});
}
/// <summary>
/// Publishes ETW event when svg could not be previewed.
/// </summary>
public void SvgFilePreviewError(string message)
{
this.Write(
SvgFilePreviewErrorEventName,
new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,
Tags = ProjectTelemetryTagProductAndServicePerformance,
},
new { Message = message, });
}
}
}

View file

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class FileBasedPreviewHandlerTests
{
public class TestFileBasedPreviewHandler : FileBasedPreviewHandler
{
public override void DoPreview()
{
throw new NotImplementedException();
}
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
return new Mock<IPreviewHandlerControl>().Object;
}
}
[DataTestMethod]
[DataRow((uint)0)]
[DataRow((uint)1)]
public void FileBasedPreviewHandler_ShouldSetFilePath_WhenInitializeCalled(uint grfMode)
{
// Arrange
var fileBasedPreviewHandler = new TestFileBasedPreviewHandler();
var filePath = "C:\\valid-path";
// Act
fileBasedPreviewHandler.Initialize(filePath, grfMode);
// Assert
Assert.AreEqual(fileBasedPreviewHandler.FilePath, filePath);
}
}
}

View file

@ -0,0 +1,181 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class FormHandlerControlTests
{
private class TestFormControl : FormHandlerControl
{ }
[TestMethod]
public void FormHandlerControl_ShouldCreateHandle_OnIntialization()
{
// Arrange and act
var testFormHandlerControl = new TestFormControl();
// Assert
Assert.IsTrue(testFormHandlerControl.IsHandleCreated);
}
[TestMethod]
public void FormHandlerControl_ShouldSetVisibleFalse_OnIntialization()
{
// Arrange and act
var testFormHandlerControl = new TestFormControl();
// Assert
Assert.IsFalse(testFormHandlerControl.Visible);
}
[TestMethod]
public void FormHandlerControl_ShouldSetFormBorderStyle_OnIntialization()
{
// Arrange and act
var testFormHandlerControl = new TestFormControl();
// Assert
Assert.AreEqual(FormBorderStyle.None, testFormHandlerControl.FormBorderStyle);
}
[TestMethod]
public void FormHandlerControl_ShouldReturnValidHandle_WhenGetHandleCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
// Act
var handle = testFormHandlerControl.GetHandle();
// Assert
Assert.AreEqual(testFormHandlerControl.Handle, handle);
}
[TestMethod]
public void FormHandlerControl_ShouldSetBackgroundColor_WhenSetBackgroundColorCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
var color = Color.Navy;
// Act
testFormHandlerControl.SetBackgroundColor(color);
// Assert
Assert.AreEqual(color, testFormHandlerControl.BackColor);
}
[TestMethod]
public void FormHandlerControl_ShouldSetFont_WhenSetFontCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
var font = new Font("Arial", 20);
// Act
testFormHandlerControl.SetFont(font);
// Assert
Assert.AreEqual(font, testFormHandlerControl.Font);
}
[TestMethod]
public void FormHandlerControl_ShouldUpdateBounds_WhenSetRectCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
var bounds = new Rectangle(2, 2, 4, 4);
// Act
testFormHandlerControl.SetRect(bounds);
// Assert
Assert.AreEqual(bounds, testFormHandlerControl.Bounds);
}
[TestMethod]
public void FormHandlerControl_ShouldSetTextColor_WhenSetTextColorCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
var color = Color.Navy;
// Act
testFormHandlerControl.SetTextColor(color);
// Assert
Assert.AreEqual(color, testFormHandlerControl.ForeColor);
}
[TestMethod]
public void FormHandlerControl_ShouldClearAllControls_WhenUnloadCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
testFormHandlerControl.Controls.Add(new TextBox());
testFormHandlerControl.Controls.Add(new RichTextBox());
// Act
testFormHandlerControl.Unload();
// Assert
Assert.AreEqual(0, testFormHandlerControl.Controls.Count);
}
[TestMethod]
public void FormHandlerControl_ShouldSetVisibleFalse_WhenUnloadCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
// Act
testFormHandlerControl.Unload();
// Assert
Assert.IsFalse(testFormHandlerControl.Visible);
}
[TestMethod]
public void FormHandlerControl_ShouldSetVisibletrue_WhenDoPreviewCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
// Act
testFormHandlerControl.DoPreview("valid-path");
// Assert
Assert.IsTrue(testFormHandlerControl.Visible);
}
[TestMethod]
public void FormHandlerControl_ShouldSetParentHandle_WhenSetWindowCalled()
{
// Arrange
var testFormHandlerControl = new TestFormControl();
var parentFormWindow = new UserControl();
var parentHwnd = parentFormWindow.Handle;
var rect = new Rectangle(2, 2, 4, 4);
// Act
testFormHandlerControl.SetWindow(parentHwnd, rect);
var actualParentHwnd = GetAncestor(testFormHandlerControl.Handle, 1); // GA_PARENT 1
// Assert
Assert.AreEqual(parentHwnd, actualParentHwnd);
}
// Gets the ancestor window: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getancestor
[DllImport("user32.dll")]
private static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
}
}

View file

@ -0,0 +1,391 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Common;
using Common.ComInterlop;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class PreviewHandlerBaseTests
{
private static IPreviewHandlerControl previewHandlerControl;
public class TestPreviewHandler : PreviewHandlerBase
{
public override void DoPreview()
{
throw new NotImplementedException();
}
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
return GetMockPreviewHandlerControl();
}
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetWindow_WhenSetWindowCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
var handle = new IntPtr(5);
var bounds = GetRectangle(2, 2, 4, 4);
var actualHandle = IntPtr.Zero;
var actualBounds = Rectangle.Empty;
mockPreviewControl
.Setup(_ => _.SetWindow(It.IsAny<IntPtr>(), It.IsAny<Rectangle>()))
.Callback<IntPtr, Rectangle>((hwnd, rect) =>
{
actualHandle = hwnd;
actualBounds = rect;
});
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
testPreviewHandler.SetWindow(handle, ref bounds);
// Assert
Assert.AreEqual(actualHandle, handle);
Assert.AreEqual(actualBounds, bounds.ToRectangle());
mockPreviewControl.Verify(_ => _.SetWindow(It.IsAny<IntPtr>(), It.IsAny<Rectangle>()), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetrect_WhenSetRectCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
var bounds = GetRectangle(2, 2, 4, 4);
var actualBounds = Rectangle.Empty;
mockPreviewControl
.Setup(_ => _.SetRect(It.IsAny<Rectangle>()))
.Callback<Rectangle>((rect) =>
{
actualBounds = rect;
});
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
testPreviewHandler.SetRect(ref bounds);
// Assert
Assert.AreEqual(actualBounds, bounds.ToRectangle());
mockPreviewControl.Verify(_ => _.SetRect(It.IsAny<Rectangle>()), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlUnload_WhenUnloadCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
testPreviewHandler.Unload();
// Assert
mockPreviewControl.Verify(_ => _.Unload(), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetBackgroundColor_WhenSetBackgroundColorCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
var color = new COLORREF();
// Act
testPreviewHandler.SetBackgroundColor(color);
// Assert
mockPreviewControl.Verify(_ => _.SetBackgroundColor(It.Is<Color>(c => (c == color.Color))), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetTextColor_WhenSetTextColorCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
var color = new COLORREF();
// Act
testPreviewHandler.SetTextColor(color);
// Assert
mockPreviewControl.Verify(_ => _.SetTextColor(It.Is<Color>(c => (c == color.Color))), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetFont_WhenSetFontCalled()
{
// Arrange
Font actualFont = null;
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
mockPreviewControl
.Setup(x => x.SetFont(It.IsAny<Font>()))
.Callback<Font>((font) =>
{
actualFont = font;
});
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
var logFont = GetLogFont();
// Act
testPreviewHandler.SetFont(ref logFont);
// Assert
mockPreviewControl.Verify(_ => _.SetFont(It.IsAny<Font>()), Times.Once);
Assert.AreEqual(Font.FromLogFont(logFont), actualFont);
}
[TestMethod]
public void PreviewHandlerBase_ShouldCallPreviewControlSetFocus_WhenSetFocusCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
testPreviewHandler.SetFocus();
// Assert
mockPreviewControl.Verify(_ => _.SetFocus(), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldSetHandleOnQueryFocus_WhenPreviewControlsReturnValidHandle()
{
// Arrange
var hwnd = new IntPtr(5);
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
mockPreviewControl.Setup(x => x.QueryFocus(out hwnd));
var actualHwnd = IntPtr.Zero;
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
testPreviewHandler.QueryFocus(out actualHwnd);
// Assert
Assert.AreEqual(actualHwnd, hwnd);
mockPreviewControl.Verify(_ => _.QueryFocus(out hwnd), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldThrowOnQueryFocus_WhenPreviewControlsReturnNotValidHandle()
{
// Arrange
var hwnd = IntPtr.Zero;
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
mockPreviewControl.Setup(x => x.QueryFocus(out hwnd));
var actualHwnd = IntPtr.Zero;
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
Win32Exception exception = null;
// Act
try
{
testPreviewHandler.QueryFocus(out actualHwnd);
}
catch (Win32Exception ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
mockPreviewControl.Verify(_ => _.QueryFocus(out hwnd), Times.Once);
}
[TestMethod]
public void PreviewHandlerBase_ShouldDirectKeyStrokesToIPreviewHandlerFrame_IfIPreviewHandlerFrameIsSet()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
var mockPreviewHandlerFrame = new Mock<IPreviewHandlerFrame>();
var msg = new MSG();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
testPreviewHandler.SetSite(mockPreviewHandlerFrame.Object);
// Act
testPreviewHandler.TranslateAccelerator(ref msg);
// Assert
mockPreviewHandlerFrame.Verify(_ => _.TranslateAccelerator(ref msg), Times.Once);
}
[DataTestMethod]
[DataRow((uint)0)]
[DataRow((uint)1)]
public void PreviewHandlerBase_ShouldReturnIPreviewHandlerFrameResponse_IfIPreviewHandlerFrameIsSet(uint resultCode)
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
var mockPreviewHandlerFrame = new Mock<IPreviewHandlerFrame>();
var msg = new MSG();
mockPreviewHandlerFrame
.Setup(x => x.TranslateAccelerator(ref msg))
.Returns(resultCode);
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
testPreviewHandler.SetSite(mockPreviewHandlerFrame.Object);
// Act
var actualResultCode = testPreviewHandler.TranslateAccelerator(ref msg);
// Assert
Assert.AreEqual(resultCode, actualResultCode);
}
[TestMethod]
public void PreviewHandlerBase_ShouldReturnS_FALSE_IfIPreviewHandlerFrameIsNotSet()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
var msg = new MSG();
uint S_FALSE = 1;
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
// Act
var result = testPreviewHandler.TranslateAccelerator(ref msg);
// Assert
Assert.AreEqual(result, S_FALSE);
}
[TestMethod]
public void PreviewHandlerBase_ShouldReturnPreviewControlHandle_IfGetWindowCalled()
{
// Arrange
var previewControlHandle = new IntPtr(5);
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
mockPreviewControl.Setup(x => x.GetHandle())
.Returns(previewControlHandle);
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
var hwndReceived = IntPtr.Zero;
// Act
testPreviewHandler.GetWindow(out hwndReceived);
// Assert
Assert.AreEqual(hwndReceived, previewControlHandle);
}
[DataTestMethod]
[DataRow(true)]
[DataRow(false)]
public void PreviewHandlerBase_ShouldThrowNotImplementedException_IfContextSensitiveHelpCalled(bool fEnterMode)
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
NotImplementedException exception = null;
// Act
try
{
testPreviewHandler.ContextSensitiveHelp(fEnterMode);
}
catch (NotImplementedException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
[TestMethod]
public void PreviewHandlerBase_ShouldReturnSite_WhenGetSiteCalled()
{
// Arrange
var mockPreviewControl = new Mock<IPreviewHandlerControl>();
previewHandlerControl = mockPreviewControl.Object;
var testPreviewHandler = new TestPreviewHandler();
var site = new Mock<IPreviewHandlerFrame>().Object;
testPreviewHandler.SetSite(site);
object actualSite = null;
var riid = Guid.Empty;
// Act
testPreviewHandler.GetSite(ref riid, out actualSite);
// Assert
Assert.AreEqual(actualSite, site);
}
private LOGFONT GetLogFont()
{
var logFont = new LOGFONT();
logFont.LfHeight = 12;
logFont.LfWidth = 0;
logFont.LfEscapement = 0;
logFont.LfWeight = 400; // FW_NORMAL
logFont.LfItalic = Convert.ToByte(false);
logFont.LfUnderline = Convert.ToByte(false);
logFont.LfStrikeOut = Convert.ToByte(0);
logFont.LfCharSet = Convert.ToByte(0); // ANSI_CHARSET
logFont.LfOutPrecision = Convert.ToByte(0); // OUT_DEFAULT_PRECIS
logFont.LfClipPrecision = Convert.ToByte(0);
logFont.LfQuality = Convert.ToByte(0);
logFont.LfPitchAndFamily = Convert.ToByte(0);
logFont.LfFaceName = "valid-font";
return logFont;
}
private RECT GetRectangle(int left, int top, int right, int bottom)
{
var rect = new RECT();
rect.Left = left;
rect.Top = top;
rect.Right = right;
rect.Bottom = bottom;
return rect;
}
private static IPreviewHandlerControl GetMockPreviewHandlerControl()
{
return previewHandlerControl;
}
}
}

View file

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices.ComTypes;
using Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class StreamBasedPreviewHandlerTests
{
public class TestStreamBasedPreviewHandler : StreamBasedPreviewHandler
{
public override void DoPreview()
{
throw new NotImplementedException();
}
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
return new Mock<IPreviewHandlerControl>().Object;
}
}
[DataTestMethod]
[DataRow((uint)0)]
[DataRow((uint)1)]
public void FileBasedPreviewHandler_ShouldSetFilePath_WhenInitializeCalled(uint grfMode)
{
// Arrange
var streamBasedPreviewHandler = new TestStreamBasedPreviewHandler();
var stream = new Mock<IStream>().Object;
// Act
streamBasedPreviewHandler.Initialize(stream, grfMode);
// Assert
Assert.AreEqual(streamBasedPreviewHandler.Stream, stream);
}
}
}

View file

@ -0,0 +1,332 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Castle.Core.Logging;
using Common.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class StreamWrapperTests
{
[TestMethod]
public void StreamWrapper_ShouldThrow_IfInitializeWithNullStream()
{
// Arrange
IStream stream = null;
ArgumentNullException exception = null;
// Act
try
{
var streamWrapper = new StreamWrapper(stream);
}
catch (ArgumentNullException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
[TestMethod]
public void StreamWrapper_ShouldReturnCanReadTrue()
{
// Arrange
var streamMock = new Mock<IStream>();
// Act
var streamWrapper = new StreamWrapper(streamMock.Object);
// Assert
Assert.AreEqual(streamWrapper.CanRead, true);
}
[TestMethod]
public void StreamWrapper_ShouldReturnCanSeekTrue()
{
// Arrange
var streamMock = new Mock<IStream>();
// Act
var streamWrapper = new StreamWrapper(streamMock.Object);
// Assert
Assert.AreEqual(streamWrapper.CanSeek, true);
}
[TestMethod]
public void StreamWrapper_ShouldReturnCanWriteFalse()
{
// Arrange
var streamMock = new Mock<IStream>();
// Act
var streamWrapper = new StreamWrapper(streamMock.Object);
// Assert
Assert.AreEqual(streamWrapper.CanWrite, false);
}
[TestMethod]
public void StreamWrapper_ShouldReturnValidLength()
{
// Arrange
long streamLength = 5;
var stremMock = new Mock<IStream>();
var stat = new System.Runtime.InteropServices.ComTypes.STATSTG();
stat.cbSize = streamLength;
stremMock
.Setup(x => x.Stat(out stat, It.IsAny<int>()));
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
var actualLength = streamWrapper.Length;
// Assert
Assert.AreEqual(actualLength, streamLength);
}
[TestMethod]
public void StreamWrapper_ShouldReturnValidPosition()
{
// Arrange
int expectedDwOrigin = 1; // STREAM_SEEK_CUR
long expectedOffset = 0;
long currPosition = 5;
var stremMock = new Mock<IStream>();
stremMock
.Setup(x => x.Seek(It.IsAny<long>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<long, int, IntPtr>((dlibMove, dwOrigin, plibNewPosition) =>
{
Marshal.WriteInt64(plibNewPosition, currPosition);
});
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
var actualPosition = streamWrapper.Position;
// Assert
Assert.AreEqual(actualPosition, currPosition);
stremMock.Verify(_ => _.Seek(It.Is<long>(offset => offset == expectedOffset), It.Is<int>(dworigin => dworigin == expectedDwOrigin), It.IsAny<IntPtr>()), Times.Once);
}
[TestMethod]
public void StreamWrapper_ShouldCallIStreamSeek_WhenSetPosition()
{
// Arrange
long positionToSet = 5;
int expectedDwOrigin = 0; // STREAM_SEEK_SET
var stremMock = new Mock<IStream>();
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
streamWrapper.Position = positionToSet;
// Assert
stremMock.Verify(_ => _.Seek(It.Is<long>(offset => offset == positionToSet), It.Is<int>(dworigin => dworigin == expectedDwOrigin), It.IsAny<IntPtr>()), Times.Once);
}
[DataTestMethod]
[DataRow((long)0, SeekOrigin.Begin)]
[DataRow((long)5, SeekOrigin.Begin)]
[DataRow((long)0, SeekOrigin.Current)]
[DataRow((long)5, SeekOrigin.Current)]
[DataRow((long)0, SeekOrigin.End)]
[DataRow((long)5, SeekOrigin.End)]
public void StreamWrapper_ShouldCallIStreamSeekWithValidArguments_WhenSeekCalled(long offset, SeekOrigin origin)
{
// Arrange
int expectedDwOrigin = 0;
switch (origin)
{
case SeekOrigin.Begin:
expectedDwOrigin = 0;
break;
case SeekOrigin.Current:
expectedDwOrigin = 1;
break;
case SeekOrigin.End:
expectedDwOrigin = 2;
break;
}
var stremMock = new Mock<IStream>();
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
streamWrapper.Seek(offset, origin);
// Assert
stremMock.Verify(_ => _.Seek(It.Is<long>(actualOffset => actualOffset == offset), It.Is<int>(actualDwOrigin => actualDwOrigin == expectedDwOrigin), It.IsAny<IntPtr>()), Times.Once);
}
[TestMethod]
public void StreamWrapper_ShouldReturnValidPosition_WhenSeekCalled()
{
// Arrange
long position = 5;
var stremMock = new Mock<IStream>();
stremMock
.Setup(x => x.Seek(It.IsAny<long>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<long, int, IntPtr>((dlibMove, dwOrigin, plibNewPosition) =>
{
Marshal.WriteInt64(plibNewPosition, position);
});
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
var actualPosition = streamWrapper.Seek(0, SeekOrigin.Begin);
// Assert
Assert.AreEqual(position, actualPosition);
}
[DataTestMethod]
[DataRow(10, -1, 5)]
[DataRow(10, 0, -5)]
[DataRow(10, 0, 11)]
[DataRow(10, 5, 6)]
public void StreamWrapper_ShouldThrow_WhenReadCalledWithOutOfRangeArguments(int bufferLength, int offSet, int bytesToRead)
{
// Arrange
var buffer = new byte[bufferLength];
var stremMock = new Mock<IStream>();
ArgumentOutOfRangeException exception = null;
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
try
{
streamWrapper.Read(buffer, offSet, bytesToRead);
}
catch (ArgumentOutOfRangeException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
[DataTestMethod]
[DataRow(5, 0)]
[DataRow(5, 5)]
[DataRow(0, 5)]
public void StreamWrapper_ShouldSetValidBuffer_WhenReadCalled(int count, int offset)
{
// Arrange
var inputBuffer = new byte[1024];
var streamBytes = new byte[count];
for (int i = 0; i < count; i++)
{
streamBytes[i] = (byte)i;
}
var stremMock = new Mock<IStream>();
stremMock
.Setup(x => x.Read(It.IsAny<byte []>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<byte [], int, IntPtr>((buffer, countToRead , bytesReadPtr) =>
{
Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length);
Marshal.WriteInt32(bytesReadPtr, count);
});
var streamWrapper = new StreamWrapper(stremMock.Object);
// Act
var bytesRead = streamWrapper.Read(inputBuffer, offset, count);
// Assert
CollectionAssert.AreEqual(streamBytes, inputBuffer.Skip(offset).Take(count).ToArray());
Assert.AreEqual(count, bytesRead);
}
[TestMethod]
public void StreamWrapper_ShouldThrowNotImplementedException_WhenFlushCalled()
{
// Arrange
var stremMock = new Mock<IStream>();
var streamWrapper = new StreamWrapper(stremMock.Object);
NotImplementedException exception = null;
// Act
try
{
streamWrapper.Flush();
}
catch (NotImplementedException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
[TestMethod]
public void StreamWrapper_ShouldThrowNotImplementedException_WhenSetLengthCalled()
{
// Arrange
var stremMock = new Mock<IStream>();
var streamWrapper = new StreamWrapper(stremMock.Object);
NotImplementedException exception = null;
// Act
try
{
streamWrapper.SetLength(5);
}
catch (NotImplementedException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
[TestMethod]
public void StreamWrapper_ShouldThrowNotImplementedException_WhenWriteCalled()
{
// Arrange
var stremMock = new Mock<IStream>();
var streamWrapper = new StreamWrapper(stremMock.Object);
NotImplementedException exception = null;
// Act
try
{
streamWrapper.Write(new byte[5], 0, 0);
}
catch (NotImplementedException ex)
{
exception = ex;
}
// Assert
Assert.IsNotNull(exception);
}
}
}

View file

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>UnitTests_PreviewHandlerCommon</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{748417CA-F17E-487F-9411-CAFB6D3F4877}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests_PreviewHandlerCommon</RootNamespace>
<AssemblyName>UnitTests-PreviewHandlerCommon</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FileBasedPreviewHandlerTests.cs" />
<Compile Include="FormHandlerControlTests.cs" />
<Compile Include="PreviewHandlerBaseTests.cs" />
<Compile Include="StreamBasedPreviewHandlerTests.cs" />
<Compile Include="StreamWrapperTests.cs" />
<Compile Include="WebBrowserExtUnitTests.cs">
<SubType>Component</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq">
<Version>4.13.1</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.3.2</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.3.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\PreviewHandlerCommon.csproj">
<Project>{af2349b8-e5b6-4004-9502-687c1c7730b1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PreviewHandlerCommon;
using System.Reflection;
namespace UnitTests_PreviewHandlerCommon
{
[TestClass]
public class WebBrowserExtUnitTests : WebBrowserExt
{
private const string DISPIDAMBIENTDLCONTROL = "[DISPID=-5512]";
[TestMethod]
public void InvokeMember_ShouldSetValidFlags_WhenCalledWithValidDispId()
{
// Arrange
var extendedSite = CreateWebBrowserSiteBase() as WebBrowserSiteExt;
// Act
var actualFlags = (int)extendedSite.InvokeMember(DISPIDAMBIENTDLCONTROL, BindingFlags.InvokeMethod, null, null, null, null, null, null);
// Assert
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.PRAGMA_NO_CACHE) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.FORCEOFFLINE) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_CLIENTPULL) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_SCRIPTS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_JAVA) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_FRAMEDOWNLOAD) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NOFRAMES) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_BEHAVIORS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.SILENT) >= 0);
}
[TestMethod]
public void InvokeMember_ShouldOnlySetValidFlags_WhenCalledWithValidDispId()
{
// Arrange
var extendedSite = CreateWebBrowserSiteBase() as WebBrowserSiteExt;
// Act
var actualFlags = (int)extendedSite.InvokeMember(DISPIDAMBIENTDLCONTROL, BindingFlags.InvokeMethod, null, null, null, null, null, null);
// Assert
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.DLIMAGES) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.VIDEOS) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.BGSOUNDS) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.DOWNLOADONLY) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.RESYNCHRONIZE) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_METACHARSET) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.URL_ENCODING_DISABLE_UTF8) == 0);
Assert.IsTrue((actualFlags & (uint)WebBrowserDownloadControlFlags.URL_ENCODING_ENABLE_UTF8) == 0);
}
}
}

View file

@ -0,0 +1,186 @@
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using PreviewHandlerCommon;
using SvgPreviewHandler;
namespace UnitTests_SvgPreviewHandler
{
[TestClass]
public class SvgPreviewControlTests
{
[TestMethod]
public void SvgPreviewControl_ShouldAddExtendedBrowserControl_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(svgPreviewControl.Controls.Count, 1);
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebBrowserExt));
}
[TestMethod]
public void SvgPreviewControl_ShouldSetDocumentStream_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.IsNotNull(((WebBrowser)svgPreviewControl.Controls[0]).DocumentStream);
}
[TestMethod]
public void SvgPreviewControl_ShouldDisableWebBrowserContextMenu_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).IsWebBrowserContextMenuEnabled, false);
}
[TestMethod]
public void SvgPreviewControl_ShouldFillDockForWebBrowser_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).Dock, DockStyle.Fill);
}
[TestMethod]
public void SvgPreviewControl_ShouldSetScriptErrorsSuppressedProperty_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScriptErrorsSuppressed, true);
}
[TestMethod]
public void SvgPreviewControl_ShouldSetScrollBarsEnabledProperty_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).ScrollBarsEnabled, true);
}
[TestMethod]
public void SvgPreviewControl_ShouldDisableAllowNavigation_WhenDoPreviewCalled()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(((WebBrowser)svgPreviewControl.Controls[0]).AllowNavigation, false);
}
[TestMethod]
public void SvgPreviewControl_ShouldAddValidInfoBar_IfSvgPreviewThrows()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();
mockStream
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Throws(new Exception());
// Act
svgPreviewControl.DoPreview(mockStream.Object);
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
// Assert
Assert.IsFalse(string.IsNullOrWhiteSpace(textBox.Text));
Assert.AreEqual(svgPreviewControl.Controls.Count, 1);
Assert.AreEqual(textBox.Dock, DockStyle.Top);
Assert.AreEqual(textBox.BackColor, Color.LightYellow);
Assert.IsTrue(textBox.Multiline);
Assert.IsTrue(textBox.ReadOnly);
Assert.AreEqual(textBox.ScrollBars, RichTextBoxScrollBars.None);
Assert.AreEqual(textBox.BorderStyle, BorderStyle.None);
}
[TestMethod]
public void SvgPreviewControl_InfoBarWidthShouldAdjustWithParentControlWidthChanges_IfSvgPreviewThrows()
{
// Arrange
var svgPreviewControl = new SvgPreviewControl();
var mockStream = new Mock<IStream>();
mockStream
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Throws(new Exception());
svgPreviewControl.DoPreview(mockStream.Object);
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
var incrementParentControlWidth = 5;
var intialParentWidth = svgPreviewControl.Width;
var intitialTextBoxWidth = textBox.Width;
var finalParentWidth = intialParentWidth + incrementParentControlWidth;
// Act
svgPreviewControl.Width += incrementParentControlWidth;
// Assert
Assert.AreEqual(intialParentWidth, intitialTextBoxWidth);
Assert.AreEqual(finalParentWidth, textBox.Width);
}
private IStream GetMockStream(string streamData)
{
var mockStream = new Mock<IStream>();
var streamBytes = Encoding.UTF8.GetBytes(streamData);
var streamMock = new Mock<IStream>();
var firstCall = true;
streamMock
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<byte[], int, IntPtr>((buffer, countToRead, bytesReadPtr) =>
{
if (firstCall)
{
Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length);
Marshal.WriteInt32(bytesReadPtr, streamBytes.Length);
firstCall = false;
}
else
{
Marshal.WriteInt32(bytesReadPtr, 0);
}
});
return streamMock.Object;
}
}
}

View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>UnitTests-SvgPreviewHandler</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{060D75DA-2D1C-48E6-A4A1-6F0718B64661}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests_SvgPreviewHandler</RootNamespace>
<AssemblyName>UnitTests-SvgPreviewHandler</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SvgPreviewControlTests.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq">
<Version>4.13.1</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.3.2</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.3.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj">
<Project>{AF2349B8-E5B6-4004-9502-687C1C7730B1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
<ProjectReference Include="..\SvgPreviewHandler\SvgPreviewHandler.csproj">
<Project>{DA425894-6E13-404F-8DCB-78584EC0557A}</Project>
<Name>SvgPreviewHandler</Name>
<EmbedInteropTypes>False</EmbedInteropTypes>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\..\..\..\installer\Version.props" />
<!-- We don't have GenerateAssemblyInfo task until we use .net core, so we generate it with WriteLinesToFile -->
<PropertyGroup>
<AssemblyTitle>PreviewHandlerCommon</AssemblyTitle>
<AssemblyCompany>Microsoft Corp.</AssemblyCompany>
<AssemblyCopyright>Copyright (C) 2019 Microsoft Corp.</AssemblyCopyright>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="// Copyright (c) Microsoft Corporation" />
<HeaderLines Include="// The Microsoft Corporation licenses this file to you under the MIT license." />
<HeaderLines Include="// See the LICENSE file in the project root for more information." />
<HeaderLines Include="#pragma warning disable SA1516" />
<HeaderLines Include="using System.Reflection%3b" />
<HeaderLines Include="using System.Resources%3b" />
<HeaderLines Include="using System.Runtime.CompilerServices%3b" />
<HeaderLines Include="using System.Runtime.InteropServices%3b" />
<HeaderLines Include="[assembly: AssemblyTitle(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyDescription(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyConfiguration(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCompany(&quot;$(AssemblyCompany)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCopyright(&quot;$(AssemblyCopyright)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyProduct(&quot;$(AssemblyTitle)&quot;)]" />
<HeaderLines Include="[assembly: AssemblyTrademark(&quot;&quot;)]" />
<HeaderLines Include="[assembly: AssemblyCulture(&quot;&quot;)]" />
<HeaderLines Include="[assembly: ComVisible(false)]" />
<HeaderLines Include="[assembly: NeutralResourcesLanguage(&quot;en-US&quot;)]" />
<HeaderLines Include="[assembly: AssemblyVersion(&quot;$(Version).0&quot;)]" />
<HeaderLines Include="[assembly: AssemblyFileVersion(&quot;$(Version).0&quot;)]" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cs" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AF2349B8-E5B6-4004-9502-687C1C7730B1}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PreviewHandlerCommon</RootNamespace>
<AssemblyName>PreviewHandlerCommon</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\PreviewHandlerCommonDocumentation.xml</DocumentationFile>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>$(SolutionDir)$(Platform)\$(Configuration)\modules\PreviewHandlerCommonDocumentation.xml</DocumentationFile>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="cominterop\IInitializeWithFile.cs" />
<Compile Include="cominterop\COLORREF.cs" />
<Compile Include="cominterop\IInitializeWithStream.cs" />
<Compile Include="cominterop\IObjectWithSite.cs" />
<Compile Include="cominterop\IOleWindow.cs" />
<Compile Include="cominterop\IPreviewHandler.cs" />
<Compile Include="cominterop\IPreviewHandlerFrame.cs" />
<Compile Include="cominterop\IPreviewHandlerVisuals.cs" />
<Compile Include="cominterop\LOGFONT.cs" />
<Compile Include="cominterop\MSG.cs" />
<Compile Include="cominterop\RECT.cs" />
<Compile Include="controls\WebBrowserDownloadControlFlags.cs" />
<Compile Include="controls\WebBrowserExt.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="examplehandler\CustomControlTest.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="handlers\FileBasedPreviewHandler.cs" />
<Compile Include="controls\FormHandlerControl.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Generated Files\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="controls\IPreviewHandlerControl.cs" />
<Compile Include="handlers\PreviewHandlerBase.cs" />
<Compile Include="handlers\StreamBasedPreviewHandler.cs" />
<Compile Include="examplehandler\TestCustomHandler.cs" />
<Compile Include="Telemetry\TelemetryBase.cs" />
<Compile Include="Utilities\StreamWrapper.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Eventing.Reader;
using System.Diagnostics.Tracing;
namespace PreviewHandlerCommon.Telemetry
{
/// <summary>
/// Base class for telemetry events.
/// </summary>
public class TelemetryBase : EventSource
{
/// <summary>
/// The event tag for this event source.
/// </summary>
public const EventTags ProjectTelemetryTagProductAndServicePerformance = (EventTags)0x0u;
/// <summary>
/// The event keyword for this event source.
/// </summary>
public const EventKeywords ProjectKeywordMeasure = (EventKeywords)0x0;
/// <summary>
/// Group ID for Powertoys project.
/// </summary>
private static readonly string[] PowerToysTelemetryTraits = { "ETW_GROUP", "{42749043-438c-46a2-82be-c6cbeb192ff2}" };
/// <summary>
/// Initializes a new instance of the <see cref="TelemetryBase"/> class.
/// </summary>
/// <param name="eventSourceName">.</param>
public TelemetryBase(
string eventSourceName)
: base(
eventSourceName,
EventSourceSettings.EtwSelfDescribingEventFormat,
PowerToysTelemetryTraits)
{
return;
}
}
}

View file

@ -0,0 +1,242 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace Common.Utilities
{
/// <summary>
/// Wraps <see cref="IStream"/> interface into <see cref="Stream"/> Class.
/// </summary>
/// <remarks>
/// Implements only read from the stream functionality.
/// </remarks>
public class StreamWrapper : Stream
{
private IStream stream;
/// <summary>
/// Initializes a new instance of the <see cref="StreamWrapper"/> class.
/// </summary>
/// <param name="stream">A pointer to an <see cref="IStream" /> interface that represents the stream source.</param>
public StreamWrapper(IStream stream)
{
this.stream = stream ?? throw new ArgumentNullException(nameof(stream));
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead
{
get
{
return true;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
public override bool CanSeek
{
get
{
return true;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// Gets the length in bytes of the stream.
/// </summary>
public override long Length
{
get
{
this.CheckDisposed();
System.Runtime.InteropServices.ComTypes.STATSTG stat;
// Stat called with STATFLAG_NONAME. The pwcsName is not required more details https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-statflag
this.stream.Stat(out stat, 1); // STATFLAG_NONAME
return stat.cbSize;
}
}
/// <summary>
/// Gets or Sets the position within the current.
/// </summary>
public override long Position
{
get
{
return this.Seek(0, SeekOrigin.Current);
}
set
{
this.Seek(value, SeekOrigin.Begin);
}
}
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the end of the stream has been reached.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
this.CheckDisposed();
if (offset < 0 || count < 0 || offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException();
}
byte[] localBuffer = buffer;
if (offset > 0)
{
localBuffer = new byte[count];
}
IntPtr bytesReadPtr = Marshal.AllocCoTaskMem(sizeof(int));
try
{
this.stream.Read(localBuffer, count, bytesReadPtr);
int bytesRead = Marshal.ReadInt32(bytesReadPtr);
if (offset > 0)
{
Array.Copy(localBuffer, 0, buffer, offset, bytesRead);
}
return bytesRead;
}
finally
{
Marshal.FreeCoTaskMem(bytesReadPtr);
}
}
/// <summary>
/// Sets the position within the current stream.
/// </summary>
/// <param name="offset">A byte offset relative to the origin parameter.</param>
/// <param name="origin">A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
this.CheckDisposed();
int dwOrigin;
// Maps the SeekOrigin with dworigin more details: https://docs.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-stream_seek
switch (origin)
{
case SeekOrigin.Begin:
dwOrigin = 0; // STREAM_SEEK_SET
break;
case SeekOrigin.Current:
dwOrigin = 1; // STREAM_SEEK_CUR
break;
case SeekOrigin.End:
dwOrigin = 2; // STREAM_SEEK_END
break;
default:
throw new ArgumentOutOfRangeException();
}
IntPtr posPtr = Marshal.AllocCoTaskMem(sizeof(long));
try
{
this.stream.Seek(offset, dwOrigin, posPtr);
return Marshal.ReadInt64(posPtr);
}
finally
{
Marshal.FreeCoTaskMem(posPtr);
}
}
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <remarks>
/// Not implemented current implementation supports only read.
/// </remarks>
public override void Flush()
{
throw new NotImplementedException();
}
/// <summary>
/// Sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
/// /// <remarks>
/// Not implemented current implementation supports only read.
/// </remarks>
public override void SetLength(long value)
{
throw new NotImplementedException();
}
/// <summary>
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <remarks>
/// Not implemented current implementation supports only read.
/// </remarks>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (this.stream != null)
{
if (Marshal.IsComObject(this.stream))
{
Marshal.ReleaseComObject(this.stream);
}
this.stream = null;
}
}
private void CheckDisposed()
{
if (this.stream == null)
{
throw new ObjectDisposedException(nameof(StreamWrapper));
}
}
}
}

View file

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// The COLORREF value is used to specify an RGB color.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct COLORREF
{
/// <summary>
/// Stores an RGB color value in a 32 bit integer.
/// </summary>
public uint Dword;
/// <summary>
/// Gets RGB value stored in <see cref="Dword"/> in <see cref="Color"/> structure.
/// </summary>
public Color Color
{
get
{
return Color.FromArgb(
(int)(0x000000FFU & this.Dword),
(int)(0x0000FF00U & this.Dword) >> 8,
(int)(0x00FF0000U & this.Dword) >> 16);
}
}
}
}

View file

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.Cominterop
{
/// <summary>
/// Exposes a method to initialize a handler, such as a property handler, thumbnail handler, or preview handler, with a file path.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("b7d14566-0509-4cce-a71f-0a554233bd9b")]
public interface IInitializeWithFile
{
/// <summary>
/// Initializes a handler with a file path.
/// </summary>
/// <param name="pszFilePath">File Path.</param>
/// <param name="grfMode">Indicate the Access Mode either STGM_READ (Read Only Access) or STGM_READWRITE (Read and Write Access).</param>
void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode);
}
}

View file

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace Common.ComInterlop
{
/// <summary>
/// Exposes a method that initializes a handler, such as a property handler, thumbnail handler, or preview handler, with a stream.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")]
public interface IInitializeWithStream
{
/// <summary>
/// Initializes a handler with a stream.
/// </summary>
/// <param name="pstream">A pointer to an <see cref="IStream" /> interface that represents the stream source.</param>
/// <param name="grfMode">One of the <see href="https://docs.microsoft.com/en-us/windows/win32/stg/stgm-constants" >STGM</see> values that indicates the access mode for <paramref name="pstream"/>.</param>
void Initialize(IStream pstream, uint grfMode);
}
}

View file

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Provides a simplified way to support communication between an object and its site in the container.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352")]
public interface IObjectWithSite
{
/// <summary>
/// Provides the site's pointer to the site object.
/// </summary>
/// <param name="pUnkSite">Address of an interface pointer to the site managing this object.</param>
void SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkSite);
/// <summary>
/// Retrieves the last site set using the <see cref="SetSite(object)" /> method. In cases where there is no known site, the object returns an exception.
/// </summary>
/// <param name="riid">Provides the IID of the interface pointer returned in the <paramref name="ppvSite"/> parameter.</param>
/// <param name="ppvSite">The address of the caller's void variable in which the object stores the interface pointer of the site last seen in the <see cref="SetSite(object)" />.</param>
void GetSite(ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvSite);
}
}

View file

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// The IOleWindow interface provides methods that allow an application to obtain the handle to the various windows that participate
/// in in-place activation, and also to enter and exit context-sensitive help mode.
/// </summary>
[ComImport]
[Guid("00000114-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleWindow
{
/// <summary>
/// Retrieves a handle to one of the windows participating in in-place activation (frame, document, parent, or in-place object window).
/// </summary>
/// <param name="phwnd">A pointer to a variable that receives the window handle.</param>
void GetWindow(out IntPtr phwnd);
/// <summary>
/// Determines whether context-sensitive help mode should be entered during an in-place activation session.
/// </summary>
/// <param name="fEnterMode">TRUE if help mode should be entered; FALSE if it should be exited.</param>
void ContextSensitiveHelp([MarshalAs(UnmanagedType.Bool)] bool fEnterMode);
}
}

View file

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Exposes methods for the display of rich previews.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("8895b1c6-b41f-4c1c-a562-0d564250836f")]
public interface IPreviewHandler
{
/// <summary>
/// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window.
/// </summary>
/// <param name="hwnd">A handle to the parent window.</param>
/// <param name="rect">A pointer to a <see cref="RECT"/> defining the area for the previewer.</param>
void SetWindow(IntPtr hwnd, ref RECT rect);
/// <summary>
/// Directs the preview handler to change the area within the parent hwnd that it draws into.
/// </summary>
/// <param name="rect">A pointer to a <see cref="RECT"/> to be used for the preview.</param>
void SetRect(ref RECT rect);
/// <summary>
/// Directs the preview handler to load data from the source specified in an earlier Initialize method call, and to begin rendering to the previewer window.
/// </summary>
void DoPreview();
/// <summary>
/// Directs the preview handler to cease rendering a preview and to release all resources that have been allocated based on the item passed in during the initialization.
/// </summary>
void Unload();
/// <summary>
/// Directs the preview handler to set focus to itself.
/// </summary>
void SetFocus();
/// <summary>
/// Directs the preview handler to return the HWND from calling the GetFocus Function.
/// </summary>
/// <param name="phwnd">When this method returns, contains a pointer to the HWND returned from calling the GetFocus Function from the preview handler's foreground thread.</param>
void QueryFocus(out IntPtr phwnd);
/// <summary>
/// Directs the preview handler to handle a keystroke passed up from the message pump of the process in which the preview handler is running.
/// </summary>
/// <param name="pmsg">A pointer to a window message.</param>
/// <returns>If the keystroke message can be processed by the preview handler, the handler will process it and return S_OK(0). If the preview handler cannot process the keystroke message, it
/// will offer it to the host using <see cref="IPreviewHandlerFrame.TranslateAccelerator(ref MSG)"/>. If the host processes the message, this method will return S_OK(0). If the host does not process the message, this method will return S_FALSE(1).
/// </returns>
[PreserveSig]
uint TranslateAccelerator(ref MSG pmsg);
}
}

View file

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Enables preview handlers to pass keyboard shortcuts to the host. This interface retrieves a list of keyboard shortcuts and directs the host to handle a keyboard shortcut.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("fec87aaf-35f9-447a-adb7-20234491401a")]
public interface IPreviewHandlerFrame
{
/// <summary>
/// Gets a list of the keyboard shortcuts for the preview host.
/// </summary>
/// <param name="pinfo">A pointer to a <see href="https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ns-shobjidl_core-previewhandlerframeinfo">PREVIEWHANDLERFRAMEINFO</see> structure
/// that receives accelerator table information.</param>
void GetWindowContext(IntPtr pinfo);
/// <summary>
/// Directs the host to handle an keyboard shortcut passed from the preview handler.
/// </summary>
/// <param name="pmsg">A reference to <see cref="MSG"/> that corresponds to a keyboard shortcut.</param>
/// <returns>If the keyboard shortcut is one that the host intends to handle, the host will process it and return S_OK(0); otherwise, it returns S_FALSE(1).</returns>
[PreserveSig]
uint TranslateAccelerator(ref MSG pmsg);
}
}

View file

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Exposes methods for applying color and font information to preview handlers.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("8327b13c-b63f-4b24-9b8a-d010dcc3f599")]
public interface IPreviewHandlerVisuals
{
/// <summary>
/// Sets the background color of the preview handler.
/// </summary>
/// <param name="color">A value of type <see cref="COLORREF"/> to use for the preview handler background.</param>
void SetBackgroundColor(COLORREF color);
/// <summary>
/// Sets the font attributes to be used for text within the preview handler.
/// </summary>
/// <param name="plf">A pointer to a <see cref="LOGFONT"/> Structure containing the necessary attributes for the font to use.</param>
void SetFont(ref LOGFONT plf);
/// <summary>
/// Sets the color of the text within the preview handler.
/// </summary>
/// <param name="color">A value of type <see cref="COLORREF"/> to use for the preview handler text color.</param>
void SetTextColor(COLORREF color);
}
}

View file

@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Defines the attributes of a font.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct LOGFONT
{
/// <summary>
/// Value of type INT that specifies the height, in logical units, of the font's character cell or character.
/// </summary>
public int LfHeight;
/// <summary>
/// Value of type INT that specifies the width, in logical units, of characters in the font.
/// </summary>
public int LfWidth;
/// <summary>
/// Value of type INT that contains the angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement
/// vector is parallel to the base line of a row of text.
/// </summary>
public int LfEscapement;
/// <summary>
/// Value of type INT that specifies the angle, in tenths of degrees, between each character's base line and the x-axis of the device.
/// </summary>
public int LfOrientation;
/// <summary>
/// Value of type INT that specifies the weight of the font in the range from 0 through 1000.
/// </summary>
public int LfWeight;
/// <summary>
/// Value of type BYTE that specifies an italic font if set to TRUE.
/// </summary>
public byte LfItalic;
/// <summary>
/// Value of type BYTE that specifies an underlined font if set to TRUE.
/// </summary>
public byte LfUnderline;
/// <summary>
/// Value of type BYTE that specifies a strikeout font if set to TRUE.
/// </summary>
public byte LfStrikeOut;
/// <summary>
/// Value of type BYTE that specifies the character set.
/// </summary>
public byte LfCharSet;
/// <summary>
/// Value of type BYTE that specifies the output precision. The output precision defines how closely the output must match the requested
/// font's height, width, character orientation, escapement, pitch, and font type.
/// </summary>
public byte LfOutPrecision;
/// <summary>
/// Value of type BYTE that specifies the clipping precision. The clipping precision defines how to clip characters that are partially outside the clipping region.
/// </summary>
public byte LfClipPrecision;
/// <summary>
/// Value of type BYTE that specifies the output quality. The output quality defines how carefully the GDI must attempt to match the logical-font attributes to those of an actual physical font.
/// </summary>
public byte LfQuality;
/// <summary>
/// Value of type BYTE that specifies the pitch and family of the font.
/// </summary>
public byte LfPitchAndFamily;
/// <summary>
/// Array of wide characters that contains a null-terminated string that specifies the typeface name of the font. The length of the string must not exceed 32 characters, including the NULL terminator.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string LfFaceName;
}
}

View file

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Contains message information from a thread's message queue.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
/// <summary>
/// A handle to the window whose window procedure receives the message. This member is NULL when the message is a thread message.
/// </summary>
public IntPtr Hwnd;
/// <summary>
/// The message identifier. Applications can only use the low word; the high word is reserved by the system.
/// </summary>
public int Message;
/// <summary>
/// Additional information about the message. The exact meaning depends on the value of the message member.
/// </summary>
public IntPtr WParam;
/// <summary>
/// Additional information about the message. The exact meaning depends on the value of the message member.
/// </summary>
public IntPtr LParam;
/// <summary>
/// The time at which the message was posted.
/// </summary>
public int Time;
/// <summary>
/// The x coordinate of cursor position, in screen coordinates, when the message was posted.
/// </summary>
public int PtX;
/// <summary>
/// The y coordinate of cursor position, in screen coordinates, when the message was posted.
/// </summary>
public int PtY;
}
}

View file

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
/// <summary>
/// Specifies the x-coordinate of the upper-left corner of the rectangle.
/// </summary>
public int Left;
/// <summary>
/// Specifies the y-coordinate of the upper-left corner of the rectangle.
/// </summary>
public int Top;
/// <summary>
/// Specifies the x-coordinate of the lower-right corner of the rectangle.
/// </summary>
public int Right;
/// <summary>
/// Specifies the y-coordinate of the lower-right corner of the rectangle.
/// </summary>
public int Bottom;
/// <summary>
/// Creates a <see cref="Rectangle" /> structure with the edge locations specified in the struct.
/// </summary>
/// <returns>Return a <see cref="Rectangle"/>.</returns>
public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(this.Left, this.Top, this.Right, this.Bottom);
}
}
}

View file

@ -0,0 +1,166 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Common
{
/// <summary>
/// Form based implementation of <see cref="IPreviewHandlerControl"/>.
/// </summary>
public abstract class FormHandlerControl : Form, IPreviewHandlerControl
{
/// <summary>
/// Holds the parent window handle.
/// </summary>
private IntPtr parentHwnd;
/// <summary>
/// Initializes a new instance of the <see cref="FormHandlerControl"/> class.
/// </summary>
public FormHandlerControl()
{
// Gets the handle of the control to create the control on the VI thread. Invoking the Control.Handle get accessor forces the creation of the underlying window for the control.
// This is important, because the thread that instantiates the preview handler component and calls its constructor is a single-threaded apartment (STA) thread, but the thread that calls into the interface members later on is a multithreaded apartment (MTA) thread. Windows Forms controls are meant to run on STA threads.
// More details: https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/windows-vista-and-office-writing-your-own-preview-handlers.
var forceCreation = this.Handle;
this.FormBorderStyle = FormBorderStyle.None;
this.Visible = false;
}
/// <inheritdoc />
public IntPtr GetHandle()
{
return this.Handle;
}
/// <inheritdoc />
public void QueryFocus(out IntPtr result)
{
var getResult = IntPtr.Zero;
this.InvokeOnControlThread(() =>
{
getResult = GetFocus();
});
result = getResult;
}
/// <inheritdoc />
public void SetBackgroundColor(Color argbColor)
{
this.InvokeOnControlThread(() =>
{
this.BackColor = argbColor;
});
}
/// <inheritdoc />
public void SetFocus()
{
this.InvokeOnControlThread(() =>
{
this.Focus();
});
}
/// <inheritdoc />
public void SetFont(Font font)
{
this.InvokeOnControlThread(() =>
{
this.Font = font;
});
}
/// <inheritdoc />
public void SetRect(Rectangle windowBounds)
{
this.UpdateWindowBounds(windowBounds);
}
/// <inheritdoc />
public void SetTextColor(Color color)
{
this.InvokeOnControlThread(() =>
{
this.ForeColor = color;
});
}
/// <inheritdoc />
public void SetWindow(IntPtr hwnd, Rectangle rect)
{
this.parentHwnd = hwnd;
this.UpdateWindowBounds(rect);
}
/// <inheritdoc />
public virtual void Unload()
{
this.InvokeOnControlThread(() =>
{
this.Visible = false;
foreach (Control c in this.Controls)
{
c.Dispose();
}
this.Controls.Clear();
});
// Call garbage collection at the time of unloading of Preview. This is to mitigate issue with WebBrowser Control not able to dispose properly.
// Which is preventing prevhost.exe to exit at the time of closing File explorer.
// Preview Handlers run in a separate process from PowerToys. This will not affect the performance of other modules.
// Mitigate the following Github issue: https://github.com/microsoft/PowerToys/issues/1468
GC.Collect();
}
/// <inheritdoc />
public virtual void DoPreview<T>(T dataSource)
{
this.Visible = true;
}
/// <summary>
/// Executes the specified delegate on the thread that owns the control's underlying window handle.
/// </summary>
/// <param name="func">Delegate to run.</param>
public void InvokeOnControlThread(MethodInvoker func)
{
this.Invoke(func);
}
/// <summary>
/// Changes the parent window of the specified child window.
/// </summary>
/// <param name="hWndChild">A handle to the child window.</param>
/// <param name="hWndNewParent">A handle to the new parent window.</param>
/// <returns>If the function succeeds, the return value is a handle to the previous parent window and NULL in case of failure.</returns>
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
/// <summary>
/// Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue.
/// </summary>
/// <returns>The return value is the handle to the window with the keyboard focus. If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL.</returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetFocus();
/// <summary>
/// Update the Form Control window with the passed rectangle.
/// </summary>
/// <param name="windowBounds">An instance of rectangle.</param>
private void UpdateWindowBounds(Rectangle windowBounds)
{
this.InvokeOnControlThread(() =>
{
SetParent(this.Handle, this.parentHwnd);
this.Bounds = windowBounds;
});
}
}
}

View file

@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
namespace Common
{
/// <summary>
/// Interface defining methods requirement by the <see cref="PreviewHandlerBase"/> control.
/// </summary>
public interface IPreviewHandlerControl
{
/// <summary>
/// Directs the preview handler to return the HWND from calling the GetFocus function.
/// Source: https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ipreviewhandler-queryfocus.
/// </summary>
/// <param name="result">Returns the handle of the window with focus.</param>
void QueryFocus(out IntPtr result);
/// <summary>
/// Sets focus to the control.
/// </summary>
void SetFocus();
/// <summary>
/// Sets the font according to the font set in Windows Settings.
/// More details: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#ipreviewhandlervisualssetfont.
/// </summary>
/// <param name="font">Instance of Font.</param>
void SetFont(Font font);
/// <summary>
/// Sets the Text color according to the Windows Settings.
/// </summary>
/// <param name="color">Instance of color.</param>
void SetTextColor(Color color);
/// <summary>
/// Sets the Background color. For instance to fill the window when the handler renders to area smaller provided by SetWindow and SetRect.
/// </summary>
/// <param name="argbColor">Instance of color.</param>
void SetBackgroundColor(Color argbColor);
/// <summary>
/// Gets the HWND of the control window.
/// </summary>
/// <returns>Pointer to the window handle.</returns>
IntPtr GetHandle();
/// <summary>
/// Hide the preview and free any resource used for the preview.
/// </summary>
void Unload();
/// <summary>
/// Directs the control to change the area within the parent hwnd that it draws into.
/// </summary>
/// <param name="windowBounds">Instance of Rectangle defining the area.</param>
void SetRect(Rectangle windowBounds);
/// <summary>
/// Sets the parent window of the previewer window, as well as the area within the parent to be used for the previewer window..
/// </summary>
/// <param name="hwnd">Pointer to the parent window handle.</param>
/// <param name="rect">Instance of Rectangle defining the area.</param>
void SetWindow(IntPtr hwnd, Rectangle rect);
/// <summary>
/// Called by Preview Handler to start the preview.
/// </summary>
/// <typeparam name="T">File Path or Stream reference for the file.</typeparam>
/// <param name="dataSource">Represents the source of preview data.</param>
void DoPreview<T>(T dataSource);
}
}

View file

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
/// <summary>
/// Flags to control download and execution in Web Browser Control.
/// Values of flags are defined in mshtmdid.h in distributed Windows Sdk.
/// </summary>
[Flags]
public enum WebBrowserDownloadControlFlags : int
{
/// <summary>
/// Images will be downloaded from the server if this flag is set.
/// </summary>
DLIMAGES = 0x00000010,
/// <summary>
/// Videos will be downloaded from the server if this flag is set.
/// </summary>
VIDEOS = 0x00000020,
/// <summary>
/// Background sounds will be downloaded from the server if this flag is set.
/// </summary>
BGSOUNDS = 0x00000040,
/// <summary>
/// Scripts will not be executed.
/// </summary>
NO_SCRIPTS = 0x00000080,
/// <summary>
/// Java applets will not be executed.
/// </summary>
NO_JAVA = 0x00000100,
/// <summary>
/// ActiveX controls will not be executed.
/// </summary>
NO_RUNACTIVEXCTLS = 0x00000200,
/// <summary>
/// ActiveX controls will not be downloaded.
/// </summary>
NO_DLACTIVEXCTLS = 0x00000400,
/// <summary>
/// The page will only be downloaded, not displayed.
/// </summary>
DOWNLOADONLY = 0x00000800,
/// <summary>
/// WebBrowser Control will download and parse a frameSet, but not the individual frame objects within the frameSet.
/// </summary>
NO_FRAMEDOWNLOAD = 0x00001000,
/// <summary>
/// The server will be asked for update status. Cached files will be used if the server indicates that the cached information is up-to-date.
/// </summary>
RESYNCHRONIZE = 0x00002000,
/// <summary>
/// Files will be re-downloaded from the server regardless of the update status of the files.
/// </summary>
PRAGMA_NO_CACHE = 0x00004000,
/// <summary>
/// Behaviors are not downloaded and are disabled in the document.
/// </summary>
NO_BEHAVIORS = 0x00008000,
/// <summary>
/// Character sets specified in meta elements are suppressed.
/// </summary>
NO_METACHARSET = 0x00010000,
/// <summary>
/// The browsing component will disable UTF-8 encoding.
/// </summary>
URL_ENCODING_DISABLE_UTF8 = 0x00020000,
/// <summary>
/// The browsing component will enable UTF-8 encoding.
/// </summary>
URL_ENCODING_ENABLE_UTF8 = 0x00040000,
/// <summary>
/// No Documentation Available.
/// </summary>
NOFRAMES = 0x00080000,
/// <summary>
/// WebBrowser Control always operates in offline mode.
/// </summary>
FORCEOFFLINE = 0x10000000,
/// <summary>
/// No client pull operations will be performed.
/// </summary>
NO_CLIENTPULL = 0x20000000,
/// <summary>
/// No user interface will be displayed during downloads.
/// </summary>
SILENT = 0x40000000,
}

View file

@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
namespace PreviewHandlerCommon
{
/// <summary>
/// Customized the WebBrowser to get control over what it downloads, displays and executes.
/// </summary>
public class WebBrowserExt : WebBrowser
{
/// <inheritdoc/>
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
// Returns instance of WebBrowserSiteExt.
return new WebBrowserSiteExt(this);
}
/// <summary>
/// Extend the WebBrowserSite with IDispatch implementation to handle the DISPID_AMBIENT_DLCONTROL.
/// More details: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa770041(v=vs.85)?redirectedfrom=MSDN#controlling-download-and-execution.
/// </summary>
protected class WebBrowserSiteExt : WebBrowserSite, IReflect
{
// Dispid of DISPID_AMBIENT_DLCONTROL is defined in MsHtmdid.h header file in distributed Windows Sdk component.
private const string DISPIDAMBIENTDLCONTROL = "[DISPID=-5512]";
private WebBrowserExt browserExtControl;
/// <summary>
/// Initializes a new instance of the <see cref="WebBrowserSiteExt"/> class.
/// </summary>
/// <param name="browserControl">Browser Control Instance pass to the site.</param>
public WebBrowserSiteExt(WebBrowserExt browserControl)
: base(browserControl)
{
this.browserExtControl = browserControl;
}
/// <inheritdoc/>
public Type UnderlyingSystemType
{
get { return this.GetType(); }
}
/// <inheritdoc/>
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
object result;
if (name.Equals(DISPIDAMBIENTDLCONTROL))
{
result = Convert.ToInt32(
WebBrowserDownloadControlFlags.PRAGMA_NO_CACHE |
WebBrowserDownloadControlFlags.FORCEOFFLINE |
WebBrowserDownloadControlFlags.NO_CLIENTPULL |
WebBrowserDownloadControlFlags.NO_SCRIPTS |
WebBrowserDownloadControlFlags.NO_JAVA |
WebBrowserDownloadControlFlags.NO_FRAMEDOWNLOAD |
WebBrowserDownloadControlFlags.NOFRAMES |
WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS |
WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS |
WebBrowserDownloadControlFlags.NO_BEHAVIORS |
WebBrowserDownloadControlFlags.SILENT);
}
else
{
result = this.GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
}
return result;
}
/// <inheritdoc/>
public FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return this.GetType().GetFields(bindingAttr);
}
/// <inheritdoc/>
public MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
return this.GetType().GetMethods(bindingAttr);
}
/// <inheritdoc/>
public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
return this.GetType().GetProperties(bindingAttr);
}
/// <inheritdoc/>
public FieldInfo GetField(string name, BindingFlags bindingAttr)
{
return this.GetType().GetField(name, bindingAttr);
}
/// <inheritdoc/>
public MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
{
return this.GetType().GetMember(name, bindingAttr);
}
/// <inheritdoc/>
public MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
return this.GetType().GetMembers(bindingAttr);
}
/// <inheritdoc/>
public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
{
return this.GetType().GetMethod(name, bindingAttr);
}
/// <inheritdoc/>
public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
{
return this.GetType().GetMethod(name, bindingAttr, binder, types, modifiers);
}
/// <inheritdoc/>
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
{
return this.GetType().GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
}
/// <inheritdoc/>
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
{
return this.GetType().GetProperty(name, bindingAttr);
}
}
}
}

View file

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
using System.Windows.Forms;
namespace Common
{
/// <summary>
/// This is test custom control to test the implementation.
/// </summary>
public class CustomControlTest : FormHandlerControl
{
/// <summary>
/// Start the preview on the Control.
/// </summary>
/// <param name="dataSource">Path to the file.</param>
public override void DoPreview<T>(T dataSource)
{
this.InvokeOnControlThread(() =>
{
var filePath = dataSource as string;
WebBrowser browser = new WebBrowser();
browser.DocumentText = "Test";
browser.Navigate(filePath);
browser.Dock = DockStyle.Fill;
browser.IsWebBrowserContextMenuEnabled = false;
this.Controls.Add(browser);
base.DoPreview(dataSource);
});
}
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace Common
{
/// <summary>
/// This is a example custom handler to show how to extend the library.
/// </summary>
[Guid("22a1a8e8-e929-4732-90ce-91eaff38b614")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class TestCustomHandler : FileBasedPreviewHandler
{
private CustomControlTest previewHandlerControl;
/// <inheritdoc />
public override void DoPreview()
{
this.previewHandlerControl.DoPreview(this.FilePath);
}
/// <inheritdoc />
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
this.previewHandlerControl = new CustomControlTest();
return this.previewHandlerControl;
}
}
}

View file

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using Common.Cominterop;
namespace Common
{
/// <summary>
/// Extends the <see cref="PreviewHandlerBase" /> by implementing IInitializeWithFile.
/// </summary>
public abstract class FileBasedPreviewHandler : PreviewHandlerBase, IInitializeWithFile
{
/// <summary>
/// Gets the file path.
/// </summary>
public string FilePath { get; private set; }
/// <inheritdoc />
public void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode)
{
// Ignore the grfMode always use read mode to access the file.
this.FilePath = pszFilePath;
}
}
}

View file

@ -0,0 +1,158 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using Common.ComInterlop;
using Microsoft.Win32;
namespace Common
{
/// <summary>
/// Preview Handler base class implmenenting interfaces required by Preview Handler.
/// </summary>
public abstract class PreviewHandlerBase : IPreviewHandler, IOleWindow, IObjectWithSite, IPreviewHandlerVisuals
{
/// <summary>
/// An instance of Preview Control Used by the Handler.
/// </summary>
private IPreviewHandlerControl previewControl;
/// <summary>
/// Hold reference for the window handle.
/// </summary>
private IntPtr parentHwnd;
/// <summary>
/// Hold the bounds of the window.
/// </summary>
private Rectangle windowBounds;
/// <summary>
/// Holds the site pointer.
/// </summary>
private object unkSite;
/// <summary>
/// Holds reference for the IPreviewHandlerFrame.
/// </summary>
private IPreviewHandlerFrame frame;
/// <summary>
/// Initializes a new instance of the <see cref="PreviewHandlerBase"/> class.
/// </summary>
public PreviewHandlerBase()
{
this.previewControl = this.CreatePreviewHandlerControl();
}
/// <inheritdoc />
public abstract void DoPreview();
/// <inheritdoc />
public void SetWindow(IntPtr hwnd, ref RECT rect)
{
this.parentHwnd = hwnd;
this.windowBounds = rect.ToRectangle();
this.previewControl.SetWindow(hwnd, this.windowBounds);
}
/// <inheritdoc />
public void SetRect(ref RECT rect)
{
this.windowBounds = rect.ToRectangle();
this.previewControl.SetRect(this.windowBounds);
}
/// <inheritdoc />
public void Unload()
{
this.previewControl.Unload();
}
/// <inheritdoc />
public void SetFocus()
{
this.previewControl.SetFocus();
}
/// <inheritdoc />
public void QueryFocus(out IntPtr phwnd)
{
this.previewControl.QueryFocus(out IntPtr result);
phwnd = result;
if (phwnd == IntPtr.Zero)
{
throw new Win32Exception();
}
}
/// <inheritdoc />
public uint TranslateAccelerator(ref MSG pmsg)
{
// Current implementation simply directs all Keystrokes to IPreviewHandlerFrame. This is the recommended approach to handle keystokes for all low-integrity preview handlers.
// Source: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#ipreviewhandlertranslateaccelerator
if (this.frame != null)
{
return this.frame.TranslateAccelerator(ref pmsg);
}
const uint S_FALSE = 1;
return S_FALSE;
}
/// <inheritdoc />
public void GetWindow(out IntPtr phwnd)
{
phwnd = this.previewControl.GetHandle();
}
/// <inheritdoc />
public void ContextSensitiveHelp(bool fEnterMode)
{
// Should always return NotImplementedException. Source: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#iolewindowcontextsensitivehelp
throw new NotImplementedException();
}
/// <inheritdoc />
public void SetSite(object pUnkSite)
{
// Implementation logic details: https://docs.microsoft.com/en-us/windows/win32/shell/building-preview-handlers#iobjectwithsitesetsite
this.unkSite = pUnkSite;
this.frame = this.unkSite as IPreviewHandlerFrame;
}
/// <inheritdoc />
public void GetSite(ref Guid riid, out object ppvSite)
{
ppvSite = this.unkSite;
}
/// <inheritdoc />
public void SetBackgroundColor(COLORREF color)
{
this.previewControl.SetBackgroundColor(color.Color);
}
/// <inheritdoc />
public void SetFont(ref LOGFONT plf)
{
this.previewControl.SetFont(Font.FromLogFont(plf));
}
/// <inheritdoc />
public void SetTextColor(COLORREF color)
{
this.previewControl.SetTextColor(color.Color);
}
/// <summary>
/// Provide instance of the implementation of <see cref="IPreviewHandlerControl"/>. Should be overide by the implementation class with control object to be used.
/// </summary>
/// <returns>Instance of the <see cref="IPreviewHandlerControl"/>.</returns>
protected abstract IPreviewHandlerControl CreatePreviewHandlerControl();
}
}

View file

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices.ComTypes;
using Common.ComInterlop;
namespace Common
{
/// <summary>
/// Extends the <see cref="PreviewHandlerBase" /> by implementing IInitializeWithStream.
/// </summary>
public abstract class StreamBasedPreviewHandler : PreviewHandlerBase, IInitializeWithStream
{
/// <summary>
/// Gets the stream object to access file.
/// </summary>
public IStream Stream { get; private set; }
/// <inheritdoc/>
public void Initialize(IStream pstream, uint grfMode)
{
// Ignore the grfMode always use read mode to access the file.
this.Stream = pstream;
}
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <guiddef.h>
// 74619BDA-A66B-451D-864C-A7726F5FE650
// CLSID used in manifest file for Preview Handler.
const CLSID CLSID_SHIMActivateSvgPreviewHandler = { 0x74619BDA, 0xA66B, 0x451D, { 0x86, 0x4C, 0xA7, 0x72, 0x6F, 0x5F, 0xE6, 0x50 } };
// ddee2b8a-6807-48a6-bb20-2338174ff779
// CLSID of the .Net Com Class for Preview Handler. Should be included in the registry.dat file under \Classes\CLSID\{guid}.
// More details here: https://docs.microsoft.com/en-us/dotnet/framework/interop/registering-assemblies-with-com
const CLSID CLSID_SvgPreviewHandler = { 0xddee2b8a, 0x6807, 0x48a6, { 0xbb, 0x20, 0x23, 0x38, 0x17, 0x4f, 0xf7, 0x79 } };
// E0907A95-6F9A-4D1B-A97A-7D9D2648881E
const CLSID CLSID_SHIMActivateMdPreviewHandler = { 0xE0907A95, 0x6F9A, 0x4D1B, { 0xA9, 0x7A, 0x7D, 0x9D, 0x26, 0x48, 0x88, 0x1E } };
// 45769bcc-e8fd-42d0-947e-02beef77a1f5
const CLSID CLSID_MdPreviewHandler = { 0x45769bcc, 0xe8fd, 0x42d0, { 0x94, 0x7e, 0x02, 0xbe, 0xef, 0x77, 0xa1, 0xf5 } };
// Pairs of NativeClsid vs ManagedClsid used for preview handlers.
const std::vector<std::pair<CLSID, CLSID>> NativeToManagedClsid({
{ CLSID_SHIMActivateMdPreviewHandler, CLSID_MdPreviewHandler },
{ CLSID_SHIMActivateSvgPreviewHandler, CLSID_SvgPreviewHandler }
});

View file

@ -0,0 +1,55 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include "powerpreview.h"
#include "CLSID.h"
// Logic to shim the Activation of .Net Assembly by calling CoGetClassObject. CLSID's of Preview Handlers should be present in the registry.dat under /Classes/CLSID/{guid}.
// See the existing Preview Handlers registry entry in registry.reg file.
// This is required since MSIX currently not support .Net Assembly for Com Activation for Preview Handlers.
HRESULT CALLBACK DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv)
{
*ppv = NULL;
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
for (auto handler : NativeToManagedClsid)
{
if (handler.first == clsid)
{
hr = CoGetClassObject(handler.second, CLSCTX_INPROC_SERVER, NULL, riid, ppv);
break;
}
}
// In case of failed error code return by CoGetClassObject return CLASS_E_CLASSNOTAVAILABLE to the caller.
if (FAILED(hr))
{
hr = CLASS_E_CLASSNOTAVAILABLE;
*ppv = NULL;
}
return hr;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new PowerPreviewModule();
}

View file

@ -0,0 +1,3 @@
#include "pch.h"
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "shlwapi.lib")

View file

@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Shlwapi.h>
#include <ProjectTelemetry.h>

View file

@ -0,0 +1,134 @@
#include "pch.h"
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <settings_objects.h>
#include <common.h>
#include "powerpreview.h"
#include "trace.h"
#include "settings.h"
#include "resource.h"
// Destroy the powertoy and free memory.
void PowerPreviewModule::destroy()
{
Trace::Destroyed();
for (FileExplorerPreviewSettings* previewHandler : this->m_previewHandlers)
{
if (previewHandler != NULL)
{
delete previewHandler;
}
}
delete this;
}
// Return the display name of the powertoy, this will be cached.
const wchar_t* PowerPreviewModule::get_name()
{
return m_moduleName.c_str();
}
const wchar_t** PowerPreviewModule::get_events()
{
return nullptr;
}
// Return JSON with the configuration options.
bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size)
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
// Create a Settings object.
PowerToysSettings::Settings settings(hinstance, get_name());
// General Settings.
settings.set_description(GET_RESOURCE_STRING(IDS_GENERAL_DESCRIPTION));
settings.set_icon_key(GET_RESOURCE_STRING(IDS_ICON_KEY_NAME));
// Preview Pane: Settings Group Header.
settings.add_header_szLarge(
GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID),
GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC),
GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT));
for (FileExplorerPreviewSettings * previewHandler : this->m_previewHandlers)
{
settings.add_bool_toogle(
previewHandler->GetName(),
previewHandler->GetDescription(),
previewHandler->GetState());
}
return settings.serialize_to_buffer(buffer, buffer_size);
}
// Called by the runner to pass the updated settings values as a serialized JSON.
void PowerPreviewModule::set_config(const wchar_t* config)
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config);
for (FileExplorerPreviewSettings * previewHandler : this->m_previewHandlers)
{
previewHandler->UpdateState(values);
}
values.save_to_settings_file();
}
catch (std::exception const& e)
{
Trace::SetConfigInvalidJSON(e.what());
}
}
// Enable preview handlers.
void PowerPreviewModule::enable()
{
init_settings();
this->m_enabled = true;
}
// Disable all preview handlers.
void PowerPreviewModule::disable()
{
for (FileExplorerPreviewSettings * previewHandler : this->m_previewHandlers)
{
previewHandler->DisablePreview();
}
this->m_enabled = false;
}
// Returns if the powertoys is enabled
bool PowerPreviewModule::is_enabled()
{
return this->m_enabled;
}
// Handle incoming event, data is event-specific
intptr_t PowerPreviewModule::signal_event(const wchar_t* name, intptr_t data)
{
return 0;
}
// Load the settings file.
void PowerPreviewModule::init_settings()
{
try
{
// Load and parse the settings file for this PowerToy.
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(PowerPreviewModule::get_name());
// Load settings states.
for (FileExplorerPreviewSettings * previewHandler : this->m_previewHandlers)
{
previewHandler->LoadState(settings);
}
}
catch (std::exception const& e)
{
Trace::InitSetErrorLoadingFile(e.what());
}
}

View file

@ -0,0 +1,2 @@
EXPORTS
DllGetClassObject PRIVATE

View file

@ -0,0 +1,59 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <common.h>
#include "trace.h"
#include "settings.h"
#include "registry_wrapper.h"
using namespace PowerPreviewSettings;
extern "C" IMAGE_DOS_HEADER __ImageBase;
// Implement the PowerToy Module Interface and all the required methods.
class PowerPreviewModule : public PowertoyModuleIface
{
private:
// The PowerToy state.
bool m_enabled = false;
std::wstring m_moduleName;
std::vector<FileExplorerPreviewSettings *> m_previewHandlers;
public:
PowerPreviewModule() :
m_moduleName(GET_RESOURCE_STRING(IDS_MODULE_NAME)),
m_previewHandlers(
{ // SVG Preview Hanlder settings object.
new FileExplorerPreviewSettings(
false,
GET_RESOURCE_STRING(IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL),
GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION),
L"{ddee2b8a-6807-48a6-bb20-2338174ff779}",
GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME),
new RegistryWrapper()),
// MarkDown Preview Handler Settings Object.
new FileExplorerPreviewSettings(
false,
GET_RESOURCE_STRING(IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL),
GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION),
L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}",
GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME),
new RegistryWrapper())
})
{
init_settings();
};
virtual void destroy();
virtual const wchar_t* get_name();
virtual const wchar_t** get_events();
virtual bool get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size);
virtual void set_config(const wchar_t* config);
virtual void enable();
virtual void disable();
virtual bool is_enabled();
virtual void init_settings();
virtual intptr_t signal_event(const wchar_t* name, intptr_t data);
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
};

View file

@ -0,0 +1,78 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
STRINGTABLE
BEGIN
IDS_GENERAL_DESCRIPTION L"These settings allow you to manage your Windows File Explorer Addons.";
IDS_MODULE_NAME L"File Explorer Preview";
IDS_ICON_KEY_NAME L"pt-power-preview";
IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID L"EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID";
IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_DESC L"Settings Group Header Text";
IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_TEXT L"Explorer Icons";
IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID L"PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID";
IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC L"Settings Group Header Text";
IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT L"Preview Pane";
IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL L"PREVPANE_MD_BOOL_TOGGLE_CONTROLL_ID"
IDS_PREVPANE_MD_SETTINGS_DESCRIPTION L"Markdown Preview Handler"
IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME L"Markdown Preview Handler"
IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL L"IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL"
IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION L"Svg Preview Handler"
IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME L"SVG Preview Handler"
IDS_EXPLR_SVG_BOOL_TOGGLE_CONTROLL L"EXPLR_SVG_BOOL_TOGGLE_CONTROLL"
IDS_EXPLR_SVG_SETTINGS_DESCRIPTION L"Render SVG images"
END
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View file

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{217DF501-135C-4E38-BFC8-99D4821032EA}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>examplepowertoy</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>powerpreview</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<ModuleDefinitionFile>powerpreview.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\..\common;..\..\common\inc;..\common\Telemetry;..\;..\..\;..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<ModuleDefinitionFile>powerpreview.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="CLSID.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="registry_wrapper.h" />
<ClInclude Include="registry_wrapper_interface.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="settings.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="powerpreview.cpp" />
<ClCompile Include="powerpreview.h" />
<ClCompile Include="registry_wrapper.cpp" />
<ClCompile Include="settings.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="powerpreview.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\common.vcxproj">
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="powerpreview.def" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="settings.cpp" />
<ClCompile Include="powerpreview.h" />
<ClCompile Include="powerpreview.cpp" />
<ClCompile Include="registry_wrapper.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="settings.h" />
<ClInclude Include="CLSID.h" />
<ClInclude Include="registry_wrapper.h" />
<ClInclude Include="registry_wrapper_interface.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="powerpreview.rc" />
</ItemGroup>
<ItemGroup>
<None Include="powerpreview.def" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,64 @@
#include <pch.h>
#include "registry_wrapper.h"
namespace PowerPreviewSettings
{
LONG RegistryWrapper::SetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, CONST BYTE* data, DWORD cbData)
{
HKEY OpenResult;
LONG err = RegOpenKeyExW(keyScope, subKey, 0, KEY_WRITE, &OpenResult);
if (err == ERROR_SUCCESS)
{
err = RegSetValueExW(
OpenResult,
valueName,
0, // This parameter is reserved and must be zero.
dwType,
data,
cbData);
RegCloseKey(OpenResult);
}
return err;
}
LONG RegistryWrapper::GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData)
{
HKEY OpenResult;
LONG err = RegOpenKeyExW(keyScope, subKey, 0, KEY_READ, &OpenResult);
if (err == ERROR_SUCCESS)
{
err = RegGetValueW(
OpenResult,
NULL,
valueName,
RRF_RT_ANY,
pdwType,
pvData,
pcbData);
RegCloseKey(OpenResult);
}
return err;
}
LONG RegistryWrapper::DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName)
{
HKEY OpenResult;
LONG err = RegOpenKeyExW(keyScope, subKey, 0, KEY_WRITE, &OpenResult);
if (err == ERROR_SUCCESS)
{
err = RegDeleteKeyValueW(
OpenResult,
NULL,
valueName);
RegCloseKey(OpenResult);
}
return err;
}
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "registry_wrapper_interface.h"
namespace PowerPreviewSettings
{
class RegistryWrapper : public RegistryWrapperIface
{
public:
virtual LONG SetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, CONST BYTE* data, DWORD cbData);
virtual LONG DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName);
virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData);
};
}

View file

@ -0,0 +1,14 @@
#pragma once
class RegistryWrapperIface
{
public:
// Sets a registry value under the mentioned scope(HKCR, HKLM, etc).
virtual LONG SetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, CONST BYTE* data, DWORD cbData) = 0;
// Delete a registry value.
virtual LONG DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName) = 0;
// Reads a registry value.
virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) = 0;
};

View file

@ -0,0 +1,32 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by powerpreview.rc
#define IDS_GENERAL_DESCRIPTION 200
#define IDS_MODULE_NAME 201
#define IDS_ICON_KEY_NAME 202
#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_HEADER_ID 203
#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_DESC 204
#define IDS_EXPLR_ICONS_PREV_STTNGS_GROUP_TEXT 205
#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID 206
#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC 207
#define IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT 208
#define IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL 209
#define IDS_PREVPANE_MD_SETTINGS_DESCRIPTION 210
#define IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL 211
#define IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION 212
#define IDS_EXPLR_SVG_BOOL_TOGGLE_CONTROLL 213
#define IDS_EXPLR_SVG_SETTINGS_DESCRIPTION 214
#define IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME 215
#define IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME 216
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View file

@ -0,0 +1,146 @@
#include "pch.h"
#include <common.h>
#include "settings.h"
#include "trace.h"
#include <iostream>
#include <atlstr.h>
using namespace std;
namespace PowerPreviewSettings
{
extern "C" IMAGE_DOS_HEADER __ImageBase;
// Relative(HKLM/HKCU) sub key path of Preview Handlers list in registry.
static LPCWSTR preview_handlers_subkey = L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers";
// Base Settinngs Class Implementation
FileExplorerPreviewSettings::FileExplorerPreviewSettings(bool enabled, const std::wstring& name, const std::wstring& description, LPCWSTR clsid, const std::wstring& displayname, RegistryWrapperIface * registryWrapper) :
m_isPreviewEnabled(enabled),
m_name(name),
m_description(description),
m_clsid(clsid),
m_displayName(displayname),
m_registryWrapper(registryWrapper)
{
}
FileExplorerPreviewSettings::~FileExplorerPreviewSettings()
{
if (this->m_registryWrapper != NULL)
{
delete this->m_registryWrapper;
}
}
bool FileExplorerPreviewSettings::GetState() const
{
return this->m_isPreviewEnabled;
}
void FileExplorerPreviewSettings::SetState(bool state)
{
this->m_isPreviewEnabled = state;
}
void FileExplorerPreviewSettings::LoadState(PowerToysSettings::PowerToyValues& settings)
{
auto toggle = settings.get_bool_value(this->GetName());
if (toggle != std::nullopt)
{
this->m_isPreviewEnabled = toggle.value();
}
}
void FileExplorerPreviewSettings::UpdateState(PowerToysSettings::PowerToyValues& values)
{
auto toggle = values.get_bool_value(this->GetName());
if (toggle != std::nullopt)
{
if (toggle.value())
{
this->EnablePreview();
}
else
{
this->DisablePreview();
}
}
else
{
Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str());
}
}
std::wstring FileExplorerPreviewSettings::GetName() const
{
return this->m_name;
}
void FileExplorerPreviewSettings::SetName(const std::wstring& name)
{
this->m_name = name;
}
std::wstring FileExplorerPreviewSettings::GetDescription() const
{
return this->m_description;
}
void FileExplorerPreviewSettings::SetDescription(const std::wstring& description)
{
this->m_description = description;
}
LPCWSTR FileExplorerPreviewSettings::GetSubKey() const
{
return preview_handlers_subkey;
}
LPCWSTR FileExplorerPreviewSettings::GetCLSID() const
{
return this->m_clsid;
}
std::wstring FileExplorerPreviewSettings::GetDisplayName() const
{
return this->m_displayName;
}
void FileExplorerPreviewSettings::SetDisplayName(const std::wstring& displayName)
{
this->m_displayName = displayName;
}
void FileExplorerPreviewSettings::EnablePreview()
{
// Add registry value to enable preview.
LONG err = this->m_registryWrapper->SetRegistryValue(HKEY_CURRENT_USER, this->GetSubKey(), this->GetCLSID(), REG_SZ, (LPBYTE)this->GetDisplayName().c_str(), (DWORD)(this->GetDisplayName().length() * sizeof(wchar_t)));
if (err == ERROR_SUCCESS)
{
this->SetState(true);
Trace::PreviewHandlerEnabled(true, this->GetDisplayName().c_str());
}
else
{
Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str());
}
}
void FileExplorerPreviewSettings::DisablePreview()
{
// Delete the registry key to disable preview.
LONG err = this->m_registryWrapper->DeleteRegistryValue(HKEY_CURRENT_USER, this->GetSubKey(), this->GetCLSID());
if (err == ERROR_SUCCESS)
{
this->SetState(false);
Trace::PreviewHandlerEnabled(false, this->GetDisplayName().c_str());
}
else
{
Trace::PowerPreviewSettingsUpDateFailed(this->GetName().c_str());
}
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <pch.h>
#include <string>
#include "resource.h"
#include <settings_objects.h>
#include "registry_wrapper_interface.h"
namespace PowerPreviewSettings
{
// PowerToy Winodws Explore File Preview Settings.
class FileExplorerPreviewSettings
{
private:
bool m_isPreviewEnabled;
std::wstring m_name;
std::wstring m_description;
std::wstring m_displayName;
RegistryWrapperIface * m_registryWrapper;
LPCWSTR m_clsid;
public:
FileExplorerPreviewSettings(bool enabled, const std::wstring& name, const std::wstring& description, LPCWSTR clsid, const std::wstring& displayname, RegistryWrapperIface* registryWrapper);
~ FileExplorerPreviewSettings();
virtual bool GetState() const;
virtual void SetState(bool state);
virtual void LoadState(PowerToysSettings::PowerToyValues& settings);
virtual void UpdateState(PowerToysSettings::PowerToyValues& values);
virtual std::wstring GetName() const;
virtual void SetName(const std::wstring& name);
virtual std::wstring GetDescription() const;
virtual void SetDescription(const std::wstring& description);
virtual void SetDisplayName(const std::wstring& displayName);
virtual std::wstring GetDisplayName() const;
virtual LPCWSTR GetCLSID() const;
virtual LPCWSTR GetSubKey() const;
virtual void EnablePreview();
virtual void DisablePreview();
};
}

View file

@ -0,0 +1,86 @@
#include "pch.h"
#include "trace.h"
#include <string>
/*
*
* This file captures the telemetry for the File Explorer Custom Renders project.
* The following telemetry is to be captured for this library:
* (1.) Is the previewer enabled.
* (2.) File rendered per user in 24 hrs per file time (one for MD, one for SVG)
* (3.) Crashes.
*
*/
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider()
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider()
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::PreviewHandlerEnabled(bool enabled, LPCWSTR previewHandlerName)
{
TraceLoggingWrite(
g_hProvider,
"PowerPreview_TweakUISettings_Enabled",
TraceLoggingWideString(previewHandlerName, "PreviewHanlder_FileType"),
TraceLoggingBoolean(enabled, "Enabled"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::PowerPreviewSettingsUpDateFailed(LPCWSTR SettingsName)
{
TraceLoggingWrite(
g_hProvider,
"PowerPreview_TweakUISettings_FailedUpdatingSettings",
TraceLoggingWideString(SettingsName, "ExceptionMessage"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::SetConfigInvalidJSON(const char* exceptionMessage)
{
TraceLoggingWrite(
g_hProvider,
"PowerPreview_TweakUISettings_SetConfig__InvalidJSONGiven",
TraceLoggingString(exceptionMessage, "ExceptionMessage"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::Destroyed()
{
TraceLoggingWrite(
g_hProvider,
"PowerPreview_TweakUISettings_Destroyed",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::InitSetErrorLoadingFile(const char* exceptionMessage)
{
TraceLoggingWrite(
g_hProvider,
"PowerPreview_TweakUISettings_InitSet__ErrorLoadingFile",
TraceLoggingString(exceptionMessage, "ExceptionMessage"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View file

@ -0,0 +1,13 @@
#pragma once
class Trace
{
public:
static void RegisterProvider();
static void UnregisterProvider();
static void SetConfigInvalidJSON(const char* exceptionMessage);
static void InitSetErrorLoadingFile(const char* exceptionMessage);
static void PreviewHandlerEnabled(bool enabled, LPCWSTR previewHandlerName);
static void PowerPreviewSettingsUpDateFailed(LPCWSTR SettingsName);
static void Destroyed();
};

View file

@ -0,0 +1,229 @@
#include "pch.h"
#include "CppUnitTest.h"
#include <settings_objects.h>
#include <powerpreview/settings.cpp>
#include <powerpreview/trace.cpp>
#include <common.h>
#include <powerpreview/registry_wrapper.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace PowerToysSettings;
using namespace PowerPreviewSettings;
namespace PreviewHandlerSettingsTest
{
extern "C" IMAGE_DOS_HEADER __ImageBase;
struct FunctionProperties
{
public:
LONG ReturnValue = ERROR_SUCCESS;
int NumOfCalls = 0;
HKEY Scope;
LPCWSTR SubKey;
LPCWSTR ValueName;
};
class RegistryMock : public RegistryWrapperIface
{
public:
FunctionProperties SetRegistryMockProperties;
FunctionProperties DeleteRegistryMockProperties;
FunctionProperties GetRegistryMockProperties;
LONG SetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, CONST BYTE* data, DWORD cbData)
{
SetRegistryMockProperties.NumOfCalls += 1;
SetRegistryMockProperties.Scope = keyScope;
SetRegistryMockProperties.SubKey = subKey;
SetRegistryMockProperties.ValueName = valueName;
return SetRegistryMockProperties.ReturnValue;
}
LONG DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName)
{
DeleteRegistryMockProperties.NumOfCalls++;
DeleteRegistryMockProperties.Scope = keyScope;
DeleteRegistryMockProperties.SubKey = subKey;
DeleteRegistryMockProperties.ValueName = valueName;
return DeleteRegistryMockProperties.ReturnValue;
}
LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData)
{
GetRegistryMockProperties.NumOfCalls++;
GetRegistryMockProperties.Scope = keyScope;
GetRegistryMockProperties.SubKey = subKey;
GetRegistryMockProperties.ValueName = valueName;
return GetRegistryMockProperties.ReturnValue;
}
};
TEST_CLASS(BaseSettingsTest)
{
public:
TEST_METHOD(LoadState_ShouldLoadNewState_WhenSucessfull)
{
// Arrange
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(new RegistryMock());
PowerToyValues values = PowerToyValues::from_json_string(GetJSONSettings(tempSettings.GetName(), L"true"));
tempSettings.SetState(false);
bool expectedState = true;
// Act
tempSettings.LoadState(values);
bool actualState = tempSettings.GetState();
// Assert
Assert::AreEqual(actualState, expectedState);
}
TEST_METHOD(UpdateState_ShouldChangeState_WhenSucessfull)
{
// Arrange
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(new RegistryMock());
PowerToyValues values = PowerToyValues::from_json_string(GetJSONSettings(tempSettings.GetName(), L"true"));
tempSettings.SetState(false);
bool expectedState = true;
// Act
tempSettings.UpdateState(values);
bool actualState = tempSettings.GetState();
// Assert
Assert::AreEqual(actualState, expectedState);
}
TEST_METHOD(EnableRender_ShouldUpdateStateToTrue_WhenSuccessful)
{
// Arrange
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(new RegistryMock());
tempSettings.SetState(false); //preview handler initially disabled
// Act
tempSettings.EnablePreview();
// Assert
Assert::IsTrue(tempSettings.GetState());
}
TEST_METHOD(DisableRender_ShouldUpdateStateToFalse_WhenSuccessful)
{
// Arrange
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(new RegistryMock());
tempSettings.SetState(true); //preview handler initially enabled
// Act
tempSettings.DisablePreview();
// Assert
Assert::IsFalse(tempSettings.GetState());
}
TEST_METHOD(EnablePreview_ShouldCallSetRegistryValueWithValidArguments_WhenCalled)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
// Act
tempSettings.EnablePreview();
// Assert
Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 1);
Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.SubKey, tempSettings.GetSubKey());
Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.ValueName, tempSettings.GetCLSID());
Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->SetRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CURRENT_USER));
}
TEST_METHOD(EnablePreview_ShouldNotSetStateToTrue_IfSetRegistryValueFailed)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
mockRegistryWrapper->SetRegistryMockProperties.ReturnValue = ERROR_OUTOFMEMORY;
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
tempSettings.SetState(false);
// Act
tempSettings.EnablePreview();
// Assert
Assert::IsFalse(tempSettings.GetState());
}
TEST_METHOD(EnablePreview_ShouldSetStateToTrue_IfSetRegistryValueReturnSuccessErrorCode)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
tempSettings.SetState(false);
// Act
tempSettings.EnablePreview();
// Assert
Assert::IsTrue(tempSettings.GetState());
}
TEST_METHOD(DisablePreview_ShouldCallDeleteRegistryValueWithValidArguments_WhenCalled)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
// Act
tempSettings.DisablePreview();
// Assert
Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 1);
Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.SubKey, tempSettings.GetSubKey());
Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.ValueName, tempSettings.GetCLSID());
Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->DeleteRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CURRENT_USER));
}
TEST_METHOD(DisablePreview_ShouldNotSetStateToFalse_IfDeleteRegistryValueFailed)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
mockRegistryWrapper->DeleteRegistryMockProperties.ReturnValue = ERROR_OUTOFMEMORY;
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
tempSettings.SetState(true);
// Act
tempSettings.DisablePreview();
// Assert
Assert::IsTrue(tempSettings.GetState());
}
TEST_METHOD(DisablePreview_ShouldSetStateToFalse_IfDeleteRegistryValueReturnSuccessErrorCode)
{
// Arrange
RegistryMock* mockRegistryWrapper = new RegistryMock();
FileExplorerPreviewSettings tempSettings = GetSttingsObjects(mockRegistryWrapper);
tempSettings.SetState(true);
// Act
tempSettings.DisablePreview();
// Assert
Assert::IsFalse(tempSettings.GetState());
}
FileExplorerPreviewSettings GetSttingsObjects(RegistryMock * registryMock)
{
return FileExplorerPreviewSettings(
false,
GET_RESOURCE_STRING(IDS_PREVPANE_MD_BOOL_TOGGLE_CONTROLL),
GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION),
L"{test-guid}",
TEXT("Test Handler\0"),
registryMock);
}
std::wstring GetJSONSettings(const std::wstring &_settingsNameId, const std::wstring &_value) const
{
return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }";
}
};
}

View file

@ -0,0 +1,3 @@
#include "pch.h"
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "shlwapi.lib")

View file

@ -0,0 +1,15 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include <Shlwapi.h>
#include <windows.h>
#include "CppUnitTest.h"
#endif //PCH_H

View file

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{47310AB4-9034-4BD1-8D8B-E88AD21A171B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>powerpreviewTest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;..\..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UseFullPaths>true</UseFullPaths>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UseFullPaths>true</UseFullPaths>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UseFullPaths>true</UseFullPaths>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;..\..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UseFullPaths>true</UseFullPaths>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="FileExplorerPreviewSettingsTest.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\common.vcxproj">
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,30 @@
<?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;hh;hpp;hxx;hm;inl;inc;ipp;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;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FileExplorerPreviewSettingsTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -175,7 +175,8 @@ int runner(bool isProcessElevated)
std::unordered_set<std::wstring> known_dlls = {
L"shortcut_guide.dll",
L"fancyzones.dll",
L"PowerRenameExt.dll"
L"PowerRenameExt.dll",
L"powerpreview.dll"
};
for (auto& file : std::filesystem::directory_iterator(L"modules/"))
{

View file

@ -1139,9 +1139,9 @@
}
},
"@microsoft/load-themed-styles": {
"version": "1.10.26",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.26.tgz",
"integrity": "sha512-N//pFTBL/iCSrMuDoLvBLpgGjlk+MgKX2kyFI3bJVb+LRozeyWCOZVRcR8aMKiYHdqwy5isu2Frp8drvWx7RbA=="
"version": "1.10.33",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.33.tgz",
"integrity": "sha512-6ke2PEMbjizIvqhSeb/K6iUStEJcfONndE1kfB46dstFYZ9jJmYgO6UHUZkA3VMufmjdI1z28bJKz3cMzmkZGw=="
},
"@microsoft/package-deps-hash": {
"version": "2.2.159",
@ -1422,25 +1422,54 @@
}
},
"@uifabric/foundation": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.5.0.tgz",
"integrity": "sha512-eymMyV3e+MFCkcfC1AFIAzVP/h6/QvDcYb1l6K3IaG1QG47ZwijJJXseEvNDjimfUiJhez9H7cSsRZPIIJ5MaQ==",
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.5.2.tgz",
"integrity": "sha512-TNDVWjVDV/UbGOMbCsRblDY6yciTL9GPMOT1b4Ibuul6ytUVLaUCRchdnMf+8JIUNBwZIDMMJWwDVpdwURqpvQ==",
"requires": {
"@uifabric/merge-styles": "^7.8.0",
"@uifabric/set-version": "^7.0.2",
"@uifabric/styling": "^7.7.2",
"@uifabric/utilities": "^7.5.0",
"tslib": "^1.7.1"
"@uifabric/merge-styles": "^7.8.2",
"@uifabric/set-version": "^7.0.3",
"@uifabric/styling": "^7.10.1",
"@uifabric/utilities": "^7.11.2",
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/merge-styles": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz",
"integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==",
"requires": {
"@uifabric/set-version": "^7.0.3",
"tslib": "^1.10.0"
}
},
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"@uifabric/icons": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.0.tgz",
"integrity": "sha512-wbcR8fJce20sPjsK2bbTC/cAZfAOFuE4dd4LHw194+8H+/dqotsowrQVp5Lu8aaHGQk+fXoiZmUy30WA9cAG4Q==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.1.tgz",
"integrity": "sha512-Jb2KjC6s+nY75H7U8/i23iKRwYFS3cavTXVYDz69guwLEhWh0aZ1QZqbAbUfF+YikVabJQmT8YT0Pup8/VbeWA==",
"requires": {
"@uifabric/set-version": "^7.0.2",
"@uifabric/styling": "^7.7.1",
"tslib": "^1.7.1"
"@uifabric/set-version": "^7.0.3",
"@uifabric/styling": "^7.10.1",
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"@uifabric/merge-styles": {
@ -1453,13 +1482,23 @@
}
},
"@uifabric/react-hooks": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.0.1.tgz",
"integrity": "sha512-cIr/ToLvc48D7A+XJrH/rHmSa/YmonvyFGHykFqHKiFSYiGKvc50GIyRJ/gkOUDuaaYVThWRwBQNOIMr3iFCYA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.0.2.tgz",
"integrity": "sha512-Ly2loVgrSJ3VYHvyOp6Q23aieOcX3w80Cf8t8+gXRZjLXgNh39omOhucD1nVnSlnUy+w88vDhr2aC1dCiw/o7w==",
"requires": {
"@uifabric/set-version": "^7.0.2",
"@uifabric/utilities": "^7.0.10",
"tslib": "^1.7.1"
"@uifabric/set-version": "^7.0.3",
"@uifabric/utilities": "^7.11.2",
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"@uifabric/set-version": {
@ -1471,26 +1510,64 @@
}
},
"@uifabric/styling": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.8.0.tgz",
"integrity": "sha512-mRNQUvfasOWW0/RJARA5mPHmPMORJXrNDoOpjoTNt+J6uj1/sA8km0l/AQtZ6b36bqo6kkaQgB9+msRobmdpiQ==",
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.10.1.tgz",
"integrity": "sha512-/G0BbIS1tI1SEiUD4mNxfNXq6dLyff6Ey5YqeMGx6v1AyQ2dWBolhGSYK8l7XGlBQCNZ+gR+AV4M6+8gWOkAaw==",
"requires": {
"@microsoft/load-themed-styles": "^1.7.13",
"@uifabric/merge-styles": "^7.8.1",
"@uifabric/set-version": "^7.0.2",
"@uifabric/utilities": "^7.8.0",
"tslib": "^1.7.1"
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/merge-styles": "^7.8.2",
"@uifabric/set-version": "^7.0.3",
"@uifabric/utilities": "^7.11.2",
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/merge-styles": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz",
"integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==",
"requires": {
"@uifabric/set-version": "^7.0.3",
"tslib": "^1.10.0"
}
},
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"@uifabric/utilities": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.8.0.tgz",
"integrity": "sha512-ow3v9arBhe5C8GvklIuqD3PZMSNnY04HC7JoQVW301Q/gH+BGVECLTBzrFm/m66GCetMksDOMnkoBQSk0poPOQ==",
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.11.2.tgz",
"integrity": "sha512-PZnBsV3fyS6FiqZlP7byBosRNvmiIg9B8TLRl0YVQYr4LDDpLE05HKYxLLrp7SepPX9sxOh5AXgwrgDPojAkpg==",
"requires": {
"@uifabric/merge-styles": "^7.8.1",
"@uifabric/set-version": "^7.0.2",
"@uifabric/merge-styles": "^7.8.2",
"@uifabric/set-version": "^7.0.3",
"prop-types": "^15.5.10",
"tslib": "^1.7.1"
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/merge-styles": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz",
"integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==",
"requires": {
"@uifabric/set-version": "^7.0.3",
"tslib": "^1.10.0"
}
},
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"@webassemblyjs/ast": {
@ -8476,20 +8553,39 @@
"dev": true
},
"office-ui-fabric-react": {
"version": "7.76.2",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.76.2.tgz",
"integrity": "sha512-rWQrkv1o4jOdYhNk3GsLDxJw8mo1+6HspGNILiodRj7RfJx/OmSyVM4KT2gGJ5k2PZGNCdp7f7kp1JuICd31Mg==",
"version": "7.83.1",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.83.1.tgz",
"integrity": "sha512-XDL+9baCqq8kDA1VHrAlRvfnWKEQRJVx9hlzsbFNxzMpZtXNH0ip4SHzahRRU0eqCXHBNkd5ZA4xUV532Uv8dQ==",
"requires": {
"@microsoft/load-themed-styles": "^1.7.13",
"@uifabric/foundation": "^7.5.0",
"@uifabric/icons": "^7.3.0",
"@uifabric/merge-styles": "^7.8.1",
"@uifabric/react-hooks": "^7.0.1",
"@uifabric/set-version": "^7.0.2",
"@uifabric/styling": "^7.8.0",
"@uifabric/utilities": "^7.8.0",
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/foundation": "^7.5.2",
"@uifabric/icons": "^7.3.1",
"@uifabric/merge-styles": "^7.8.2",
"@uifabric/react-hooks": "^7.0.2",
"@uifabric/set-version": "^7.0.3",
"@uifabric/styling": "^7.10.1",
"@uifabric/utilities": "^7.11.2",
"prop-types": "^15.5.10",
"tslib": "^1.7.1"
"tslib": "^1.10.0"
},
"dependencies": {
"@uifabric/merge-styles": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.8.2.tgz",
"integrity": "sha512-CiGZkOQegNdrXIaVvgd8pumeHLm3odSRE21rsrA7HiqdyF+fh6ArQ0RWsKTlJRIQklCUJjMkXaO54QIWNOlQyg==",
"requires": {
"@uifabric/set-version": "^7.0.3",
"tslib": "^1.10.0"
}
},
"@uifabric/set-version": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.3.tgz",
"integrity": "sha512-03A68Fyfx3y75dUW9rjQ2fZv/9zmGgMeovVLAQa0wc/oVjQ++eVDlAEK0AjfgnOaujYmhk79lXbYAuW3n+YUXw==",
"requires": {
"tslib": "^1.10.0"
}
}
}
},
"on-finished": {

View file

@ -30,7 +30,8 @@
"dependencies": {
"@svgr/webpack": "^4.3.3",
"@uifabric/azure-themes": "^7.0.10",
"office-ui-fabric-react": "^7.76.2",
"@uifabric/styling": "^7.10.1",
"office-ui-fabric-react": "^7.83.1",
"react": "~16.8.0",
"react-dom": "~16.8.0"
},

View file

@ -163,6 +163,10 @@ export class ModuleSettings extends React.Component <any, any> {
on_change={this.parent_on_change}
ref={(input) => {this.references[key]=input;}}
/>;
case 'header_large':
return <Text
variant='large'
className="SubHeader">{power_toys_properties[key].value}</Text>;
default:
return null;
}

View file

@ -58,3 +58,6 @@
flex-direction: column;
flex-grow: 1;
}
.SubHeader{
font-weight: bold;
}

View file

@ -72,6 +72,10 @@
{
"name": "CircleRing",
"unicode": "EA3A"
},
{
"name": "FabricReportLibrary",
"unicode": "F0A1"
}
]
}

View file

@ -3,7 +3,7 @@
*/
@font-face {
font-family: 'FabricMDL2Icons';
src: url('data:application/octet-stream;base64,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') format('truetype');
src: url('data:application/octet-stream;base64,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') format('truetype');
}
.ms-Icon {
@ -32,3 +32,4 @@
.ms-Icon--ChevronLeftMed:before { content: "\E973"; }
.ms-Icon--ChevronRightMed:before { content: "\E974"; }
.ms-Icon--CircleRing:before { content: "\EA3A"; }
.ms-Icon--FabricReportLibrary:before { content: "\F0A1"; }

View file

@ -32,3 +32,4 @@
.ms-Icon--ChevronLeftMed:before { content: "\E973"; }
.ms-Icon--ChevronRightMed:before { content: "\E974"; }
.ms-Icon--CircleRing:before { content: "\EA3A"; }
.ms-Icon--FabricReportLibrary:before { content: "\F0A1"; }

View file

@ -271,6 +271,19 @@
</div>
</div>
<div class="IconContainer" title="FabricReportLibrary - F0A1">
<div class="IconContainer-icon">
<i class="ms-Icon ms-Icon--FabricReportLibrary"></i>
</div>
<div class="IconContainer-name ms-font-l">
FabricReportLibrary
</div>
<div class="IconContainer-unicode ms-font-s">
F0A1
</div>
</div>
</div>
</body>
</html>

View file

@ -3,7 +3,7 @@
*/
@font-face {
font-family: 'FabricMDL2Icons';
src: url('data:application/octet-stream;base64,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') format('truetype');
src: url('data:application/octet-stream;base64,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') format('truetype');
}
.ms-Icon {
@ -33,6 +33,7 @@
@mixin ms-Icon--ChevronLeftMed { content: "\E973"; }
@mixin ms-Icon--ChevronRightMed { content: "\E974"; }
@mixin ms-Icon--CircleRing { content: "\EA3A"; }
@mixin ms-Icon--FabricReportLibrary { content: "\F0A1"; }
// Classes
@ -52,4 +53,5 @@
.ms-Icon--ChevronLeftMed:before { @include ms-Icon--ChevronLeftMed }
.ms-Icon--ChevronRightMed:before { @include ms-Icon--ChevronRightMed }
.ms-Icon--CircleRing:before { @include ms-Icon--CircleRing }
.ms-Icon--FabricReportLibrary:before { @include ms-Icon--FabricReportLibrary }

View file

@ -33,6 +33,7 @@
@mixin ms-Icon--ChevronLeftMed { content: "\E973"; }
@mixin ms-Icon--ChevronRightMed { content: "\E974"; }
@mixin ms-Icon--CircleRing { content: "\EA3A"; }
@mixin ms-Icon--FabricReportLibrary { content: "\F0A1"; }
// Classes
@ -52,4 +53,5 @@
.ms-Icon--ChevronLeftMed:before { @include ms-Icon--ChevronLeftMed }
.ms-Icon--ChevronRightMed:before { @include ms-Icon--ChevronRightMed }
.ms-Icon--CircleRing:before { @include ms-Icon--CircleRing }
.ms-Icon--FabricReportLibrary:before { @include ms-Icon--FabricReportLibrary }

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more