diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 3acc79d4b1..1222c66e66 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -63,6 +63,7 @@ APPIDS appium Applets Applicationcan +applicationconfiguration applicationframehost Applist applog @@ -87,9 +88,11 @@ ARRAYSIZE arsinh artanh Artsakh +asd asdf AShortcut ASingle +Asn ASSOCCHANGED ASYNCWINDOWPLACEMENT ASYNCWINDOWPOS @@ -158,8 +161,8 @@ bpmf bpp bricelam BRIGHTGREEN +Brotli Browsable -brucelindbloom bsd bstr bti @@ -199,9 +202,8 @@ CHILDACTIVATE CHILDWINDOW Choibalsan chrdavis -chromaticities Chrzan -CHT +cht Chukotka Chuuk CIELAB @@ -220,6 +222,10 @@ CLIPCHILDREN Clipperton CLIPSIBLINGS clrcall +clrcompression +clretwrc +clrgc +clrjit Cls CLSCTX clsid @@ -234,6 +240,7 @@ CMock CMONITORS cmpgt cmyk +Cng cnt Cocklebiddy coclass @@ -254,13 +261,11 @@ comctl COMDAT comdef comdlg -comhost cominterop commandline COMMANDTITLE commctrl Comoros -companding COMPOSITIONFULL comsupp comsuppw @@ -281,6 +286,7 @@ CONTROLL CONTROLPARENT Controlz copiedcolorrepresentation +coreclr corewebview cortana cotaskmem @@ -325,12 +331,10 @@ cwd cxfksword CXSMICON CXVIRTUALSCREEN -cxxopts cyberrex CYSMICON CYVIRTUALSCREEN cziplib -cziplob Dac dacl damienleroy @@ -346,6 +350,7 @@ davidegiacometti Dayof Dbg Dbghelp +dbgshim DBLCLKS DBLEPSILON DCapture @@ -354,6 +359,7 @@ DCOM dcommon dcomp dcompi +DCompiler DComposition DCR DDevice @@ -388,7 +394,7 @@ DESKTOPABSOLUTEEDITING DESKTOPABSOLUTEPARSING desktopshorcutinstalled desktopwindowxamlsource -DEU +deu devblogs devdocs devenum @@ -476,12 +482,11 @@ ENABLEDPOPUP endpointvolume endregion ENTERSIZEMOVE -ENU +enu enumerationoptions EOAC epicgames epu -Eqn ERASEBKGND EREOF EResize @@ -490,6 +495,7 @@ ERRORLEVEL ERRORTITLE ESettings esize +esn esrp Eswatini etl @@ -533,7 +539,6 @@ Faroe FARPROC fdw feimage -ffaa fff fileapi FILEEXPLORER @@ -679,6 +684,8 @@ HOMEPATH homljgmgpmcbpjbnjpfijnhipfkiclkd HOOKPROC Hostbackdropbrush +hostfxr +hostpolicy hotkeycontrol hotkeys hotlight @@ -750,6 +757,7 @@ IMAGERESIZEREXT imageresizerinput imageresizersettings imagingdevices +Imc ime imeutil inetcpl @@ -786,17 +794,15 @@ Intelli interactable Interlop INTRESOURCE +Intrinsics INVALIDARG invalidoperatioexception -iobjectwithsitesetsite -iolewindowcontextsensitivehelp ipc ipcmanager IPlugin IPower -ipreview +IPREVIEW ipreviewhandler -ipreviewhandlertranslateaccelerator ipreviewhandlervisualssetfont IProperty IPublic @@ -824,7 +830,7 @@ jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi jjw jobject jpe -JPN +jpn jpnime JSONOf Jsons @@ -847,7 +853,6 @@ keyevent KEYEVENTF keynum keyremaps -keystokes Keytool keyup Khakassia @@ -906,6 +911,7 @@ lmcons LMEM LMENU lnk +LOADFROMFILE LOADLIBRARYASDATAFILE LOBYTE LOCALAPPDATA @@ -917,6 +923,7 @@ LOCATIONCHANGE logconsole logfile LOGFONT +LOGFONTW LOGMSG logon LOGPIXELSX @@ -983,6 +990,8 @@ MAPPEDTOSAMEKEY MAPTOSAMESHORTCUT MAPVK Markdig +markdownpreviewhandler +MARKDOWNPREVIEWHANDLERCPP Markovic Marquesas martinchrzan @@ -1015,6 +1024,7 @@ Metadatas metafile mfapi mfc +mfcm mfidl mfobjects mfplat @@ -1074,8 +1084,11 @@ mru msbuild msc msclr +mscordaccore +mscordbi mscoree mscorlib +mscorrc msdata msedge MSGFLT @@ -1088,6 +1101,7 @@ MSIXCA MSLLHOOKSTRUCT Mso msp +msquic msrc msstore mst @@ -1137,6 +1151,7 @@ netcpl netframework netsetup netsh +netstandard Neue newcolor newdev @@ -1313,6 +1328,7 @@ pinvoke pipename Pitcairn PKBDLLHOOKSTRUCT +Pkcs PKEY plib PLK @@ -1413,6 +1429,7 @@ QUERYENDSESSION queryfocus QUERYOPEN QUEUESYNC +Quic Quickime QUNS qwertyuiopasdfghjklzxcvbnm @@ -1530,7 +1547,7 @@ runtimeclass runtimeconfig runtimeobject runtimes -RUS +rus Rutkas RValue rvm @@ -1574,7 +1591,6 @@ SETFOCUS SETFOREGROUND SETICON setlocal -Setrect SETREDRAW SETTEXT SETTINGCHANGE @@ -1682,6 +1698,8 @@ srw srwlock sse ssf +Ssl +sss STACKFRAME stackoverflow stackpanel @@ -1799,6 +1817,7 @@ THH THICKFRAME THISCOMPONENT THotkey +thumbcache TILEDWINDOW timedate timediff @@ -1873,7 +1892,7 @@ uniquifier Uniquifies unitconverter unittests -unk +Unk unknwn UNLEN Unmap @@ -2008,7 +2027,6 @@ WINDOWPOSCHANGING Windowsapp WINDOWSBUILDNUMBER Windowscodecs -windowsdesktop windowssearch windowssettings WINDOWSTYLES @@ -2071,6 +2089,7 @@ workspaces wox wparam wpf +wpfgfx wpftmp wpr wprp diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index dac486b7a2..af84380a24 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -46,26 +46,35 @@ "modules\\FancyZones\\PowerToys.FancyZones.exe", "modules\\FileExplorerPreview\\PowerToys.GcodePreviewHandler.dll", - "modules\\FileExplorerPreview\\PowerToys.GcodePreviewHandler.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.GcodePreviewHandler.exe", + "modules\\FileExplorerPreview\\PowerToys.GcodePreviewHandlerCpp.dll", "modules\\FileExplorerPreview\\PowerToys.GcodeThumbnailProvider.dll", - "modules\\FileExplorerPreview\\PowerToys.GcodeThumbnailProvider.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.GcodeThumbnailProvider.exe", + "modules\\FileExplorerPreview\\PowerToys.GcodeThumbnailProviderCpp.dll", "modules\\FileExplorerPreview\\PowerToys.ManagedTelemetry.dll", "modules\\FileExplorerPreview\\PowerToys.MarkdownPreviewHandler.dll", - "modules\\FileExplorerPreview\\PowerToys.MarkdownPreviewHandler.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.MarkdownPreviewHandler.exe", + "modules\\FileExplorerPreview\\PowerToys.MarkdownPreviewHandlerCpp.dll", "modules\\FileExplorerPreview\\PowerToys.MonacoPreviewHandler.dll", - "modules\\FileExplorerPreview\\PowerToys.MonacoPreviewHandler.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.MonacoPreviewHandler.exe", + "modules\\FileExplorerPreview\\PowerToys.MonacoPreviewHandlerCpp.dll", "modules\\FileExplorerPreview\\PowerToys.PdfPreviewHandler.dll", - "modules\\FileExplorerPreview\\PowerToys.PdfPreviewHandler.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.PdfPreviewHandler.exe", + "modules\\FileExplorerPreview\\PowerToys.PdfPreviewHandlerCpp.dll", "modules\\FileExplorerPreview\\PowerToys.PdfThumbnailProvider.dll", - "modules\\FileExplorerPreview\\PowerToys.PdfThumbnailProvider.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.PdfThumbnailProvider.exe", + "modules\\FileExplorerPreview\\PowerToys.PdfThumbnailProviderCpp.dll", "modules\\FileExplorerPreview\\PowerToys.powerpreview.dll", "modules\\FileExplorerPreview\\PowerToys.PreviewHandlerCommon.dll", "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProvider.dll", - "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProvider.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProvider.exe", + "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProviderCpp.dll", "modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandler.dll", - "modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandler.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandler.exe", + "modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandlerCpp.dll", "modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.dll", - "modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.comhost.dll", + "modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.exe", + "modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProviderCpp.dll", "modules\\Hosts\\PowerToys.HostsModuleInterface.dll", "modules\\Hosts\\PowerToys.Hosts.dll", @@ -238,6 +247,7 @@ "modules\\launcher\\Interop.Microsoft.Office.Interop.OneNote.dll", "modules\\launcher\\hyjiacan.py4n.dll", "Settings\\Microsoft.Graphics.Canvas.Interop.dll", + "Settings\\clrcompression.dll", "Settings\\CommunityToolkit.Labs.WinUI.SettingsControls.dll", "ColorCode.Core.dll", "ColorCode.UWP.dll", diff --git a/.pipelines/ci/templates/build-powertoys-ci.yml b/.pipelines/ci/templates/build-powertoys-ci.yml index b403ecdb30..20107c8063 100644 --- a/.pipelines/ci/templates/build-powertoys-ci.yml +++ b/.pipelines/ci/templates/build-powertoys-ci.yml @@ -9,6 +9,7 @@ jobs: variables: BuildConfiguration: ${{ parameters.configuration }} BuildPlatform: ${{ parameters.platform }} + NUGET_RESTORE_MSBUILD_ARGS: /p:Platform=${{ parameters.platform }} # Required for nuget to work due to self contained pool: demands: ImageOverride -equals WinDevVS17-latest ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: diff --git a/.pipelines/release.yml b/.pipelines/release.yml index cb6b6615e4..cab7398de9 100644 --- a/.pipelines/release.yml +++ b/.pipelines/release.yml @@ -39,6 +39,7 @@ jobs: ${{ config }}_${{ platform }}: BuildConfiguration: ${{ config }} BuildPlatform: ${{ platform }} + NUGET_RESTORE_MSBUILD_ARGS: /p:Platform=${{ platform }} # Required for nuget to work due to self contained displayName: Build timeoutInMinutes: 120 # Some of the loc stuff adds quite a bit of time. cancelTimeoutInMinutes: 1 diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 0e27375355..168f7d0c25 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -62,7 +62,8 @@ $items | ForEach-Object { (-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.Bootstrap.dll")) -and (-not $_.Name.EndsWith("MRM.dll")) -and (-not $_.Name.EndsWith("PushNotificationsLongRunningTask.ProxyStub.dll")) -and - (-not $_.Name.EndsWith("WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll")) + (-not $_.Name.EndsWith("WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll")) -and + (-not $_.Name.EndsWith("System.Diagnostics.EventLog.Messages.dll")) ) { Write-Host "Version not set: " + $_.FullName diff --git a/PowerToys.sln b/PowerToys.sln index 1d85f27dde..7583cc9b36 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -468,6 +468,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPOWrapper", "src\common\GP EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPOWrapperProjection", "src\common\GPOWrapperProjection\GPOWrapperProjection.csproj", "{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MarkdownPreviewHandlerCpp", "src\modules\previewpane\MarkdownPreviewHandlerCpp\MarkdownPreviewHandlerCpp.vcxproj", "{ED9A1AC6-AEB0-4569-A6E9-E1696182B545}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GcodePreviewHandlerCpp", "src\modules\previewpane\GcodePreviewHandlerCpp\GcodePreviewHandlerCpp.vcxproj", "{5A5DD09D-723A-44D3-8F2B-293584C3D731}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MonacoPreviewHandlerCpp", "src\modules\previewpane\MonacoPreviewHandlerCpp\MonacoPreviewHandlerCpp.vcxproj", "{B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PdfPreviewHandlerCpp", "src\modules\previewpane\PdfPreviewHandlerCpp\PdfPreviewHandlerCpp.vcxproj", "{54F7C616-FD41-4E62-BFF9-015686914F4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgPreviewHandlerCpp", "src\modules\previewpane\SvgPreviewHandlerCpp\SvgPreviewHandlerCpp.vcxproj", "{143F13E3-D2E3-4D83-B035-356612D99956}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GcodeThumbnailProviderCpp", "src\modules\previewpane\GcodeThumbnailProviderCpp\GcodeThumbnailProviderCpp.vcxproj", "{56CC2F10-6E41-453D-BE16-C593A5E58482}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PdfThumbnailProviderCpp", "src\modules\previewpane\PdfThumbnailProviderCpp\PdfThumbnailProviderCpp.vcxproj", "{CA5518ED-0458-4B09-8F53-4122B9888655}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StlThumbnailProviderCpp", "src\modules\previewpane\StlThumbnailProviderCpp\StlThumbnailProviderCpp.vcxproj", "{D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -1895,6 +1913,114 @@ Global {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x64.Build.0 = Release|x64 {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x86.ActiveCfg = Release|x64 {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x86.Build.0 = Release|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|ARM64.Build.0 = Debug|ARM64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|x64.ActiveCfg = Debug|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|x64.Build.0 = Debug|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|x86.ActiveCfg = Debug|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Debug|x86.Build.0 = Debug|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|ARM64.ActiveCfg = Release|ARM64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|ARM64.Build.0 = Release|ARM64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|x64.ActiveCfg = Release|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|x64.Build.0 = Release|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|x86.ActiveCfg = Release|x64 + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545}.Release|x86.Build.0 = Release|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|ARM64.Build.0 = Debug|ARM64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|x64.ActiveCfg = Debug|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|x64.Build.0 = Debug|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|x86.ActiveCfg = Debug|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Debug|x86.Build.0 = Debug|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|ARM64.ActiveCfg = Release|ARM64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|ARM64.Build.0 = Release|ARM64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|x64.ActiveCfg = Release|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|x64.Build.0 = Release|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|x86.ActiveCfg = Release|x64 + {5A5DD09D-723A-44D3-8F2B-293584C3D731}.Release|x86.Build.0 = Release|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|ARM64.Build.0 = Debug|ARM64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|x64.ActiveCfg = Debug|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|x64.Build.0 = Debug|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|x86.ActiveCfg = Debug|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Debug|x86.Build.0 = Debug|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|ARM64.ActiveCfg = Release|ARM64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|ARM64.Build.0 = Release|ARM64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|x64.ActiveCfg = Release|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|x64.Build.0 = Release|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|x86.ActiveCfg = Release|x64 + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9}.Release|x86.Build.0 = Release|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|ARM64.Build.0 = Debug|ARM64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|x64.ActiveCfg = Debug|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|x64.Build.0 = Debug|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|x86.ActiveCfg = Debug|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Debug|x86.Build.0 = Debug|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|ARM64.ActiveCfg = Release|ARM64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|ARM64.Build.0 = Release|ARM64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|x64.ActiveCfg = Release|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|x64.Build.0 = Release|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|x86.ActiveCfg = Release|x64 + {54F7C616-FD41-4E62-BFF9-015686914F4D}.Release|x86.Build.0 = Release|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|ARM64.Build.0 = Debug|ARM64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|x64.ActiveCfg = Debug|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|x64.Build.0 = Debug|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|x86.ActiveCfg = Debug|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Debug|x86.Build.0 = Debug|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|ARM64.ActiveCfg = Release|ARM64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|ARM64.Build.0 = Release|ARM64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|x64.ActiveCfg = Release|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|x64.Build.0 = Release|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|x86.ActiveCfg = Release|x64 + {143F13E3-D2E3-4D83-B035-356612D99956}.Release|x86.Build.0 = Release|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|ARM64.Build.0 = Debug|ARM64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|x64.ActiveCfg = Debug|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|x64.Build.0 = Debug|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|x86.ActiveCfg = Debug|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Debug|x86.Build.0 = Debug|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|ARM64.ActiveCfg = Release|ARM64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|ARM64.Build.0 = Release|ARM64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|x64.ActiveCfg = Release|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|x64.Build.0 = Release|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|x86.ActiveCfg = Release|x64 + {56CC2F10-6E41-453D-BE16-C593A5E58482}.Release|x86.Build.0 = Release|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|ARM64.Build.0 = Debug|ARM64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|x64.ActiveCfg = Debug|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|x64.Build.0 = Debug|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|x86.ActiveCfg = Debug|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Debug|x86.Build.0 = Debug|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|ARM64.ActiveCfg = Release|ARM64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|ARM64.Build.0 = Release|ARM64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|x64.ActiveCfg = Release|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|x64.Build.0 = Release|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|x86.ActiveCfg = Release|x64 + {CA5518ED-0458-4B09-8F53-4122B9888655}.Release|x86.Build.0 = Release|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|ARM64.Build.0 = Debug|ARM64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|x64.ActiveCfg = Debug|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|x64.Build.0 = Debug|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|x86.ActiveCfg = Debug|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Debug|x86.Build.0 = Debug|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|ARM64.ActiveCfg = Release|ARM64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|ARM64.Build.0 = Release|ARM64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|x64.ActiveCfg = Release|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|x64.Build.0 = Release|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|x86.ActiveCfg = Release|x64 + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D}.Release|x86.Build.0 = Release|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|ARM64.Build.0 = Debug|ARM64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|x64.ActiveCfg = Debug|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|x64.Build.0 = Debug|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|x86.ActiveCfg = Debug|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Debug|x86.Build.0 = Debug|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|ARM64.ActiveCfg = Release|ARM64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|ARM64.Build.0 = Release|ARM64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.ActiveCfg = Release|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64 + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2056,6 +2182,15 @@ Global {C604B37E-9D0E-4484-8778-E8B31B0E1B3A} = {AB82E5DD-C32D-4F28-9746-2C780846188E} {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788} = {1AFB6476-670D-4E80-A464-657E01DFF482} {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97} = {1AFB6476-670D-4E80-A464-657E01DFF482} + {ED9A1AC6-AEB0-4569-A6E9-E1696182B545} = {2F305555-C296-497E-AC20-5FA1B237996A} + {5A5DD09D-723A-44D3-8F2B-293584C3D731} = {2F305555-C296-497E-AC20-5FA1B237996A} + {B3E869C4-8210-4EBD-A621-FF4C4AFCBFA9} = {2F305555-C296-497E-AC20-5FA1B237996A} + {54F7C616-FD41-4E62-BFF9-015686914F4D} = {2F305555-C296-497E-AC20-5FA1B237996A} + {143F13E3-D2E3-4D83-B035-356612D99956} = {2F305555-C296-497E-AC20-5FA1B237996A} + {56CC2F10-6E41-453D-BE16-C593A5E58482} = {2F305555-C296-497E-AC20-5FA1B237996A} + {CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A} + {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A} + {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/PowerToys.wxs b/installer/PowerToysSetup/PowerToys.wxs index f3e5cfeac5..0818170273 100644 --- a/installer/PowerToysSetup/PowerToys.wxs +++ b/installer/PowerToysSetup/PowerToys.wxs @@ -6,21 +6,13 @@ - - - - - - - - - + @@ -41,7 +33,6 @@ SuppressRepair="yes" /> - @@ -73,26 +64,6 @@ PerMachine="yes" Vital="no"> - - - - - + - + - + + + + + - + @@ -117,7 +121,7 @@ - + @@ -125,9 +129,9 @@ - + - + @@ -139,7 +143,19 @@ - + + + + + + + + + + + + + + + NOT Installed and CREATESCHEDULEDTASK = 1 @@ -250,6 +268,9 @@ NOT Installed + + NOT Installed + @@ -275,6 +296,10 @@ Installed AND (REMOVE="ALL") + + Installed AND (REMOVE="ALL") + + + + + + + @@ -758,6 +806,18 @@ + + + + + + + + + + + + @@ -1056,8 +1116,8 @@ - - + + @@ -1076,8 +1136,8 @@ - - + + @@ -1086,8 +1146,8 @@ - - + + @@ -1384,8 +1444,9 @@ - - + + + @@ -2066,7 +2127,7 @@ - + diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index 7434fe3af7..faa67c0931 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -15,6 +15,8 @@ #include #include +#include "DepsFilesLists.h" + using namespace std; HINSTANCE DLL_HANDLE = nullptr; @@ -32,56 +34,6 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0' static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; -const std::vector winAppSdkFiles = { - L"CoreMessagingXP.dll", - L"DWriteCore.dll", - L"DwmSceneI.dll", - L"MRM.dll", - L"Microsoft.DirectManipulation.dll", - L"Microsoft.InputStateManager.dll", - L"Microsoft.Internal.FrameworkUdk.dll", - L"Microsoft.UI.Composition.OSSupport.dll", - L"Microsoft.UI.Input.dll", - L"Microsoft.UI.Windowing.Core.dll", - L"Microsoft.UI.Xaml.Controls.dll", - L"Microsoft.UI.Xaml.Controls.pri", - L"Microsoft.UI.Xaml.Internal.dll", - L"Microsoft.UI.Xaml.Phone.dll", - L"Microsoft.Web.WebView2.Core.dll", - L"Microsoft.Windows.AppNotifications.Projection.dll", - L"Microsoft.Windows.ApplicationModel.Resources.dll", - L"Microsoft.WindowsAppRuntime.Bootstrap.dll", - L"Microsoft.Windows.PushNotifications.Projection.dll", - L"Microsoft.Windows.System.Projection.dll", - L"Microsoft.WindowsAppRuntime.Insights.Resource.dll", - L"Microsoft.WindowsAppRuntime.Release.Net.dll", - L"Microsoft.WindowsAppRuntime.dll", - L"Microsoft.ui.xaml.dll", - L"Microsoft.ui.xaml.resources.19h1.dll", - L"Microsoft.ui.xaml.resources.common.dll", - L"PushNotificationsLongRunningTask.ProxyStub.dll", - L"WinUIEdit.dll", - L"WindowsAppRuntime.png", - L"WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll", - L"dcompi.dll", - L"dwmcorei.dll", - L"marshal.dll", - L"wuceffectsi.dll" }; - -const std::vector powerToysInteropFiles = { - L"concrt140.dll", - L"msvcp140.dll", - L"msvcp140_1.dll", - L"msvcp140_2.dll", - L"msvcp140_atomic_wait.dll", - L"msvcp140_codecvt_ids.dll", - L"PowerToys.Interop.dll", - L"vcamp140.dll", - L"vccorlib140.dll", - L"vcomp140.dll", - L"vcruntime140.dll", - L"vcruntime140_1.dll" }; - struct WcaSink : spdlog::sinks::base_sink { virtual void sink_it_(const spdlog::details::log_msg& msg) override @@ -1103,6 +1055,7 @@ const std::wstring PTInteropConsumers[] = L"modules\\PowerAccent", L"modules\\FileLocksmith", L"modules\\Hosts", + L"modules\\FileExplorerPreview", }; UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall) @@ -1141,6 +1094,87 @@ LExit: return WcaFinalize(er); } +UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + std::wstring installationFolder, dotnetRuntimeFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, + imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir; + + hr = WcaInitialize(hInstall, "CreateDotnetRuntimeHardlinksCA"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = getInstallFolder(hInstall, installationFolder); + ExitOnFailure(hr, "Failed to get installation folder"); + + dotnetRuntimeFilesSrcDir = installationFolder + L"dll\\dotnet\\"; + colorPickerDir = installationFolder + L"modules\\ColorPicker\\"; + powerOCRDir = installationFolder + L"modules\\PowerOCR\\"; + launcherDir = installationFolder + L"modules\\launcher\\"; + fancyZonesDir = installationFolder + L"modules\\FancyZones\\"; + imageResizerDir = installationFolder + L"modules\\ImageResizer\\"; + settingsDir = installationFolder + L"Settings\\"; + awakeDir = installationFolder + L"modules\\Awake\\"; + measureToolDir = installationFolder + L"modules\\MeasureTool\\"; + powerAccentDir = installationFolder + L"modules\\PowerAccent\\"; + fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\"; + hostsDir = installationFolder + L"modules\\Hosts\\"; + fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; + + for (auto file : dotnetRuntimeFiles) + { + std::error_code ec; + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (colorPickerDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileLocksmithDir + file).c_str(), ec); + + if (ec.value() != S_OK) + { + std::wstring errorMessage{ L"Error creating hard link for: " }; + errorMessage += file; + errorMessage += L", error code: " + std::to_wstring(ec.value()); + Logger::error(errorMessage); + er = ERROR_INSTALL_FAILURE; + } + } + + for (auto file : dotnetRuntimeWPFFiles) + { + std::error_code ec; + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (colorPickerDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); + + if (ec.value() != S_OK) + { + std::wstring errorMessage{ L"Error creating hard link for: " }; + errorMessage += file; + errorMessage += L", error code: " + std::to_wstring(ec.value()); + Logger::error(errorMessage); + er = ERROR_INSTALL_FAILURE; + } + } + + LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall) { HRESULT hr = S_OK; @@ -1213,6 +1247,78 @@ LExit: return WcaFinalize(er); } +UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + std::wstring installationFolder, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, + imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, + hostsDir, fileLocksmithDir; + + hr = WcaInitialize(hInstall, "DeleteDotnetRuntimeHardlinksCA"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = getInstallFolder(hInstall, installationFolder); + ExitOnFailure(hr, "Failed to get installation folder"); + + colorPickerDir = installationFolder + L"modules\\ColorPicker\\"; + powerOCRDir = installationFolder + L"modules\\PowerOCR\\"; + launcherDir = installationFolder + L"modules\\launcher\\"; + fancyZonesDir = installationFolder + L"modules\\FancyZones\\"; + imageResizerDir = installationFolder + L"modules\\ImageResizer\\"; + settingsDir = installationFolder + L"Settings\\"; + awakeDir = installationFolder + L"modules\\Awake\\"; + measureToolDir = installationFolder + L"modules\\MeasureTool\\"; + powerAccentDir = installationFolder + L"modules\\PowerAccent\\"; + fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\"; + hostsDir = installationFolder + L"modules\\Hosts\\"; + fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; + + try + { + for (auto file : dotnetRuntimeFiles) + { + DeleteFile((colorPickerDir + file).c_str()); + DeleteFile((powerOCRDir + file).c_str()); + DeleteFile((launcherDir + file).c_str()); + DeleteFile((fancyZonesDir + file).c_str()); + DeleteFile((imageResizerDir + file).c_str()); + DeleteFile((settingsDir + file).c_str()); + DeleteFile((awakeDir + file).c_str()); + DeleteFile((measureToolDir + file).c_str()); + DeleteFile((powerAccentDir + file).c_str()); + DeleteFile((fileExplorerAddOnsDir + file).c_str()); + DeleteFile((hostsDir + file).c_str()); + DeleteFile((fileLocksmithDir + file).c_str()); + } + + for (auto file : dotnetRuntimeWPFFiles) + { + DeleteFile((awakeDir + file).c_str()); + DeleteFile((colorPickerDir + file).c_str()); + DeleteFile((powerOCRDir + file).c_str()); + DeleteFile((launcherDir + file).c_str()); + DeleteFile((fancyZonesDir + file).c_str()); + DeleteFile((imageResizerDir + file).c_str()); + DeleteFile((powerAccentDir + file).c_str()); + DeleteFile((fileExplorerAddOnsDir + file).c_str()); + DeleteFile((hostsDir + file).c_str()); + } + } + catch (std::exception e) + { + std::string errorMessage{ "Exception thrown while trying to delete dotnet runtime hardlinks: " }; + errorMessage += e.what(); + Logger::error(errorMessage); + + er = ERROR_INSTALL_FAILURE; + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) { HRESULT hr = S_OK; diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index 3a36f02f7d..27d59b2fda 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -7,6 +7,8 @@ EXPORTS DeleteWinAppSDKHardlinksCA CreatePTInteropHardlinksCA DeletePTInteropHardlinksCA + CreateDotnetRuntimeHardlinksCA + DeleteDotnetRuntimeHardlinksCA DetectPrevInstallPathCA RemoveScheduledTasksCA TelemetryLogInstallSuccessCA diff --git a/installer/PowerToysSetupCustomActions/DepsFilesLists.h b/installer/PowerToysSetupCustomActions/DepsFilesLists.h new file mode 100644 index 0000000000..de7e4e0b7d --- /dev/null +++ b/installer/PowerToysSetupCustomActions/DepsFilesLists.h @@ -0,0 +1,535 @@ +#pragma once + +#include +#include + +inline const std::vector winAppSdkFiles = { + L"CoreMessagingXP.dll", + L"DWriteCore.dll", + L"DwmSceneI.dll", + L"MRM.dll", + L"Microsoft.DirectManipulation.dll", + L"Microsoft.InputStateManager.dll", + L"Microsoft.Internal.FrameworkUdk.dll", + L"Microsoft.UI.Composition.OSSupport.dll", + L"Microsoft.UI.Input.dll", + L"Microsoft.UI.Windowing.Core.dll", + L"Microsoft.UI.Xaml.Controls.dll", + L"Microsoft.UI.Xaml.Controls.pri", + L"Microsoft.UI.Xaml.Internal.dll", + L"Microsoft.UI.Xaml.Phone.dll", + L"Microsoft.Web.WebView2.Core.dll", + L"Microsoft.Windows.AppNotifications.Projection.dll", + L"Microsoft.Windows.ApplicationModel.Resources.dll", + L"Microsoft.WindowsAppRuntime.Bootstrap.dll", + L"Microsoft.Windows.PushNotifications.Projection.dll", + L"Microsoft.Windows.System.Projection.dll", + L"Microsoft.WindowsAppRuntime.Insights.Resource.dll", + L"Microsoft.WindowsAppRuntime.Release.Net.dll", + L"Microsoft.WindowsAppRuntime.dll", + L"Microsoft.ui.xaml.dll", + L"Microsoft.ui.xaml.resources.19h1.dll", + L"Microsoft.ui.xaml.resources.common.dll", + L"PushNotificationsLongRunningTask.ProxyStub.dll", + L"WinUIEdit.dll", + L"WindowsAppRuntime.png", + L"WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll", + L"dcompi.dll", + L"dwmcorei.dll", + L"marshal.dll", + L"wuceffectsi.dll" +}; + +inline const std::vector powerToysInteropFiles = { + L"concrt140.dll", + L"msvcp140.dll", + L"msvcp140_1.dll", + L"msvcp140_2.dll", + L"msvcp140_atomic_wait.dll", + L"msvcp140_codecvt_ids.dll", + L"PowerToys.Interop.dll", + L"vcamp140.dll", + L"vccorlib140.dll", + L"vcomp140.dll", + L"vcruntime140.dll", + L"vcruntime140_1.dll" +}; + +#ifdef _M_X64 +inline const std::vector dotnetRuntimeFiles = { + L"clrcompression.dll", + L"clretwrc.dll", + L"clrgc.dll", + L"clrjit.dll", + L"coreclr.dll", + L"hostfxr.dll", + L"hostpolicy.dll", + L"Microsoft.CSharp.dll", + L"Microsoft.DiaSymReader.Native.amd64.dll", + L"Microsoft.VisualBasic.Core.dll", + L"Microsoft.VisualBasic.dll", + L"Microsoft.Win32.Primitives.dll", + L"Microsoft.Win32.Registry.dll", + L"mscordaccore.dll", + L"mscordbi.dll", + L"mscorlib.dll", + L"mscorrc.dll", + L"msquic.dll", + L"netstandard.dll", + L"System.AppContext.dll", + L"System.Buffers.dll", + L"System.CodeDom.dll", + L"System.Collections.Concurrent.dll", + L"System.Collections.dll", + L"System.Collections.Immutable.dll", + L"System.Collections.NonGeneric.dll", + L"System.Collections.Specialized.dll", + L"System.ComponentModel.Annotations.dll", + L"System.ComponentModel.DataAnnotations.dll", + L"System.ComponentModel.dll", + L"System.ComponentModel.EventBasedAsync.dll", + L"System.ComponentModel.Primitives.dll", + L"System.ComponentModel.TypeConverter.dll", + L"System.Configuration.dll", + L"System.Console.dll", + L"System.Core.dll", + L"System.Data.Common.dll", + L"System.Data.DataSetExtensions.dll", + L"System.Data.dll", + L"System.Diagnostics.Contracts.dll", + L"System.Diagnostics.Debug.dll", + L"System.Diagnostics.DiagnosticSource.dll", + L"System.Diagnostics.FileVersionInfo.dll", + L"System.Diagnostics.Process.dll", + L"System.Diagnostics.StackTrace.dll", + L"System.Diagnostics.TextWriterTraceListener.dll", + L"System.Diagnostics.Tools.dll", + L"System.Diagnostics.TraceSource.dll", + L"System.Diagnostics.Tracing.dll", + L"System.dll", + L"System.Drawing.dll", + L"System.Drawing.Primitives.dll", + L"System.Dynamic.Runtime.dll", + L"System.Formats.Asn1.dll", + L"System.Formats.Tar.dll", + L"System.Globalization.Calendars.dll", + L"System.Globalization.dll", + L"System.Globalization.Extensions.dll", + L"System.IO.Compression.Brotli.dll", + L"System.IO.Compression.dll", + L"System.IO.Compression.FileSystem.dll", + L"System.IO.Compression.Native.dll", + L"System.IO.Compression.ZipFile.dll", + L"System.IO.dll", + L"System.IO.FileSystem.AccessControl.dll", + L"System.IO.FileSystem.dll", + L"System.IO.FileSystem.DriveInfo.dll", + L"System.IO.FileSystem.Primitives.dll", + L"System.IO.FileSystem.Watcher.dll", + L"System.IO.IsolatedStorage.dll", + L"System.IO.MemoryMappedFiles.dll", + L"System.IO.Pipes.AccessControl.dll", + L"System.IO.Pipes.dll", + L"System.IO.UnmanagedMemoryStream.dll", + L"System.Linq.dll", + L"System.Linq.Expressions.dll", + L"System.Linq.Parallel.dll", + L"System.Linq.Queryable.dll", + L"System.Memory.dll", + L"System.Net.dll", + L"System.Net.Http.dll", + L"System.Net.Http.Json.dll", + L"System.Net.HttpListener.dll", + L"System.Net.Mail.dll", + L"System.Net.NameResolution.dll", + L"System.Net.NetworkInformation.dll", + L"System.Net.Ping.dll", + L"System.Net.Primitives.dll", + L"System.Net.Quic.dll", + L"System.Net.Requests.dll", + L"System.Net.Security.dll", + L"System.Net.ServicePoint.dll", + L"System.Net.Sockets.dll", + L"System.Net.WebClient.dll", + L"System.Net.WebHeaderCollection.dll", + L"System.Net.WebProxy.dll", + L"System.Net.WebSockets.Client.dll", + L"System.Net.WebSockets.dll", + L"System.Numerics.dll", + L"System.Numerics.Vectors.dll", + L"System.ObjectModel.dll", + L"System.Private.CoreLib.dll", + L"System.Private.DataContractSerialization.dll", + L"System.Private.Uri.dll", + L"System.Private.Xml.dll", + L"System.Private.Xml.Linq.dll", + L"System.Reflection.DispatchProxy.dll", + L"System.Reflection.dll", + L"System.Reflection.Emit.dll", + L"System.Reflection.Emit.ILGeneration.dll", + L"System.Reflection.Emit.Lightweight.dll", + L"System.Reflection.Extensions.dll", + L"System.Reflection.Metadata.dll", + L"System.Reflection.Primitives.dll", + L"System.Reflection.TypeExtensions.dll", + L"System.Resources.Reader.dll", + L"System.Resources.ResourceManager.dll", + L"System.Resources.Writer.dll", + L"System.Runtime.CompilerServices.Unsafe.dll", + L"System.Runtime.CompilerServices.VisualC.dll", + L"System.Runtime.dll", + L"System.Runtime.Extensions.dll", + L"System.Runtime.Handles.dll", + L"System.Runtime.InteropServices.dll", + L"System.Runtime.InteropServices.JavaScript.dll", + L"System.Runtime.InteropServices.RuntimeInformation.dll", + L"System.Runtime.Intrinsics.dll", + L"System.Runtime.Loader.dll", + L"System.Runtime.Numerics.dll", + L"System.Runtime.Serialization.dll", + L"System.Runtime.Serialization.Formatters.dll", + L"System.Runtime.Serialization.Json.dll", + L"System.Runtime.Serialization.Primitives.dll", + L"System.Runtime.Serialization.Xml.dll", + L"System.Security.AccessControl.dll", + L"System.Security.Claims.dll", + L"System.Security.Cryptography.dll", + L"System.Security.Cryptography.Algorithms.dll", + L"System.Security.Cryptography.Cng.dll", + L"System.Security.Cryptography.Csp.dll", + L"System.Security.Cryptography.Encoding.dll", + L"System.Security.Cryptography.OpenSsl.dll", + L"System.Security.Cryptography.Primitives.dll", + L"System.Security.Cryptography.X509Certificates.dll", + L"System.Security.dll", + L"System.Security.Principal.dll", + L"System.Security.Principal.Windows.dll", + L"System.Security.SecureString.dll", + L"System.ServiceModel.Web.dll", + L"System.ServiceProcess.dll", + L"System.Text.Encoding.CodePages.dll", + L"System.Text.Encoding.dll", + L"System.Text.Encoding.Extensions.dll", + L"System.Text.Encodings.Web.dll", + L"System.Text.RegularExpressions.dll", + L"System.Threading.Channels.dll", + L"System.Threading.dll", + L"System.Threading.Overlapped.dll", + L"System.Threading.Tasks.Dataflow.dll", + L"System.Threading.Tasks.dll", + L"System.Threading.Tasks.Extensions.dll", + L"System.Threading.Tasks.Parallel.dll", + L"System.Threading.Thread.dll", + L"System.Threading.ThreadPool.dll", + L"System.Threading.Timer.dll", + L"System.Transactions.dll", + L"System.Transactions.Local.dll", + L"System.ValueTuple.dll", + L"System.Web.dll", + L"System.Web.HttpUtility.dll", + L"System.Windows.dll", + L"System.Xml.dll", + L"System.Xml.Linq.dll", + L"System.Xml.ReaderWriter.dll", + L"System.Xml.Serialization.dll", + L"System.Xml.XDocument.dll", + L"System.Xml.XmlDocument.dll", + L"System.Xml.XmlSerializer.dll", + L"System.Xml.XPath.dll", + L"System.Xml.XPath.XDocument.dll" }; + +inline const std::vector dotnetRuntimeWPFFiles = { + L"Accessibility.dll", + L"D3DCompiler_47_cor3.dll", + L"DirectWriteForwarder.dll", + L"Microsoft.VisualBasic.Forms.dll", + L"Microsoft.Win32.Registry.AccessControl.dll", + L"Microsoft.Win32.SystemEvents.dll", + L"PenImc_cor3.dll", + L"PresentationCore.dll", + L"PresentationFramework-SystemCore.dll", + L"PresentationFramework-SystemData.dll", + L"PresentationFramework-SystemDrawing.dll", + L"PresentationFramework-SystemXml.dll", + L"PresentationFramework-SystemXmlLinq.dll", + L"PresentationFramework.Aero.dll", + L"PresentationFramework.Aero2.dll", + L"PresentationFramework.AeroLite.dll", + L"PresentationFramework.Classic.dll", + L"PresentationFramework.dll", + L"PresentationFramework.Luna.dll", + L"PresentationFramework.Royale.dll", + L"PresentationNative_cor3.dll", + L"PresentationUI.dll", + L"ReachFramework.dll", + L"System.Configuration.ConfigurationManager.dll", + L"System.Design.dll", + L"System.Diagnostics.EventLog.dll", + L"System.Diagnostics.EventLog.Messages.dll", + L"System.Diagnostics.PerformanceCounter.dll", + L"System.DirectoryServices.dll", + L"System.Drawing.Common.dll", + L"System.Drawing.Design.dll", + L"System.IO.Packaging.dll", + L"System.Printing.dll", + L"System.Resources.Extensions.dll", + L"System.Security.Cryptography.Pkcs.dll", + L"System.Security.Cryptography.ProtectedData.dll", + L"System.Security.Cryptography.Xml.dll", + L"System.Security.Permissions.dll", + L"System.Threading.AccessControl.dll", + L"System.Windows.Controls.Ribbon.dll", + L"System.Windows.Extensions.dll", + L"System.Windows.Forms.Design.dll", + L"System.Windows.Forms.Design.Editors.dll", + L"System.Windows.Forms.dll", + L"System.Windows.Forms.Primitives.dll", + L"System.Windows.Input.Manipulations.dll", + L"System.Windows.Presentation.dll", + L"System.Xaml.dll", + L"UIAutomationClient.dll", + L"UIAutomationClientSideProviders.dll", + L"UIAutomationProvider.dll", + L"UIAutomationTypes.dll", + L"vcruntime140_cor3.dll", + L"WindowsFormsIntegration.dll", + L"wpfgfx_cor3.dll" }; +#else //ARM64 +inline const std::vector dotnetRuntimeFiles = { + L"clretwrc.dll", + L"clrgc.dll", + L"clrjit.dll", + L"coreclr.dll", + L"dbgshim.dll", + L"hostfxr.dll", + L"hostpolicy.dll", + L"Microsoft.CSharp.dll", + L"Microsoft.DiaSymReader.Native.arm64.dll", + L"Microsoft.Graphics.Canvas.dll", + L"Microsoft.VisualBasic.Core.dll", + L"Microsoft.VisualBasic.dll", + L"Microsoft.Win32.Primitives.dll", + L"Microsoft.Win32.Registry.dll", + L"mscordaccore.dll", + L"mscordbi.dll", + L"mscorlib.dll", + L"mscorrc.dll", + L"netstandard.dll", + L"System.AppContext.dll", + L"System.Buffers.dll", + L"System.Collections.Concurrent.dll", + L"System.Collections.dll", + L"System.Collections.Immutable.dll", + L"System.Collections.NonGeneric.dll", + L"System.Collections.Specialized.dll", + L"System.ComponentModel.Annotations.dll", + L"System.ComponentModel.DataAnnotations.dll", + L"System.ComponentModel.dll", + L"System.ComponentModel.EventBasedAsync.dll", + L"System.ComponentModel.Primitives.dll", + L"System.ComponentModel.TypeConverter.dll", + L"System.Configuration.dll", + L"System.Console.dll", + L"System.Core.dll", + L"System.Data.Common.dll", + L"System.Data.DataSetExtensions.dll", + L"System.Data.dll", + L"System.Diagnostics.Contracts.dll", + L"System.Diagnostics.Debug.dll", + L"System.Diagnostics.DiagnosticSource.dll", + L"System.Diagnostics.FileVersionInfo.dll", + L"System.Diagnostics.Process.dll", + L"System.Diagnostics.StackTrace.dll", + L"System.Diagnostics.TextWriterTraceListener.dll", + L"System.Diagnostics.Tools.dll", + L"System.Diagnostics.TraceSource.dll", + L"System.Diagnostics.Tracing.dll", + L"System.dll", + L"System.Drawing.dll", + L"System.Drawing.Primitives.dll", + L"System.Dynamic.Runtime.dll", + L"System.Formats.Asn1.dll", + L"System.Formats.Tar.dll", + L"System.Globalization.Calendars.dll", + L"System.Globalization.dll", + L"System.Globalization.Extensions.dll", + L"System.IO.Compression.Brotli.dll", + L"System.IO.Compression.dll", + L"System.IO.Compression.FileSystem.dll", + L"System.IO.Compression.Native.dll", + L"System.IO.Compression.ZipFile.dll", + L"System.IO.dll", + L"System.IO.FileSystem.AccessControl.dll", + L"System.IO.FileSystem.dll", + L"System.IO.FileSystem.DriveInfo.dll", + L"System.IO.FileSystem.Primitives.dll", + L"System.IO.FileSystem.Watcher.dll", + L"System.IO.IsolatedStorage.dll", + L"System.IO.MemoryMappedFiles.dll", + L"System.IO.Pipes.AccessControl.dll", + L"System.IO.Pipes.dll", + L"System.IO.UnmanagedMemoryStream.dll", + L"System.Linq.dll", + L"System.Linq.Expressions.dll", + L"System.Linq.Parallel.dll", + L"System.Linq.Queryable.dll", + L"System.Memory.dll", + L"System.Net.dll", + L"System.Net.Http.dll", + L"System.Net.Http.Json.dll", + L"System.Net.HttpListener.dll", + L"System.Net.Mail.dll", + L"System.Net.NameResolution.dll", + L"System.Net.NetworkInformation.dll", + L"System.Net.Ping.dll", + L"System.Net.Primitives.dll", + L"System.Net.Quic.dll", + L"System.Net.Requests.dll", + L"System.Net.Security.dll", + L"System.Net.ServicePoint.dll", + L"System.Net.Sockets.dll", + L"System.Net.WebClient.dll", + L"System.Net.WebHeaderCollection.dll", + L"System.Net.WebProxy.dll", + L"System.Net.WebSockets.Client.dll", + L"System.Net.WebSockets.dll", + L"System.Numerics.dll", + L"System.Numerics.Vectors.dll", + L"System.ObjectModel.dll", + L"System.Private.CoreLib.dll", + L"System.Private.DataContractSerialization.dll", + L"System.Private.Uri.dll", + L"System.Private.Xml.dll", + L"System.Private.Xml.Linq.dll", + L"System.Reflection.DispatchProxy.dll", + L"System.Reflection.dll", + L"System.Reflection.Emit.dll", + L"System.Reflection.Emit.ILGeneration.dll", + L"System.Reflection.Emit.Lightweight.dll", + L"System.Reflection.Extensions.dll", + L"System.Reflection.Metadata.dll", + L"System.Reflection.Primitives.dll", + L"System.Reflection.TypeExtensions.dll", + L"System.Resources.Reader.dll", + L"System.Resources.ResourceManager.dll", + L"System.Resources.Writer.dll", + L"System.Runtime.CompilerServices.Unsafe.dll", + L"System.Runtime.CompilerServices.VisualC.dll", + L"System.Runtime.dll", + L"System.Runtime.Extensions.dll", + L"System.Runtime.Handles.dll", + L"System.Runtime.InteropServices.dll", + L"System.Runtime.InteropServices.JavaScript.dll", + L"System.Runtime.InteropServices.RuntimeInformation.dll", + L"System.Runtime.Intrinsics.dll", + L"System.Runtime.Loader.dll", + L"System.Runtime.Numerics.dll", + L"System.Runtime.Serialization.dll", + L"System.Runtime.Serialization.Formatters.dll", + L"System.Runtime.Serialization.Json.dll", + L"System.Runtime.Serialization.Primitives.dll", + L"System.Runtime.Serialization.Xml.dll", + L"System.Security.AccessControl.dll", + L"System.Security.Claims.dll", + L"System.Security.Cryptography.dll", + L"System.Security.Cryptography.Algorithms.dll", + L"System.Security.Cryptography.Cng.dll", + L"System.Security.Cryptography.Csp.dll", + L"System.Security.Cryptography.Encoding.dll", + L"System.Security.Cryptography.OpenSsl.dll", + L"System.Security.Cryptography.Primitives.dll", + L"System.Security.Cryptography.X509Certificates.dll", + L"System.Security.dll", + L"System.Security.Principal.dll", + L"System.Security.Principal.Windows.dll", + L"System.Security.SecureString.dll", + L"System.ServiceModel.Web.dll", + L"System.ServiceProcess.dll", + L"System.Text.Encoding.CodePages.dll", + L"System.Text.Encoding.dll", + L"System.Text.Encoding.Extensions.dll", + L"System.Text.Encodings.Web.dll", + L"System.Text.Json.dll", + L"System.Text.RegularExpressions.dll", + L"System.Threading.Channels.dll", + L"System.Threading.dll", + L"System.Threading.Overlapped.dll", + L"System.Threading.Tasks.Dataflow.dll", + L"System.Threading.Tasks.dll", + L"System.Threading.Tasks.Extensions.dll", + L"System.Threading.Tasks.Parallel.dll", + L"System.Threading.Thread.dll", + L"System.Threading.ThreadPool.dll", + L"System.Threading.Timer.dll", + L"System.Transactions.dll", + L"System.Transactions.Local.dll", + L"System.ValueTuple.dll", + L"System.Web.dll", + L"System.Web.HttpUtility.dll", + L"System.Windows.dll", + L"System.Xml.dll", + L"System.Xml.Linq.dll", + L"System.Xml.ReaderWriter.dll", + L"System.Xml.Serialization.dll", + L"System.Xml.XDocument.dll", + L"System.Xml.XmlDocument.dll", + L"System.Xml.XmlSerializer.dll", + L"System.Xml.XPath.dll", + L"System.Xml.XPath.XDocument.dll" }; + +inline const std::vector dotnetRuntimeWPFFiles = { + L"Accessibility.dll", + L"DirectWriteForwarder.dll", + L"Microsoft.VisualBasic.Forms.dll", + L"Microsoft.Win32.Registry.AccessControl.dll", + L"Microsoft.Win32.SystemEvents.dll", + L"PenImc_cor3.dll", + L"PresentationCore.dll", + L"PresentationFramework-SystemCore.dll", + L"PresentationFramework-SystemData.dll", + L"PresentationFramework-SystemDrawing.dll", + L"PresentationFramework-SystemXml.dll", + L"PresentationFramework-SystemXmlLinq.dll", + L"PresentationFramework.Aero.dll", + L"PresentationFramework.Aero2.dll", + L"PresentationFramework.AeroLite.dll", + L"PresentationFramework.Classic.dll", + L"PresentationFramework.dll", + L"PresentationFramework.Luna.dll", + L"PresentationFramework.Royale.dll", + L"PresentationNative_cor3.dll", + L"PresentationUI.dll", + L"ReachFramework.dll", + L"System.CodeDom.dll", + L"System.Configuration.ConfigurationManager.dll", + L"System.Design.dll", + L"System.Diagnostics.EventLog.dll", + L"System.Diagnostics.EventLog.Messages.dll", + L"System.Diagnostics.PerformanceCounter.dll", + L"System.DirectoryServices.dll", + L"System.Drawing.Common.dll", + L"System.Drawing.Design.dll", + L"System.IO.Packaging.dll", + L"System.Security.Cryptography.Pkcs.dll", + L"System.Security.Cryptography.ProtectedData.dll", + L"System.Security.Cryptography.Xml.dll", + L"System.Security.Permissions.dll", + L"System.Threading.AccessControl.dll", + L"System.Windows.Controls.Ribbon.dll", + L"System.Windows.Extensions.dll", + L"System.Windows.Forms.Design.dll", + L"System.Windows.Forms.Design.Editors.dll", + L"System.Windows.Forms.dll", + L"System.Windows.Forms.Primitives.dll", + L"System.Windows.Input.Manipulations.dll", + L"System.Windows.Presentation.dll", + L"System.Xaml.dll", + L"UIAutomationClient.dll", + L"UIAutomationClientSideProviders.dll", + L"UIAutomationProvider.dll", + L"UIAutomationTypes.dll", + L"vcruntime140_cor3.dll", + L"WindowsFormsIntegration.dll", + L"wpfgfx_cor3.dll" +}; +#endif diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index 19547da5cf..985d920f58 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -109,6 +109,7 @@ + diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj.filters b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj.filters index 7108f08409..71e80f20f2 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj.filters +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj.filters @@ -14,6 +14,7 @@ + diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index 83f48732f8..7d5bfe099f 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -207,5 +207,24 @@ public return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT); } + static String ^ GcodePreviewResizeEvent() { + return gcnew String(CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT); + } + + static String ^ DevFilesPreviewResizeEvent() { + return gcnew String(CommonSharedConstants::DEV_FILES_PREVIEW_RESIZE_EVENT); + } + + static String ^ MarkdownPreviewResizeEvent() { + return gcnew String(CommonSharedConstants::MARKDOWN_PREVIEW_RESIZE_EVENT); + } + + static String ^ PdfPreviewResizeEvent() { + return gcnew String(CommonSharedConstants::PDF_PREVIEW_RESIZE_EVENT); + } + + static String ^ SvgPreviewResizeEvent() { + return gcnew String(CommonSharedConstants::SVG_PREVIEW_RESIZE_EVENT); + } }; } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 7adf617047..a1116a6065 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -44,6 +44,21 @@ namespace CommonSharedConstants // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + // Path to the event used by GcodePreviewHandler + const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d"; + + // Path to the event used by DevFilesPreviewHandler + const wchar_t DEV_FILES_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysDevFilesPreviewResizeEvent-5707a22c-2cac-4ea2-82f0-27c03ef0b5f3"; + + // Path to the event used by MarkdownPreviewHandler + const wchar_t MARKDOWN_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysMarkdownPreviewResizeEvent-54c9ab69-11f3-49e9-a98f-53221cfef3ec"; + + // Path to the event used by MarkdownPreviewHandler + const wchar_t PDF_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysPdfPreviewResizeEvent-5a2f162a-f728-45fe-8bda-ef3d5e434ce7"; + + // Path to the event used by MarkdownPreviewHandler + const wchar_t SVG_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysSvgPreviewResizeEvent-0701a4fc-d5a1-4ee7-b885-f83982c62a0d"; + // Max DWORD for key code to disable keys. const DWORD VK_DISABLED = 0x100; } diff --git a/src/common/logger/logger.cpp b/src/common/logger/logger.cpp index 7bf0e62a91..9587b97743 100644 --- a/src/common/logger/logger.cpp +++ b/src/common/logger/logger.cpp @@ -58,18 +58,24 @@ bool Logger::wasLogFailedShown() void Logger::init(std::string loggerName, std::wstring logFilePath, std::wstring_view logSettingsPath) { auto logLevel = getLogLevel(logSettingsPath); + bool newLoggerCreated = false; try { - auto sink = make_shared(logFilePath, 0, 0, false, LogSettings::retention); - if (IsDebuggerPresent()) + logger = spdlog::get(loggerName); + if (logger == nullptr) { - auto msvc_sink = make_shared(); - msvc_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%n] [t-%t] [%l] %v"); - logger = make_shared(loggerName, sinks_init_list{ sink, msvc_sink }); - } - else - { - logger = make_shared(loggerName, sink); + auto sink = make_shared(logFilePath, 0, 0, false, LogSettings::retention); + if (IsDebuggerPresent()) + { + auto msvc_sink = make_shared(); + msvc_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%n] [t-%t] [%l] %v"); + logger = make_shared(loggerName, sinks_init_list{ sink, msvc_sink }); + } + else + { + logger = make_shared(loggerName, sink); + } + newLoggerCreated = true; } } catch (...) @@ -89,10 +95,14 @@ void Logger::init(std::string loggerName, std::wstring logFilePath, std::wstring return; } - logger->set_level(logLevel); - logger->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [p-%P] [t-%t] [%l] %v"); - logger->flush_on(logLevel); // Auto flush on every log message. - spdlog::register_logger(logger); + if (newLoggerCreated) + { + logger->set_level(logLevel); + logger->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [p-%P] [t-%t] [%l] %v"); + logger->flush_on(logLevel); // Auto flush on every log message. + spdlog::register_logger(logger); + } + logger->info("{} logger is initialized", loggerName); } diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h index e342ba410b..b424212314 100644 --- a/src/common/logger/logger_settings.h +++ b/src/common/logger/logger_settings.h @@ -15,6 +15,24 @@ struct LogSettings inline const static std::wstring updateLogPath = L"UpdateLogs\\update-log.txt"; inline const static std::string fileExplorerLoggerName = "FileExplorer"; inline const static std::wstring fileExplorerLogPath = L"Logs\\file-explorer-log.txt"; + inline const static std::string gcodePrevLoggerName = "GcodePrevHandler"; + inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.txt"; + inline const static std::string gcodeThumbLoggerName = "GcodeThumbnailProvider"; + inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.txt"; + inline const static std::string mdPrevLoggerName = "MDPrevHandler"; + inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.txt"; + inline const static std::string monacoPrevLoggerName = "MonacoPrevHandler"; + inline const static std::wstring monacoPrevLogPath = L"logs\\FileExplorer_localLow\\MonacoPrevHandler\\monaco-prev-handler-log.txt"; + inline const static std::string pdfPrevLoggerName = "PdfPrevHandler"; + inline const static std::wstring pdfPrevLogPath = L"logs\\FileExplorer_localLow\\PdfPrevHandler\\pdf-prev-handler-log.txt"; + inline const static std::string pdfThumbLoggerName = "PdfThumbnailProvider"; + inline const static std::wstring pdfThumbLogPath = L"logs\\FileExplorer_localLow\\PdfThumbnailProvider\\pdf-thumbnail-provider-log.txt"; + inline const static std::string stlThumbLoggerName = "StlThumbnailProvider"; + inline const static std::wstring stlThumbLogPath = L"logs\\FileExplorer_localLow\\StlThumbnailProvider\\stl-thumbnail-provider-log.txt"; + inline const static std::string svgPrevLoggerName = "SvgPrevHandler"; + inline const static std::wstring svgPrevLogPath = L"logs\\FileExplorer_localLow\\SvgPrevHandler\\svg-prev-handler-log.txt"; + inline const static std::string svgThumbLoggerName = "SvgThumbnailProvider"; + inline const static std::wstring svgThumbLogPath = L"logs\\FileExplorer_localLow\\SvgThumbnailProvider\\svg-thumbnail-provider-log.txt"; inline const static std::string launcherLoggerName = "launcher"; inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.txt"; inline const static std::wstring awakeLogPath = L"Logs\\awake-log.txt"; diff --git a/src/common/utils/modulesRegistry.h b/src/common/utils/modulesRegistry.h index 7f8631ea09..62d0d35cf2 100644 --- a/src/common/utils/modulesRegistry.h +++ b/src/common/utils/modulesRegistry.h @@ -28,13 +28,12 @@ inline registry::ChangeSet getSvgPreviewHandlerChangeSet(const std::wstring inst using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::preview, perUser, - L"{ddee2b8a-6807-48a6-bb20-2338174ff779}", + L"{FCDD4EED-41AA-492F-8A84-31A1546226E0}", get_std_product_version(), (fs::path{ installationDir } / - LR"d(modules\FileExplorerPreview\PowerToys.SvgPreviewHandler.comhost.dll)d") + LR"d(modules\FileExplorerPreview\PowerToys.SvgPreviewHandlerCpp.dll)d") .wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.PreviewHandler.Svg.SvgPreviewHandler", + L"SvgPreviewHandler", L"Svg Preview Handler", NonLocalizable::ExtSVG); } @@ -44,11 +43,10 @@ inline registry::ChangeSet getMdPreviewHandlerChangeSet(const std::wstring insta using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::preview, perUser, - L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}", + L"{60789D87-9C3C-44AF-B18C-3DE2C2820ED3}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.MarkdownPreviewHandler.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.PreviewHandler.Markdown.MarkdownPreviewHandler", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.MarkdownPreviewHandlerCpp.dll)d").wstring(), + L"MarkdownPreviewHandler", L"Markdown Preview Handler", NonLocalizable::ExtMarkdown); } @@ -107,11 +105,10 @@ inline registry::ChangeSet getMonacoPreviewHandlerChangeSet(const std::wstring i return generatePreviewHandler(PreviewHandlerType::preview, perUser, - L"{afbd5a44-2520-4ae0-9224-6cfce8fe4400}", + L"{D8034CFA-F34B-41FE-AD45-62FCBB52A6DA}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.MonacoPreviewHandler.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.PreviewHandler.Monaco.MonacoPreviewHandler", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.MonacoPreviewHandlerCpp.dll)d").wstring(), + L"MonacoPreviewHandler", L"Monaco Preview Handler", extensions); } @@ -121,11 +118,10 @@ inline registry::ChangeSet getPdfPreviewHandlerChangeSet(const std::wstring inst using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::preview, perUser, - L"{07665729-6243-4746-95b7-79579308d1b2}", + L"{A5A41CC7-02CB-41D4-8C9B-9087040D6098}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.PdfPreviewHandler.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.PreviewHandler.Pdf.PdfPreviewHandler", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.PdfPreviewHandlerCpp.dll)d").wstring(), + L"PdfPreviewHandler", L"Pdf Preview Handler", NonLocalizable::ExtPDF); } @@ -135,11 +131,10 @@ inline registry::ChangeSet getGcodePreviewHandlerChangeSet(const std::wstring in using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::preview, perUser, - L"{ec52dea8-7c9f-4130-a77b-1737d0418507}", + L"{A0257634-8812-4CE8-AF11-FA69ACAEAFAE}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.GcodePreviewHandler.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.PreviewHandler.Gcode.GcodePreviewHandler", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.GcodePreviewHandlerCpp.dll)d").wstring(), + L"GcodePreviewHandler", L"G-code Preview Handler", NonLocalizable::ExtGCode); } @@ -149,11 +144,10 @@ inline registry::ChangeSet getSvgThumbnailHandlerChangeSet(const std::wstring in using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::thumbnail, perUser, - L"{36B27788-A8BB-4698-A756-DF9F11F64F84}", + L"{10144713-1526-46C9-88DA-1FB52807A9FF}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.SvgThumbnailProvider.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.ThumbnailHandler.Svg.SvgThumbnailProvider", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.SvgThumbnailProviderCpp.dll)d").wstring(), + L"SvgThumbnailProvider", L"Svg Thumbnail Provider", NonLocalizable::ExtSVG, L"Picture"); @@ -164,11 +158,10 @@ inline registry::ChangeSet getPdfThumbnailHandlerChangeSet(const std::wstring in using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::thumbnail, perUser, - L"{BCC13D15-9720-4CC4-8371-EA74A274741E}", + L"{D8BB9942-93BD-412D-87E4-33FAB214DC1A}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.PdfThumbnailProvider.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.ThumbnailHandler.Pdf.PdfThumbnailProvider", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.PdfThumbnailProviderCpp.dll)d").wstring(), + L"PdfThumbnailProvider", L"Pdf Thumbnail Provider", NonLocalizable::ExtPDF); } @@ -178,11 +171,10 @@ inline registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::thumbnail, perUser, - L"{BFEE99B4-B74D-4348-BCA5-E757029647FF}", + L"{F2847CBE-CD03-4C83-A359-1A8052C1B9D5}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.GcodeThumbnailProvider.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.ThumbnailHandler.Gcode.GcodeThumbnailProvider", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.GcodeThumbnailProviderCpp.dll)d").wstring(), + L"GcodeThumbnailProvider", L"G-code Thumbnail Provider", NonLocalizable::ExtGCode); } @@ -192,11 +184,10 @@ inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring in using namespace registry::shellex; return generatePreviewHandler(PreviewHandlerType::thumbnail, perUser, - L"{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}", + L"{77257004-6F25-4521-B602-50ECC6EC62A6}", get_std_product_version(), - (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.StlThumbnailProvider.comhost.dll)d").wstring(), - registry::DOTNET_COMPONENT_CATEGORY_CLSID, - L"Microsoft.PowerToys.ThumbnailHandler.Stl.StlThumbnailProvider", + (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.StlThumbnailProviderCpp.dll)d").wstring(), + L"StlThumbnailProvider", L"Stl Thumbnail Provider", NonLocalizable::ExtSTL); } diff --git a/src/common/utils/registry.h b/src/common/utils/registry.h index 689b436a7b..d254457999 100644 --- a/src/common/utils/registry.h +++ b/src/common/utils/registry.h @@ -315,11 +315,10 @@ namespace registry std::wstring handlerClsid, std::wstring powertoysVersion, std::wstring fullPathToHandler, - std::wstring handlerCategory, std::wstring className, std::wstring displayName, std::vector fileTypes, - std::wstring fileKindType = L"" ) + std::wstring fileKindType = L"") { const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; @@ -331,9 +330,6 @@ namespace registry inprocServerPath += L'\\'; inprocServerPath += L"InprocServer32"; - std::wstring implementedCategoriesPath = clsidPath + LR"d(\Implemented Categories\)d"; - implementedCategoriesPath += handlerCategory; - std::wstring assemblyKeyValue; if (const auto lastDotPos = className.rfind(L'.'); lastDotPos != std::wstring::npos) { @@ -356,13 +352,10 @@ namespace registry // TODO: verify that we actually need all of those vec_t changes = { { scope, clsidPath, L"DisplayName", displayName }, { scope, clsidPath, std::nullopt, className }, - { scope, implementedCategoriesPath, std::nullopt, L"" }, { scope, inprocServerPath, std::nullopt, fullPathToHandler }, { scope, inprocServerPath, L"Assembly", assemblyKeyValue }, { scope, inprocServerPath, L"Class", className }, - { scope, inprocServerPath, L"ThreadingModel", L"Both" }, - { scope, versionPath, L"Assembly", assemblyKeyValue }, - { scope, versionPath, L"Class", className } }; + { scope, inprocServerPath, L"ThreadingModel", L"Apartment" } }; for (const auto& fileType : fileTypes) { diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj index 1e5faa376b..b0f04a01a6 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj @@ -21,6 +21,15 @@ true 10.0.19041.0 Assets/Icon.ico + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/FileLocksmith/FileLocksmithUI/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/FileLocksmith/FileLocksmithUI/Properties/PublishProfiles/InstallationPublishProfile.pubxml index b4ac401e4e..a4a0a4c4f1 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/FileLocksmith/FileLocksmithUI/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileLocksmith win10-$(Platform) - false + true False False false diff --git a/src/modules/Hosts/Hosts/Hosts.csproj b/src/modules/Hosts/Hosts/Hosts.csproj index 4a065e0f28..81361152bc 100644 --- a/src/modules/Hosts/Hosts/Hosts.csproj +++ b/src/modules/Hosts/Hosts/Hosts.csproj @@ -20,6 +20,15 @@ PowerToys.Hosts DISABLE_XAML_GENERATED_MAIN,TRACE Assets/Hosts.ico + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj index ff7c66c004..f92f7e5567 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj +++ b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj @@ -13,7 +13,6 @@ app.manifest x86;x64;arm64 win10-x86;win10-x64;win10-arm64 - win10-$(Platform).pubxml true true false @@ -22,6 +21,14 @@ None true 10.0.19041.0 + true + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj index 57fe667ab3..bfb1a03791 100644 --- a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj +++ b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj @@ -10,6 +10,15 @@ enable true true + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/awake/Awake/Awake.csproj b/src/modules/awake/Awake/Awake.csproj index e05c2aeeb4..01da46c13a 100644 --- a/src/modules/awake/Awake/Awake.csproj +++ b/src/modules/awake/Awake/Awake.csproj @@ -17,6 +17,15 @@ 10.0.19041.0 https://awake.den.dev https://github.com/microsoft/powertoys + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index 734b1601bb..d75d01efee 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -9,7 +9,17 @@ true true ColorPicker.Program + true + + + + win10-x64 + + + win10-arm64 + + {BA58206B-1493-4C75-BFEA-A85768A1E156} WinExe diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj index ccb358c55a..fa00d92d70 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj +++ b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj @@ -11,9 +11,18 @@ false false true - $(SolutionDir)$(Platform)\$(Configuration)\modules\FancyZones + $(SolutionDir)$(Platform)\$(Configuration)\modules\FancyZones + true - + + + + win10-x64 + + + win10-arm64 + + {5CCC8468-DEC8-4D36-99D4-5C891BEBD481} net7.0-windows10.0.19041.0 diff --git a/src/modules/imageresizer/ui/ImageResizerUI.csproj b/src/modules/imageresizer/ui/ImageResizerUI.csproj index c8e456f417..b9fd21cc61 100644 --- a/src/modules/imageresizer/ui/ImageResizerUI.csproj +++ b/src/modules/imageresizer/ui/ImageResizerUI.csproj @@ -8,8 +8,17 @@ false true true + true - + + + + win10-x64 + + + win10-arm64 + + {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} WinExe diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj index 0bc9df1487..4c13b9eb4c 100644 --- a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj +++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj @@ -19,8 +19,16 @@ PowerToys.PowerLauncher ..\..\..\..\$(Platform)\$(Configuration)\modules\launcher\ true + true + + + win10-x64 + + + win10-arm64 + DEBUG;TRACE diff --git a/src/modules/launcher/PowerLauncher/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/launcher/PowerLauncher/Properties/PublishProfiles/InstallationPublishProfile.pubxml index acfc5cdd38..0c628c50af 100644 --- a/src/modules/launcher/PowerLauncher/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/launcher/PowerLauncher/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\launcher win-$(Platform) - false + true False False false diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj index 338a68c9c3..a622b1d653 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj @@ -7,8 +7,18 @@ disable True true - true + true + true + + + + win10-x64 + + + win10-arm64 + + PowerToys.GPOWrapper;PowerToys.PowerAccentKeyboardService $(OutDir) diff --git a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj index 18db1b36e4..cc01326236 100644 --- a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj +++ b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj @@ -10,10 +10,19 @@ icon.ico PowerToys.PowerAccent True - PowerAccent.UI.Program - $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent - false - false + PowerAccent.UI.Program + $(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent + false + false + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.cs b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.cs deleted file mode 100644 index 168855a589..0000000000 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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; -using Microsoft.PowerToys.Telemetry; - -namespace Microsoft.PowerToys.PreviewHandler.Gcode -{ - /// - /// Extends for Gcode Preview Handler. - /// - [Guid("ec52dea8-7c9f-4130-a77b-1737d0418507")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class GcodePreviewHandler : StreamBasedPreviewHandler, IDisposable - { - private GcodePreviewHandlerControl _gcodePreviewControl; - private bool disposedValue; - - /// - /// Initializes a new instance of the class. - /// - public GcodePreviewHandler() - { - Initialize(); - } - - /// - public override void DoPreview() - { - _gcodePreviewControl.DoPreview(Stream); - } - - /// - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.GcodeFileHandlerLoaded()); - _gcodePreviewControl = new GcodePreviewHandlerControl(); - - return _gcodePreviewControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - _gcodePreviewControl.Dispose(); - } - - disposedValue = true; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj index c61b95e07d..954f426d4e 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj @@ -1,5 +1,6 @@  + enable true PowerToys.GcodePreviewHandler PowerToys GcodePreviewHandler @@ -11,13 +12,21 @@ true true PowerToys.GcodePreviewHandler + true + + + + + win10-x64 + + + win10-arm64 {805306FF-A562-4415-8DEF-E493BDC45918} Microsoft.PowerToys.PreviewHandler.Gcode net7.0-windows10.0.19041.0 - true @@ -40,6 +49,7 @@ $(NoWarn);1591 + WinExe @@ -48,7 +58,9 @@ + + diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs index 63e83480e9..ed1c2621b0 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs @@ -46,52 +46,50 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGcodePreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. Show an error message instead. - InvokeOnControlThread(() => - { - _infoBarAdded = true; - AddTextBoxControl(Properties.Resource.GpoDisabledErrorText); - Resize += FormResized; - base.DoPreview(dataSource); - }); + _infoBarAdded = true; + AddTextBoxControl(Properties.Resource.GpoDisabledErrorText); + Resize += FormResized; + base.DoPreview(dataSource); return; } - InvokeOnControlThread(() => + try { - try + Bitmap thumbnail = null; + + if (!(dataSource is string filePath)) { - Bitmap thumbnail = null; - - using (var stream = new ReadonlyStream(dataSource as IStream)) - { - using (var reader = new StreamReader(stream)) - { - thumbnail = GetThumbnail(reader); - } - } - - _infoBarAdded = false; - - if (thumbnail == null) - { - _infoBarAdded = true; - AddTextBoxControl(Properties.Resource.GcodeWithoutEmbeddedThumbnails); - } - else - { - AddPictureBoxControl(thumbnail); - } - - Resize += FormResized; - base.DoPreview(dataSource); - PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed()); + throw new ArgumentException($"{nameof(dataSource)} for {nameof(GcodePreviewHandlerControl)} must be a string but was a '{typeof(T)}'"); } - catch (Exception ex) + + FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); + + using (var reader = new StreamReader(fs)) { - PreviewError(ex, dataSource); + thumbnail = GetThumbnail(reader); } - }); + + _infoBarAdded = false; + + if (thumbnail == null) + { + _infoBarAdded = true; + AddTextBoxControl(Properties.Resource.GcodeWithoutEmbeddedThumbnails); + } + else + { + AddPictureBoxControl(thumbnail); + } + + Resize += FormResized; + base.DoPreview(fs); + PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed()); + } + catch (Exception ex) + { + PreviewError(ex, dataSource); + } } /// diff --git a/src/modules/previewpane/GcodePreviewHandler/Program.cs b/src/modules/previewpane/GcodePreviewHandler/Program.cs new file mode 100644 index 0000000000..ec9194fcd2 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandler/Program.cs @@ -0,0 +1,63 @@ +// 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. + +namespace Microsoft.PowerToys.PreviewHandler.Gcode +{ + using System.Globalization; + using System.Windows.Threading; + using Common.UI; + using interop; + + internal static class Program + { + private static CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + private static GcodePreviewHandlerControl _previewHandlerControl; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 6) + { + string filePath = args[0]; + int hwnd = Convert.ToInt32(args[1], 16); + + Rectangle s = default(Rectangle); + int left = Convert.ToInt32(args[2], 10); + int right = Convert.ToInt32(args[3], 10); + int top = Convert.ToInt32(args[4], 10); + int bottom = Convert.ToInt32(args[5], 10); + + _previewHandlerControl = new GcodePreviewHandlerControl(); + _previewHandlerControl.SetWindow((IntPtr)hwnd, s); + _previewHandlerControl.DoPreview(filePath); + + NativeEventWaiter.WaitForEventLoop( + Constants.GcodePreviewResizeEvent(), + () => + { + Rectangle s = default(Rectangle); + _previewHandlerControl.SetRect(s); + }, + Dispatcher.CurrentDispatcher, + _tokenSource.Token); + } + else + { + MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + Application.Run(); + } + } +} diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.cpp b/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.cpp new file mode 100644 index 0000000000..28524635f4 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "GcodePreviewHandler.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + GcodePreviewHandler* pExt = new (std::nothrow) GcodePreviewHandler(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.h b/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp new file mode 100644 index 0000000000..8903d9908c --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.cpp @@ -0,0 +1,262 @@ +#include "pch.h" +#include "GcodePreviewHandler.h" + +#include +#include +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +GcodePreviewHandler::GcodePreviewHandler() : + m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL) +{ + m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT); + + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::gcodePrevLogPath); + Logger::init(LogSettings::gcodePrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +GcodePreviewHandler::~GcodePreviewHandler() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP GcodePreviewHandler::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(GcodePreviewHandler, IPreviewHandler), + QITABENT(GcodePreviewHandler, IInitializeWithFile), + QITABENT(GcodePreviewHandler, IPreviewHandlerVisuals), + QITABENT(GcodePreviewHandler, IOleWindow), + QITABENT(GcodePreviewHandler, IObjectWithSite), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +GcodePreviewHandler::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +GcodePreviewHandler::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithFile + +IFACEMETHODIMP GcodePreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode) +{ + m_filePath = pszFilePath; + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandler + +IFACEMETHODIMP GcodePreviewHandler::SetWindow(HWND hwnd, const RECT* prc) +{ + if (hwnd && prc) + { + m_hwndParent = hwnd; + m_rcParent = *prc; + } + return S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::SetFocus() +{ + return S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::QueryFocus(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = ::GetFocus(); + if (*phwnd) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +IFACEMETHODIMP GcodePreviewHandler::TranslateAccelerator(MSG* pmsg) +{ + HRESULT hr = S_FALSE; + IPreviewHandlerFrame* pFrame = NULL; + if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame))) + { + hr = pFrame->TranslateAccelerator(pmsg); + + pFrame->Release(); + } + return hr; +} + +IFACEMETHODIMP GcodePreviewHandler::SetRect(const RECT* prc) +{ + HRESULT hr = E_INVALIDARG; + if (prc != NULL) + { + if (!m_resizeEvent) + { + Logger::error(L"Failed to create resize event for GcodePreviewHandler"); + } + else + { + if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom) + { + if (!SetEvent(m_resizeEvent)) + { + Logger::error(L"Failed to signal resize event for GcodePreviewHandler"); + } + } + } + m_rcParent = *prc; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP GcodePreviewHandler::DoPreview() +{ + try + { + Logger::info(L"Starting GcodePreviewHandler.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + m_filePath + L"\"" }; + cmdLine += L" "; + std::wostringstream ss; + ss << std::hex << m_hwndParent; + + cmdLine += ss.str(); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.left); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.right); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.top); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.bottom); + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.GcodePreviewHandler.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start GcodePreviewHandler.exe. Error: {}", errorMessage); + } + + return S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::Unload() +{ + Logger::info(L"Unload and terminate .exe"); + + m_hwndParent = NULL; + TerminateProcess(m_process, 0); + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandlerVisuals + +IFACEMETHODIMP GcodePreviewHandler::SetBackgroundColor(COLORREF color) +{ + return S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::SetFont(const LOGFONTW* plf) +{ + return S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::SetTextColor(COLORREF color) +{ + return S_OK; +} + +#pragma endregion + +#pragma region IOleWindow + +IFACEMETHODIMP GcodePreviewHandler::GetWindow(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = m_hwndParent; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP GcodePreviewHandler::ContextSensitiveHelp(BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +#pragma endregion + +#pragma region IObjectWithSite + +IFACEMETHODIMP GcodePreviewHandler::SetSite(IUnknown* punkSite) +{ + if (m_punkSite) + { + m_punkSite->Release(); + m_punkSite = NULL; + } + return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK; +} + +IFACEMETHODIMP GcodePreviewHandler::GetSite(REFIID riid, void** ppv) +{ + *ppv = NULL; + return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.h b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.h new file mode 100644 index 0000000000..715bb44922 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandler.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class GcodePreviewHandler : + public IInitializeWithFile, + public IPreviewHandler, + public IPreviewHandlerVisuals, + public IOleWindow, + public IObjectWithSite +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithFile + IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc); + IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP QueryFocus(HWND* phwnd); + IFACEMETHODIMP TranslateAccelerator(MSG* pmsg); + IFACEMETHODIMP SetRect(const RECT* prc); + IFACEMETHODIMP DoPreview(); + IFACEMETHODIMP Unload(); + + // IPreviewHandlerVisuals + IFACEMETHODIMP SetBackgroundColor(COLORREF color); + IFACEMETHODIMP SetFont(const LOGFONTW* plf); + IFACEMETHODIMP SetTextColor(COLORREF color); + + // IOleWindow + IFACEMETHODIMP GetWindow(HWND* phwnd); + IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IObjectWithSite + IFACEMETHODIMP SetSite(IUnknown* punkSite); + IFACEMETHODIMP GetSite(REFIID riid, void** ppv); + + GcodePreviewHandler(); +protected: + ~GcodePreviewHandler(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + std::wstring m_filePath; + + // Parent window that hosts the previewer window. + // Note: do NOT DestroyWindow this. + HWND m_hwndParent; + // Bounding rect of the parent window. + RECT m_rcParent; + + // Site pointer from host, used to get IPreviewHandlerFrame. + IUnknown* m_punkSite; + + HANDLE m_process; + HANDLE m_resizeEvent; +}; \ No newline at end of file diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.rc b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj new file mode 100644 index 0000000000..df0afaaaf9 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj @@ -0,0 +1,119 @@ + + + + + 16.0 + Win32Proj + {5a5dd09d-723a-44d3-8f2b-293584c3d731} + GcodePreviewHandlerCpp + 10.0.19041.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + + Create + + + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj.filters b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj.filters new file mode 100644 index 0000000000..5c278e0fbb --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/GlobalExportFunctions.def b/src/modules/previewpane/GcodePreviewHandlerCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/dllmain.cpp b/src/modules/previewpane/GcodePreviewHandlerCpp/dllmain.cpp new file mode 100644 index 0000000000..259403f0c8 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {A0257634-8812-4CE8-AF11-FA69ACAEAFAE} +static const GUID CLSID_GcodePreviewHandler = { 0xa0257634, 0x8812, 0x4ce8, { 0xaf, 0x11, 0xfa, 0x69, 0xac, 0xae, 0xaf, 0xae } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_GcodePreviewHandler, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/packages.config b/src/modules/previewpane/GcodePreviewHandlerCpp/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/pch.cpp b/src/modules/previewpane/GcodePreviewHandlerCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/pch.h b/src/modules/previewpane/GcodePreviewHandlerCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/GcodePreviewHandlerCpp/resource.h b/src/modules/previewpane/GcodePreviewHandlerCpp/resource.h new file mode 100644 index 0000000000..b7fc0664a7 --- /dev/null +++ b/src/modules/previewpane/GcodePreviewHandlerCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Gcode Preview Handler Module" +#define INTERNAL_NAME "PowerToys.GcodePreviewHandlerCpp" +#define ORIGINAL_FILENAME "PowerToys.GcodePreviewHandlerCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs index 2a52a53dbe..09a3c98c27 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs @@ -1,32 +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.Collections.Generic; -using System.Drawing; using System.Drawing.Drawing2D; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; -using Common.ComInterlop; -using Common.Utilities; namespace Microsoft.PowerToys.ThumbnailHandler.Gcode { /// /// G-code Thumbnail Provider. /// - [Guid("BFEE99B4-B74D-4348-BCA5-E757029647FF")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class GcodeThumbnailProvider : IInitializeWithStream, IThumbnailProvider + public class GcodeThumbnailProvider { + public GcodeThumbnailProvider(string filePath) + { + FilePath = filePath; + Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + } + + /// + /// Gets the file path to the file creating thumbnail for. + /// + public string FilePath { get; private set; } + /// /// Gets the stream object to access file. /// - public IStream Stream { get; private set; } + public Stream Stream { get; private set; } /// /// The maximum dimension (width or height) thumbnail we will generate. @@ -140,44 +139,36 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode return destImage; } - /// - public void Initialize(IStream pstream, uint grfMode) + /// + /// Generate thumbnail bitmap for provided Gcode file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + public Bitmap GetThumbnail(uint cx) { - // Ignore the grfMode always use read mode to access the file. - this.Stream = pstream; - } - - /// - public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha) - { - phbmp = IntPtr.Zero; - pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN; - if (cx == 0 || cx > MaxThumbnailSize) { - return; + return null; } if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGcodeThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. - return; + return null; } - using (var stream = new ReadonlyStream(this.Stream as IStream)) + using (var reader = new StreamReader(this.Stream)) { - using (var reader = new StreamReader(stream)) + using (Bitmap thumbnail = GetThumbnail(reader, cx)) { - using (Bitmap thumbnail = GetThumbnail(reader, cx)) + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - phbmp = thumbnail.GetHbitmap(Color.Transparent); - pdwAlpha = WTS_ALPHATYPE.WTSAT_ARGB; - } + return (Bitmap)thumbnail.Clone(); } } } + + return null; } } } diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj index e3653ff114..11c1c3f3c9 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj @@ -1,5 +1,6 @@  + enable true {809AA252-E17A-4FA2-B0A1-0450976B763F} Microsoft.PowerToys.ThumbnailHandler.Gcode @@ -7,13 +8,21 @@ PowerToys.GcodeThumbnailProvider PowerToys GcodePreviewHandler net7.0-windows10.0.19041.0 - true true PowerToys GcodePreviewHandler $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ false false true + true + + + + + win10-x64 + + + win10-arm64 @@ -27,6 +36,7 @@ $(NoWarn);1591 + WinExe diff --git a/src/modules/previewpane/GcodeThumbnailProvider/Program.cs b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs new file mode 100644 index 0000000000..84bfd5dcaf --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Microsoft.PowerToys.ThumbnailHandler.Gcode +{ + using System.Globalization; + + internal static class Program + { + private static GcodeThumbnailProvider _thumbnailProvider; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 2) + { + string filePath = args[0]; + uint cx = Convert.ToUInt32(args[1], 10); + + _thumbnailProvider = new GcodeThumbnailProvider(filePath); + Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); + filePath = filePath.Replace(".gcode", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } + else + { + MessageBox.Show("Gcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + } + } +} diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.cpp new file mode 100644 index 0000000000..33e8c037c6 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "GcodeThumbnailProvider.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + GcodeThumbnailProvider* pExt = new (std::nothrow) GcodeThumbnailProvider(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp new file mode 100644 index 0000000000..7f16288d46 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp @@ -0,0 +1,185 @@ +#include "pch.h" +#include "GcodeThumbnailProvider.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +GcodeThumbnailProvider::GcodeThumbnailProvider() : + m_cRef(1), m_pStream(NULL), m_process(NULL) +{ + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::gcodeThumbLogPath); + Logger::init(LogSettings::gcodeThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +GcodeThumbnailProvider::~GcodeThumbnailProvider() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP GcodeThumbnailProvider::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(GcodeThumbnailProvider, IThumbnailProvider), + QITABENT(GcodeThumbnailProvider, IInitializeWithStream), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +GcodeThumbnailProvider::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +GcodeThumbnailProvider::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithStream + +IFACEMETHODIMP GcodeThumbnailProvider::Initialize(IStream* pStream, DWORD grfMode) +{ + HRESULT hr = E_INVALIDARG; + if (pStream) + { + // Initialize can be called more than once, so release existing valid + // m_pStream. + if (m_pStream) + { + m_pStream->Release(); + m_pStream = NULL; + } + + m_pStream = pStream; + m_pStream->AddRef(); + hr = S_OK; + } + return hr; +} + +#pragma endregion + +#pragma region IThumbnailProvider + +IFACEMETHODIMP GcodeThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha) +{ + // Read stream into the buffer + char buffer[4096]; + ULONG cbRead; + + Logger::trace(L"Begin"); + + GUID guid; + if (CoCreateGuid(&guid) == S_OK) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED(StringFromCLSID(guid, &guidString))) + { + Logger::info(L"Read stream and save to tmp file."); + + // {CLSID} -> CLSID + std::wstring guid = std::wstring(guidString.get()).substr(1, std::wstring(guidString.get()).size() - 2); + std::wstring filePath = PTSettingsHelper::get_local_low_folder_location() + L"\\GCodeThumbnail-Temp\\"; + if (!std::filesystem::exists(filePath)) + { + std::filesystem::create_directories(filePath); + } + + std::wstring fileName = filePath + guid + L".gcode"; + + // Write data to tmp file + std::fstream file; + file.open(fileName, std::ios_base::out | std::ios_base::binary); + + if (!file.is_open()) + { + return 0; + } + + while (true) + { + auto result = m_pStream->Read(buffer, 4096, &cbRead); + + file.write(buffer, cbRead); + if (result == S_FALSE) + { + break; + } + } + file.close(); + + + try + { + Logger::info(L"Start GcodeThumbnailProvider.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + fileName + L"\"" }; + cmdLine += L" "; + cmdLine += std::to_wstring(cx); + + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.GcodeThumbnailProvider.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + WaitForSingleObject(m_process, INFINITE); + std::filesystem::remove(fileName); + + std::wstring fileNameBmp = filePath + guid + L".bmp"; + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + + std::filesystem::remove(fileNameBmp); + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start GcodeThumbnailProvider.exe. Error: {}", errorMessage); + } + } + } + + + return S_OK; +} + + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h new file mode 100644 index 0000000000..8662de66e6 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h @@ -0,0 +1,37 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class GcodeThumbnailProvider : + public IInitializeWithStream, + public IThumbnailProvider +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithStream + IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); + + GcodeThumbnailProvider(); +protected: + ~GcodeThumbnailProvider(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + IStream* m_pStream; + + HANDLE m_process; +}; \ No newline at end of file diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.rc b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj new file mode 100644 index 0000000000..59a2db3c48 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj @@ -0,0 +1,121 @@ + + + + + 16.0 + Win32Proj + {56cc2f10-6e41-453d-be16-c593a5e58482} + GcodeThumbnailProviderCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + + Create + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj.filters b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj.filters new file mode 100644 index 0000000000..08004c2ea6 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GlobalExportFunctions.def b/src/modules/previewpane/GcodeThumbnailProviderCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/dllmain.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/dllmain.cpp new file mode 100644 index 0000000000..34fde4073e --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {F2847CBE-CD03-4C83-A359-1A8052C1B9D5} +static const GUID CLSID_GcodeThumbnailProvider = { 0xf2847cbe, 0xcd03, 0x4c83, { 0xa3, 0x59, 0x1a, 0x80, 0x52, 0xc1, 0xb9, 0xd5 } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_GcodeThumbnailProvider, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/packages.config b/src/modules/previewpane/GcodeThumbnailProviderCpp/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.h new file mode 100644 index 0000000000..8a0d004247 --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/pch.h @@ -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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/resource.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/resource.h new file mode 100644 index 0000000000..87f19fc75e --- /dev/null +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Gcode Thumbnail Provider Module" +#define INTERNAL_NAME "PowerToys.GcodeThumbnailProviderCpp" +#define ORIGINAL_FILENAME "PowerToys.GcodeThumbnailProviderCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs deleted file mode 100644 index 83fa419c89..0000000000 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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; -using Microsoft.PowerToys.Telemetry; - -namespace Microsoft.PowerToys.PreviewHandler.Markdown -{ - /// - /// Implementation of preview handler for markdown files. - /// - [Guid("45769bcc-e8fd-42d0-947e-02beef77a1f5")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class MarkdownPreviewHandler : FileBasedPreviewHandler, IDisposable - { - private MarkdownPreviewHandlerControl _markdownPreviewHandlerControl; - private bool disposedValue; - - /// - /// Initializes a new instance of the class. - /// - public MarkdownPreviewHandler() - { - Initialize(); - } - - /// - public override void DoPreview() - { - _markdownPreviewHandlerControl.DoPreview(FilePath); - } - - /// - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.MarkdownFileHandlerLoaded()); - _markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl(); - - return _markdownPreviewHandlerControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - _markdownPreviewHandlerControl.Dispose(); - } - - disposedValue = true; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj index 59455ae34e..5c90e75c57 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj @@ -1,5 +1,6 @@  + enable true PowerToys.MarkdownPreviewHandler PowerToys MarkdownPreviewHandler @@ -12,12 +13,20 @@ true win10-x64;win10-arm64 net7.0-windows10.0.19041.0 + true + + + + + win10-x64 + + + win10-arm64 {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} Microsoft.PowerToys.PreviewHandler.Markdown - true PowerToys.MarkdownPreviewHandler @@ -46,6 +55,7 @@ $(NoWarn);1591 + WinExe @@ -58,6 +68,7 @@ + diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs index cc0a1b9420..0129fa5319 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs @@ -133,14 +133,11 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMarkdownPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. Show an error message instead. - InvokeOnControlThread(() => - { - _infoBarDisplayed = true; - _infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText); - Resize += FormResized; - Controls.Add(_infoBar); - base.DoPreview(dataSource); - }); + _infoBarDisplayed = true; + _infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText); + Resize += FormResized; + Controls.Add(_infoBar); + base.DoPreview(dataSource); return; } @@ -153,7 +150,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown { if (!(dataSource is string filePath)) { - throw new ArgumentException($"{nameof(dataSource)} for {nameof(MarkdownPreviewHandler)} must be a string but was a '{typeof(T)}'"); + throw new ArgumentException($"{nameof(dataSource)} for {nameof(MarkdownPreviewHandlerControl)} must be a string but was a '{typeof(T)}'"); } string fileText = File.ReadAllText(filePath); @@ -174,80 +171,74 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown Dock = DockStyle.Fill, }; - InvokeOnControlThread(() => - { - var webView2Options = new CoreWebView2EnvironmentOptions("--block-new-web-contents"); - ConfiguredTaskAwaitable.ConfiguredTaskAwaiter + var webView2Options = new CoreWebView2EnvironmentOptions("--block-new-web-contents"); + ConfiguredTaskAwaitable.ConfiguredTaskAwaiter webView2EnvironmentAwaiter = CoreWebView2Environment .CreateAsync(userDataFolder: _webView2UserDataFolder, options: webView2Options) .ConfigureAwait(true).GetAwaiter(); - webView2EnvironmentAwaiter.OnCompleted(() => + webView2EnvironmentAwaiter.OnCompleted(async () => + { + try { - InvokeOnControlThread(async () => + _webView2Environment = webView2EnvironmentAwaiter.GetResult(); + await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); + _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny); + _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; + _browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; + _browser.CoreWebView2.Settings.AreDevToolsEnabled = false; + _browser.CoreWebView2.Settings.AreHostObjectsAllowed = false; + _browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; + _browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; + _browser.CoreWebView2.Settings.IsScriptEnabled = false; + _browser.CoreWebView2.Settings.IsWebMessageEnabled = false; + + // Don't load any resources. + _browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); + _browser.CoreWebView2.WebResourceRequested += (object sender, CoreWebView2WebResourceRequestedEventArgs e) => { - try + // Show local file we've saved with the markdown contents. Block all else. + if (new Uri(e.Request.Uri) != _localFileURI) { - _webView2Environment = webView2EnvironmentAwaiter.GetResult(); - await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny); - _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; - _browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; - _browser.CoreWebView2.Settings.AreDevToolsEnabled = false; - _browser.CoreWebView2.Settings.AreHostObjectsAllowed = false; - _browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; - _browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; - _browser.CoreWebView2.Settings.IsScriptEnabled = false; - _browser.CoreWebView2.Settings.IsWebMessageEnabled = false; - - // Don't load any resources. - _browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); - _browser.CoreWebView2.WebResourceRequested += (object sender, CoreWebView2WebResourceRequestedEventArgs e) => - { - // Show local file we've saved with the markdown contents. Block all else. - if (new Uri(e.Request.Uri) != _localFileURI) - { - e.Response = _browser.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", null); - } - }; - - // WebView2.NavigateToString() limitation - // See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks - // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes - if (markdownHTML.Length > 1_500_000) - { - string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; - File.WriteAllText(filename, markdownHTML); - _localFileURI = new Uri(filename); - _browser.Source = _localFileURI; - } - else - { - _browser.NavigateToString(markdownHTML); - } - - Controls.Add(_browser); - - _browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) => - { - if (args.Uri != null && args.Uri != _localFileURI?.ToString() && args.IsUserInitiated) - { - args.Cancel = true; - await Launcher.LaunchUriAsync(new Uri(args.Uri)); - } - }; - - if (_infoBarDisplayed) - { - _infoBar = GetTextBoxControl(Resources.BlockedImageInfoText); - Resize += FormResized; - Controls.Add(_infoBar); - } + e.Response = _browser.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", null); } - catch (NullReferenceException) + }; + + // WebView2.NavigateToString() limitation + // See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks + // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes + if (markdownHTML.Length > 1_500_000) + { + string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; + File.WriteAllText(filename, markdownHTML); + _localFileURI = new Uri(filename); + _browser.Source = _localFileURI; + } + else + { + _browser.NavigateToString(markdownHTML); + } + + Controls.Add(_browser); + + _browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) => + { + if (args.Uri != null && args.Uri != _localFileURI?.ToString() && args.IsUserInitiated) { + args.Cancel = true; + await Launcher.LaunchUriAsync(new Uri(args.Uri)); } - }); - }); + }; + + if (_infoBarDisplayed) + { + _infoBar = GetTextBoxControl(Resources.BlockedImageInfoText); + Resize += FormResized; + Controls.Add(_infoBar); + } + } + catch (NullReferenceException) + { + } }); PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed()); @@ -256,14 +247,11 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown { PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewError { Message = ex.Message }); - InvokeOnControlThread(() => - { - Controls.Clear(); - _infoBarDisplayed = true; - _infoBar = GetTextBoxControl(Resources.MarkdownNotPreviewedError); - Resize += FormResized; - Controls.Add(_infoBar); - }); + Controls.Clear(); + _infoBarDisplayed = true; + _infoBar = GetTextBoxControl(Resources.MarkdownNotPreviewedError); + Resize += FormResized; + Controls.Add(_infoBar); } finally { diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Program.cs b/src/modules/previewpane/MarkdownPreviewHandler/Program.cs new file mode 100644 index 0000000000..32bd6ac7fa --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandler/Program.cs @@ -0,0 +1,63 @@ +// 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. + +namespace Microsoft.PowerToys.PreviewHandler.Markdown +{ + using System.Globalization; + using System.Windows.Threading; + using Common.UI; + using interop; + + internal static class Program + { + private static CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + private static MarkdownPreviewHandlerControl _previewHandlerControl; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 6) + { + string filePath = args[0]; + int hwnd = Convert.ToInt32(args[1], 16); + + Rectangle s = default(Rectangle); + int left = Convert.ToInt32(args[2], 10); + int right = Convert.ToInt32(args[3], 10); + int top = Convert.ToInt32(args[4], 10); + int bottom = Convert.ToInt32(args[5], 10); + + _previewHandlerControl = new MarkdownPreviewHandlerControl(); + _previewHandlerControl.SetWindow((IntPtr)hwnd, s); + _previewHandlerControl.DoPreview(filePath); + + NativeEventWaiter.WaitForEventLoop( + Constants.MarkdownPreviewResizeEvent(), + () => + { + Rectangle s = default(Rectangle); + _previewHandlerControl.SetRect(s); + }, + Dispatcher.CurrentDispatcher, + _tokenSource.Token); + } + else + { + MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + Application.Run(); + } + } +} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/previewpane/MarkdownPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml index 74181a551d..d8a1a0ab2d 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/previewpane/MarkdownPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview win10-$(Platform) - false + true False False false diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.cpp b/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.cpp new file mode 100644 index 0000000000..fcf04c6d1f --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "MarkdownPreviewHandler.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + MarkdownPreviewHandler* pExt = new (std::nothrow) MarkdownPreviewHandler(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.h b/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/GlobalExportFunctions.def b/src/modules/previewpane/MarkdownPreviewHandlerCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp new file mode 100644 index 0000000000..3a0210ff3a --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.cpp @@ -0,0 +1,263 @@ +#include "pch.h" +#include "MarkdownPreviewHandler.h" +#include "Generated Files/resource.h" + +#include +#include +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +MarkdownPreviewHandler::MarkdownPreviewHandler() : + m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL) +{ + m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MARKDOWN_PREVIEW_RESIZE_EVENT); + + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::mdPrevLogPath); + Logger::init(LogSettings::mdPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +MarkdownPreviewHandler::~MarkdownPreviewHandler() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP MarkdownPreviewHandler::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(MarkdownPreviewHandler, IPreviewHandler), + QITABENT(MarkdownPreviewHandler, IInitializeWithFile), + QITABENT(MarkdownPreviewHandler, IPreviewHandlerVisuals), + QITABENT(MarkdownPreviewHandler, IOleWindow), + QITABENT(MarkdownPreviewHandler, IObjectWithSite), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +MarkdownPreviewHandler::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +MarkdownPreviewHandler::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithFile + +IFACEMETHODIMP MarkdownPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode) +{ + m_filePath = pszFilePath; + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandler + +IFACEMETHODIMP MarkdownPreviewHandler::SetWindow(HWND hwnd, const RECT* prc) +{ + if (hwnd && prc) + { + m_hwndParent = hwnd; + m_rcParent = *prc; + } + return S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::SetFocus() +{ + return S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::QueryFocus(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = ::GetFocus(); + if (*phwnd) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +IFACEMETHODIMP MarkdownPreviewHandler::TranslateAccelerator(MSG* pmsg) +{ + HRESULT hr = S_FALSE; + IPreviewHandlerFrame* pFrame = NULL; + if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame))) + { + hr = pFrame->TranslateAccelerator(pmsg); + + pFrame->Release(); + } + return hr; +} + +IFACEMETHODIMP MarkdownPreviewHandler::SetRect(const RECT* prc) +{ + HRESULT hr = E_INVALIDARG; + if (prc != NULL) + { + if (!m_resizeEvent) + { + Logger::error(L"Failed to create resize event for MDPreviewHandler"); + } + else + { + if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom) + { + if (!SetEvent(m_resizeEvent)) + { + Logger::error(L"Failed to signal resize event for MDPreviewHandler"); + } + } + } + m_rcParent = *prc; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP MarkdownPreviewHandler::DoPreview() +{ + try + { + Logger::info(L"Starting MarkdownPreviewHandler.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + m_filePath + L"\"" }; + cmdLine += L" "; + std::wostringstream ss; + ss << std::hex << m_hwndParent; + + cmdLine += ss.str(); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.left); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.right); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.top); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.bottom); + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.MarkdownPreviewHandler.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start MarkdownPreviewHandler.exe. Error: {}", errorMessage); + } + + return S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::Unload() + +{ + Logger::info(L"Unload and terminate .exe"); + + TerminateProcess(m_process, 0); + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandlerVisuals + +IFACEMETHODIMP MarkdownPreviewHandler::SetBackgroundColor(COLORREF color) +{ + return S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::SetFont(const LOGFONTW* plf) +{ + return S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::SetTextColor(COLORREF color) +{ + return S_OK; +} + +#pragma endregion + +#pragma region IOleWindow + +IFACEMETHODIMP MarkdownPreviewHandler::GetWindow(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = m_hwndParent; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP MarkdownPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +#pragma endregion + +#pragma region IObjectWithSite + +IFACEMETHODIMP MarkdownPreviewHandler::SetSite(IUnknown* punkSite) +{ + if (m_punkSite) + { + m_punkSite->Release(); + m_punkSite = NULL; + } + return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK; +} + +IFACEMETHODIMP MarkdownPreviewHandler::GetSite(REFIID riid, void** ppv) +{ + *ppv = NULL; + return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.h b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.h new file mode 100644 index 0000000000..c12e6a1e9b --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandler.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class MarkdownPreviewHandler : + public IInitializeWithFile, + public IPreviewHandler, + public IPreviewHandlerVisuals, + public IOleWindow, + public IObjectWithSite +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithFile + IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc); + IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP QueryFocus(HWND* phwnd); + IFACEMETHODIMP TranslateAccelerator(MSG* pmsg); + IFACEMETHODIMP SetRect(const RECT* prc); + IFACEMETHODIMP DoPreview(); + IFACEMETHODIMP Unload(); + + // IPreviewHandlerVisuals + IFACEMETHODIMP SetBackgroundColor(COLORREF color); + IFACEMETHODIMP SetFont(const LOGFONTW* plf); + IFACEMETHODIMP SetTextColor(COLORREF color); + + // IOleWindow + IFACEMETHODIMP GetWindow(HWND* phwnd); + IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IObjectWithSite + IFACEMETHODIMP SetSite(IUnknown* punkSite); + IFACEMETHODIMP GetSite(REFIID riid, void** ppv); + + MarkdownPreviewHandler(); +protected: + ~MarkdownPreviewHandler(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + std::wstring m_filePath; + + // Parent window that hosts the previewer window. + // Note: do NOT DestroyWindow this. + HWND m_hwndParent; + // Bounding rect of the parent window. + RECT m_rcParent; + + // Site pointer from host, used to get IPreviewHandlerFrame. + IUnknown* m_punkSite; + + HANDLE m_process; + HANDLE m_resizeEvent; +}; \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj new file mode 100644 index 0000000000..6866ce4f77 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj @@ -0,0 +1,128 @@ + + + + + + + + 16.0 + Win32Proj + {ed9a1ac6-aeb0-4569-a6e9-e1696182b545} + MarkdownPreviewHandlerCpp + 10.0.19041.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + true + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj.filters b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj.filters new file mode 100644 index 0000000000..331ad3cb42 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + Resource Files + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/Resources.resx b/src/modules/previewpane/MarkdownPreviewHandlerCpp/Resources.resx new file mode 100644 index 0000000000..7a845ea7a2 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/Resources.resx @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator. + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/dllmain.cpp b/src/modules/previewpane/MarkdownPreviewHandlerCpp/dllmain.cpp new file mode 100644 index 0000000000..ef29b3ccc3 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/dllmain.cpp @@ -0,0 +1,74 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {60789D87-9C3C-44AF-B18C-3DE2C2820ED3} +static const GUID CLSID_MarkdownPreviewHandler = { 0x60789d87, 0x9c3c, 0x44af, { 0xb1, 0x8c, 0x3d, 0xe2, 0xc2, 0x82, 0xe, 0xd3 } }; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_MarkdownPreviewHandler, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/markdownpreviewhandler.base.rc b/src/modules/previewpane/MarkdownPreviewHandlerCpp/markdownpreviewhandler.base.rc new file mode 100644 index 0000000000..ec61be69d7 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/markdownpreviewhandler.base.rc @@ -0,0 +1,46 @@ +#include +#include "resource.h" +#include "../../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/packages.config b/src/modules/previewpane/MarkdownPreviewHandlerCpp/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.cpp b/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.h b/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.base.h b/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.base.h new file mode 100644 index 0000000000..bf58c9ddb2 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.base.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by powerpreview.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Markdown Preview Handler" +#define INTERNAL_NAME "PowerToys.MarkdownPreviewHandler" +#define ORIGINAL_FILENAME "PowerToys.MarkdownPreviewHandlerCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.h b/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.h new file mode 100644 index 0000000000..750d292358 --- /dev/null +++ b/src/modules/previewpane/MarkdownPreviewHandlerCpp/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by markdownpreviewhandler.base.rc + +// 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 diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.cs deleted file mode 100644 index 0445c2af1a..0000000000 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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. - -namespace Microsoft.PowerToys.PreviewHandler.Monaco -{ - using System; - using System.Runtime.InteropServices; - using Common; - - /// - /// Implementation of preview handler for files with source code. - /// - [Guid("afbd5a44-2520-4ae0-9224-6cfce8fe4400")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class MonacoPreviewHandler : FileBasedPreviewHandler, IDisposable - { - private MonacoPreviewHandlerControl _monacoPreviewHandlerControl; - private bool _disposedValue; - - /// - /// Initializes a new instance of the class. - /// - public MonacoPreviewHandler() - { - this.Initialize(); - } - - /// - [STAThread] - public override void DoPreview() - { - _monacoPreviewHandlerControl.DoPreview(FilePath); - } - - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - _monacoPreviewHandlerControl = new MonacoPreviewHandlerControl(); - - return _monacoPreviewHandlerControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - [STAThread] - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - _monacoPreviewHandlerControl.Dispose(); - } - - _disposedValue = true; - this.Unload(); - } - } - - /// - [STAThread] - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj index 4115158951..b54e276b29 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj @@ -1,5 +1,7 @@ + enable + true PowerToys.MonacoPreviewHandler PowerToys MonacoPreviewHandler PowerToys MonacoPreviewHandler @@ -9,14 +11,22 @@ true true win10-x64;win10-arm64 + true - + + + + win10-x64 + + + win10-arm64 + + Microsoft.PowerToys.PreviewHandler.Monaco net7.0-windows10.0.19041.0 - true PowerToys.MonacoPreviewHandler @@ -35,6 +45,7 @@ $(NoWarn);1591 + WinExe diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index 81e962aeed..bd76aacc0e 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -107,13 +107,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMonacoPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. Show an error message instead. - InvokeOnControlThread(() => - { - _infoBarAdded = true; - AddTextBoxControl(Properties.Resources.GpoDisabledErrorText); - Resize += FormResized; - base.DoPreview(dataSource); - }); + _infoBarAdded = true; + AddTextBoxControl(Properties.Resources.GpoDisabledErrorText); + Resize += FormResized; + base.DoPreview(dataSource); return; } @@ -132,7 +129,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco // Checks if dataSource is a string if (!(dataSource is string filePath)) { - throw new ArgumentException($"{nameof(dataSource)} for {nameof(MonacoPreviewHandler)} must be a string but was a '{typeof(T)}'"); + throw new ArgumentException($"{nameof(dataSource)} for {nameof(MonacoPreviewHandlerControl)} must be a string but was a '{typeof(T)}'"); } // Check if the file is too big. @@ -145,103 +142,94 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco try { - InvokeOnControlThread(() => + Logger.LogInfo("Create WebView2 environment"); + ConfiguredTaskAwaitable.ConfiguredTaskAwaiter + webView2EnvironmentAwaiter = CoreWebView2Environment + .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\MonacoPreview-Temp") + .ConfigureAwait(true).GetAwaiter(); + webView2EnvironmentAwaiter.OnCompleted(async () => { - Logger.LogInfo("Create WebView2 environment"); - ConfiguredTaskAwaitable.ConfiguredTaskAwaiter - webView2EnvironmentAwaiter = CoreWebView2Environment - .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\MonacoPreview-Temp") - .ConfigureAwait(true).GetAwaiter(); - webView2EnvironmentAwaiter.OnCompleted(() => + _loadingBar.Value = 60; + this.Update(); + try { - _loadingBar.Value = 60; - this.Update(); - InvokeOnControlThread(async () => + if (CoreWebView2Environment.GetAvailableBrowserVersionString() == null) { - try - { - if (CoreWebView2Environment.GetAvailableBrowserVersionString() == null) - { - throw new WebView2RuntimeNotFoundException(); - } + throw new WebView2RuntimeNotFoundException(); + } - _webView2Environment = webView2EnvironmentAwaiter.GetResult(); + _webView2Environment = webView2EnvironmentAwaiter.GetResult(); - _loadingBar.Value = 70; - this.Update(); + _loadingBar.Value = 70; + this.Update(); - // Initialize WebView - try - { - await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); + // Initialize WebView + try + { + await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - // Wait until html is loaded - initializeIndexFileAndSelectedFileTask.Wait(); + // Wait until html is loaded + initializeIndexFileAndSelectedFileTask.Wait(); - _webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); + _webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); - Logger.LogInfo("Navigates to string of HTML file"); + Logger.LogInfo("Navigates to string of HTML file"); - _webView.NavigateToString(_html); - _webView.NavigationCompleted += WebView2Init; - _webView.Height = this.Height; - _webView.Width = this.Width; - Controls.Add(_webView); - _webView.SendToBack(); - _loadingBar.Value = 100; - this.Update(); - } - catch (NullReferenceException e) - { - Logger.LogError("NullReferenceException catched. Skipping exception.", e); - } - } - catch (WebView2RuntimeNotFoundException e) - { - Logger.LogWarning("WebView2 was not found:"); - Logger.LogWarning(e.Message); - Controls.Remove(_loading); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); + _webView.NavigateToString(_html); + _webView.NavigationCompleted += WebView2Init; + _webView.Height = this.Height; + _webView.Width = this.Width; + Controls.Add(_webView); + _webView.SendToBack(); + _loadingBar.Value = 100; + this.Update(); + } + catch (NullReferenceException e) + { + Logger.LogError("NullReferenceException catched. Skipping exception.", e); + } + } + catch (WebView2RuntimeNotFoundException e) + { + Logger.LogWarning("WebView2 was not found:"); + Logger.LogWarning(e.Message); + Controls.Remove(_loading); + Controls.Remove(_loadingBar); + Controls.Remove(_loadingBackground); - // WebView2 not installed message - Label errorMessage = new Label(); - errorMessage.Text = Resources.WebView2_Not_Installed_Message; - errorMessage.Width = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Width + 10; - errorMessage.Height = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height; - Controls.Add(errorMessage); + // WebView2 not installed message + Label errorMessage = new Label(); + errorMessage.Text = Resources.WebView2_Not_Installed_Message; + errorMessage.Width = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Width + 10; + errorMessage.Height = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height; + Controls.Add(errorMessage); - // Download Link - Label downloadLink = new LinkLabel(); - downloadLink.Text = Resources.Download_WebView2; - downloadLink.Click += DownloadLink_Click; - downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10; - downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10; - downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height; - Controls.Add(downloadLink); - } - }); - }); + // Download Link + Label downloadLink = new LinkLabel(); + downloadLink.Text = Resources.Download_WebView2; + downloadLink.Click += DownloadLink_Click; + downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10; + downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10; + downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height; + Controls.Add(downloadLink); + } }); } catch (Exception e) { - InvokeOnControlThread(() => - { - Controls.Remove(_loading); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label text = new Label(); - text.Text = Resources.Exception_Occurred; - text.Text += e.Message; - text.Text += "\n" + e.Source; - text.Text += "\n" + e.StackTrace; - text.Width = 500; - text.Height = 10000; - Controls.Add(text); - Logger.LogError(e.Message); - }); + Controls.Remove(_loading); + Controls.Remove(_loadingBar); + Controls.Remove(_loadingBackground); + Label text = new Label(); + text.Text = Resources.Exception_Occurred; + text.Text += e.Message; + text.Text += "\n" + e.Source; + text.Text += "\n" + e.StackTrace; + text.Width = 500; + text.Height = 10000; + Controls.Add(text); + Logger.LogError(e.Message); } this.Resize += FormResize; @@ -249,18 +237,16 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco else { Logger.LogInfo("File is too big to display. Showing error message"); - InvokeOnControlThread(() => - { - Controls.Remove(_loading); - _loadingBar.Dispose(); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label errorMessage = new Label(); - errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture); - errorMessage.Width = 500; - errorMessage.Height = 50; - Controls.Add(errorMessage); - }); + + Controls.Remove(_loading); + _loadingBar.Dispose(); + Controls.Remove(_loadingBar); + Controls.Remove(_loadingBackground); + Label errorMessage = new Label(); + errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture); + errorMessage.Width = 500; + errorMessage.Height = 50; + Controls.Add(errorMessage); } } @@ -350,46 +336,41 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco private void SetBackground() { Logger.LogTrace(); - InvokeOnControlThread(() => - { - this.BackColor = Settings.BackgroundColor; - }); + this.BackColor = Settings.BackgroundColor; } private void InitializeLoadingScreen() { Logger.LogTrace(); - InvokeOnControlThread(() => - { - _loadingBackground = new Label(); - _loadingBackground.BackColor = Settings.BackgroundColor; - _loadingBackground.Width = this.Width; - _loadingBackground.Height = this.Height; - Controls.Add(_loadingBackground); - _loadingBackground.BringToFront(); + _loadingBackground = new Label(); + _loadingBackground.BackColor = Settings.BackgroundColor; + _loadingBackground.Width = this.Width; + _loadingBackground.Height = this.Height; + Controls.Add(_loadingBackground); + _loadingBackground.BringToFront(); - _loadingBar = new ProgressBar(); - _loadingBar.Width = this.Width - 10; - _loadingBar.Location = new Point(5, this.Height / 2); - _loadingBar.Maximum = 100; - _loadingBar.Value = 10; - Controls.Add(_loadingBar); + _loadingBar = new ProgressBar(); + _loadingBar.Width = this.Width - 10; + _loadingBar.Location = new Point(5, this.Height / 2); + _loadingBar.Maximum = 100; + _loadingBar.Value = 10; + Controls.Add(_loadingBar); - _loading = new Label(); - _loading.Text = Resources.Loading_Screen_Message; - _loading.Width = this.Width; - _loading.Height = 45; - _loading.Location = new Point(0, _loadingBar.Location.Y - _loading.Height); - _loading.TextAlign = ContentAlignment.TopCenter; - _loading.Font = new Font("MS Sans Serif", 16, FontStyle.Bold); - _loading.ForeColor = Settings.TextColor; - Controls.Add(_loading); + _loading = new Label(); + _loading.Text = Resources.Loading_Screen_Message; + _loading.Width = this.Width; + _loading.Height = 45; + _loading.Location = new Point(0, _loadingBar.Location.Y - _loading.Height); + _loading.TextAlign = ContentAlignment.TopCenter; + _loading.Font = new Font("MS Sans Serif", 16, FontStyle.Bold); + _loading.ForeColor = Settings.TextColor; + Controls.Add(_loading); - _loading.BringToFront(); - _loadingBar.BringToFront(); + _loading.BringToFront(); + _loadingBar.BringToFront(); + + this.Update(); - this.Update(); - }); Logger.LogInfo("Loading screen initialized"); } diff --git a/src/modules/previewpane/MonacoPreviewHandler/Program.cs b/src/modules/previewpane/MonacoPreviewHandler/Program.cs new file mode 100644 index 0000000000..c5d00f2725 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandler/Program.cs @@ -0,0 +1,63 @@ +// 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. + +namespace Microsoft.PowerToys.PreviewHandler.Monaco +{ + using System.Globalization; + using System.Windows.Threading; + using Common.UI; + using interop; + + internal static class Program + { + private static CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + private static MonacoPreviewHandlerControl _previewHandlerControl; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 6) + { + string filePath = args[0]; + int hwnd = Convert.ToInt32(args[1], 16); + + Rectangle s = default(Rectangle); + int left = Convert.ToInt32(args[2], 10); + int right = Convert.ToInt32(args[3], 10); + int top = Convert.ToInt32(args[4], 10); + int bottom = Convert.ToInt32(args[5], 10); + + _previewHandlerControl = new MonacoPreviewHandlerControl(); + _previewHandlerControl.SetWindow((IntPtr)hwnd, s); + _previewHandlerControl.DoPreview(filePath); + + NativeEventWaiter.WaitForEventLoop( + Constants.DevFilesPreviewResizeEvent(), + () => + { + Rectangle s = default(Rectangle); + _previewHandlerControl.SetRect(s); + }, + Dispatcher.CurrentDispatcher, + _tokenSource.Token); + } + else + { + MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + Application.Run(); + } + } +} diff --git a/src/modules/previewpane/MonacoPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/previewpane/MonacoPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml index 74181a551d..d8a1a0ab2d 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/previewpane/MonacoPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview win10-$(Platform) - false + true False False false diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.cpp b/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.cpp new file mode 100644 index 0000000000..6aafa46a6e --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "MonacoPreviewHandler.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + MonacoPreviewHandler* pExt = new (std::nothrow) MonacoPreviewHandler(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.h b/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/GlobalExportFunctions.def b/src/modules/previewpane/MonacoPreviewHandlerCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp new file mode 100644 index 0000000000..740769c5dd --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.cpp @@ -0,0 +1,262 @@ +#include "pch.h" +#include "MonacoPreviewHandler.h" + +#include +#include +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +MonacoPreviewHandler::MonacoPreviewHandler() : + m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL) +{ + m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::DEV_FILES_PREVIEW_RESIZE_EVENT); + + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::monacoPrevLogPath); + Logger::init(LogSettings::monacoPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +MonacoPreviewHandler::~MonacoPreviewHandler() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP MonacoPreviewHandler::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(MonacoPreviewHandler, IPreviewHandler), + QITABENT(MonacoPreviewHandler, IInitializeWithFile), + QITABENT(MonacoPreviewHandler, IPreviewHandlerVisuals), + QITABENT(MonacoPreviewHandler, IOleWindow), + QITABENT(MonacoPreviewHandler, IObjectWithSite), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +MonacoPreviewHandler::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +MonacoPreviewHandler::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithFile + +IFACEMETHODIMP MonacoPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode) +{ + m_filePath = pszFilePath; + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandler + +IFACEMETHODIMP MonacoPreviewHandler::SetWindow(HWND hwnd, const RECT* prc) +{ + if (hwnd && prc) + { + m_hwndParent = hwnd; + m_rcParent = *prc; + } + return S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::SetFocus() +{ + return S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::QueryFocus(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = ::GetFocus(); + if (*phwnd) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +IFACEMETHODIMP MonacoPreviewHandler::TranslateAccelerator(MSG* pmsg) +{ + HRESULT hr = S_FALSE; + IPreviewHandlerFrame* pFrame = NULL; + if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame))) + { + hr = pFrame->TranslateAccelerator(pmsg); + + pFrame->Release(); + } + return hr; +} + +IFACEMETHODIMP MonacoPreviewHandler::SetRect(const RECT* prc) +{ + HRESULT hr = E_INVALIDARG; + if (prc != NULL) + { + if (!m_resizeEvent) + { + Logger::error(L"Failed to create resize event for MonacoPreviewHandler"); + } + else + { + if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom) + { + if (!SetEvent(m_resizeEvent)) + { + Logger::error(L"Failed to signal resize event for MonacoPreviewHandler"); + } + } + } + m_rcParent = *prc; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP MonacoPreviewHandler::DoPreview() +{ + try + { + Logger::info(L"Starting MonacoPreviewHandler.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + m_filePath + L"\"" }; + cmdLine += L" "; + std::wostringstream ss; + ss << std::hex << m_hwndParent; + + cmdLine += ss.str(); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.left); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.right); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.top); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.bottom); + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.MonacoPreviewHandler.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start MonacoPreviewHandler.exe. Error: {}", errorMessage); + } + + return S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::Unload() +{ + Logger::info(L"Unload and terminate .exe"); + + m_hwndParent = NULL; + TerminateProcess(m_process, 0); + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandlerVisuals + +IFACEMETHODIMP MonacoPreviewHandler::SetBackgroundColor(COLORREF color) +{ + return S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::SetFont(const LOGFONTW* plf) +{ + return S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::SetTextColor(COLORREF color) +{ + return S_OK; +} + +#pragma endregion + +#pragma region IOleWindow + +IFACEMETHODIMP MonacoPreviewHandler::GetWindow(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = m_hwndParent; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP MonacoPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +#pragma endregion + +#pragma region IObjectWithSite + +IFACEMETHODIMP MonacoPreviewHandler::SetSite(IUnknown* punkSite) +{ + if (m_punkSite) + { + m_punkSite->Release(); + m_punkSite = NULL; + } + return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK; +} + +IFACEMETHODIMP MonacoPreviewHandler::GetSite(REFIID riid, void** ppv) +{ + *ppv = NULL; + return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.h b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.h new file mode 100644 index 0000000000..20b7ac1fba --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandler.h @@ -0,0 +1,70 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class MonacoPreviewHandler : + public IInitializeWithFile, + public IPreviewHandler, + public IPreviewHandlerVisuals, + public IOleWindow, + public IObjectWithSite +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithFile + IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc); + IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP QueryFocus(HWND* phwnd); + IFACEMETHODIMP TranslateAccelerator(MSG* pmsg); + IFACEMETHODIMP SetRect(const RECT* prc); + IFACEMETHODIMP DoPreview(); + IFACEMETHODIMP Unload(); + + // IPreviewHandlerVisuals + IFACEMETHODIMP SetBackgroundColor(COLORREF color); + IFACEMETHODIMP SetFont(const LOGFONTW* plf); + IFACEMETHODIMP SetTextColor(COLORREF color); + + // IOleWindow + IFACEMETHODIMP GetWindow(HWND* phwnd); + IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IObjectWithSite + IFACEMETHODIMP SetSite(IUnknown* punkSite); + IFACEMETHODIMP GetSite(REFIID riid, void** ppv); + + MonacoPreviewHandler(); +protected: + ~MonacoPreviewHandler(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + std::wstring m_filePath; + + // Parent window that hosts the previewer window. + // Note: do NOT DestroyWindow this. + HWND m_hwndParent; + // Bounding rect of the parent window. + RECT m_rcParent; + + // Site pointer from host, used to get IPreviewHandlerFrame. + IUnknown* m_punkSite; + + HANDLE m_process; + + HANDLE m_resizeEvent; +}; \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.rc b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj new file mode 100644 index 0000000000..41098d30e4 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj @@ -0,0 +1,119 @@ + + + + + 16.0 + Win32Proj + {b3e869c4-8210-4ebd-a621-ff4c4afcbfa9} + MonacoPreviewHandlerCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + + Create + + + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj.filters b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj.filters new file mode 100644 index 0000000000..911f755363 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/dllmain.cpp b/src/modules/previewpane/MonacoPreviewHandlerCpp/dllmain.cpp new file mode 100644 index 0000000000..11a7a5fb11 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {D8034CFA-F34B-41FE-AD45-62FCBB52A6DA} +static const GUID CLSID_MonacoPreviewHandler = { 0xd8034cfa, 0xf34b, 0x41fe, { 0xad, 0x45, 0x62, 0xfc, 0xbb, 0x52, 0xa6, 0xda } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_MonacoPreviewHandler, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/packages.config b/src/modules/previewpane/MonacoPreviewHandlerCpp/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.cpp b/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.h b/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/MonacoPreviewHandlerCpp/resource.h b/src/modules/previewpane/MonacoPreviewHandlerCpp/resource.h new file mode 100644 index 0000000000..278191cab1 --- /dev/null +++ b/src/modules/previewpane/MonacoPreviewHandlerCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Dev Files Preview Handler Module" +#define INTERNAL_NAME "PowerToys.MonacoPreviewHandlerCpp" +#define ORIGINAL_FILENAME "PowerToys.MonacoPreviewHandlerCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.cs b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.cs deleted file mode 100644 index 8c91030447..0000000000 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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; -using Microsoft.PowerToys.Telemetry; - -namespace Microsoft.PowerToys.PreviewHandler.Pdf -{ - /// - /// Implementation of preview handler for pdf files. - /// - [Guid("07665729-6243-4746-95b7-79579308d1b2")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class PdfPreviewHandler : StreamBasedPreviewHandler, IDisposable - { - private PdfPreviewHandlerControl _pdfPreviewHandlerControl; - private bool _disposedValue; - - /// - /// Initializes a new instance of the class. - /// - public PdfPreviewHandler() - { - Initialize(); - } - - /// - public override void DoPreview() - { - _pdfPreviewHandlerControl.DoPreview(Stream); - } - - /// - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.PdfFileHandlerLoaded()); - _pdfPreviewHandlerControl = new PdfPreviewHandlerControl(); - - return _pdfPreviewHandlerControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - _pdfPreviewHandlerControl.Dispose(); - } - - _disposedValue = true; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj index c24a2385cf..9624934c81 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj @@ -1,5 +1,7 @@  + enable + true true PowerToys.PdfPreviewHandler PowerToys PdfPreviewHandler @@ -10,13 +12,21 @@ false true true + true + + + + + win10-x64 + + + win10-arm64 {69E1EE8D-143A-4060-9129-4658ACF14AAF} Microsoft.PowerToys.PreviewHandler.Pdf net7.0-windows10.0.19041.0 - true PowerToys.PdfPreviewHandler @@ -40,6 +50,7 @@ $(NoWarn);1591 + WinExe @@ -48,7 +59,9 @@ + + diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs index e59a976793..ed1e94ee19 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs @@ -55,12 +55,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPdfPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. Show an error message instead. - InvokeOnControlThread(() => - { - _infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText); - Controls.Add(_infoBar); - base.DoPreview(dataSource); - }); + _infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText); + Controls.Add(_infoBar); + base.DoPreview(dataSource); return; } @@ -69,7 +66,12 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf try { - using (var dataStream = new ReadonlyStream(dataSource as IStream)) + if (!(dataSource is string filePath)) + { + throw new ArgumentException($"{nameof(dataSource)} for {nameof(PdfPreviewHandlerControl)} must be a string but was a '{typeof(T)}'"); + } + + using (var dataStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { var memStream = new MemoryStream(); dataStream.CopyTo(memStream); @@ -82,77 +84,71 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf if (pdf.PageCount > 0) { - InvokeOnControlThread(() => + _flowLayoutPanel = new FlowLayoutPanel { - _flowLayoutPanel = new FlowLayoutPanel - { - AutoScroll = true, - AutoSize = true, - Dock = DockStyle.Fill, - FlowDirection = FlowDirection.TopDown, - WrapContents = false, - }; - _flowLayoutPanel.Resize += FlowLayoutPanel_Resize; + AutoScroll = true, + AutoSize = true, + Dock = DockStyle.Fill, + FlowDirection = FlowDirection.TopDown, + WrapContents = false, + }; + _flowLayoutPanel.Resize += FlowLayoutPanel_Resize; - // Only show first 10 pages. - for (uint i = 0; i < pdf.PageCount && i < 10; i++) + // Only show first 10 pages. + for (uint i = 0; i < pdf.PageCount && i < 10; i++) + { + using (var page = pdf.GetPage(i)) { - using (var page = pdf.GetPage(i)) + var image = PageToImage(page); + + var picturePanel = new Panel() { - var image = PageToImage(page); - - var picturePanel = new Panel() - { - Name = "picturePanel", - Margin = new Padding(6, 6, 6, 0), - Size = CalculateSize(image), - BorderStyle = BorderStyle.FixedSingle, - }; - - var picture = new PictureBox - { - Dock = DockStyle.Fill, - Image = image, - SizeMode = PictureBoxSizeMode.Zoom, - }; - - picturePanel.Controls.Add(picture); - _flowLayoutPanel.Controls.Add(picturePanel); - } - } - - if (pdf.PageCount > 10) - { - var messageBox = new RichTextBox - { - Name = "messageBox", - Text = Resources.PdfMorePagesMessage, - BackColor = Color.LightYellow, - Dock = DockStyle.Fill, - Multiline = true, - ReadOnly = true, - ScrollBars = RichTextBoxScrollBars.None, - BorderStyle = BorderStyle.None, + Name = "picturePanel", + Margin = new Padding(6, 6, 6, 0), + Size = CalculateSize(image), + BorderStyle = BorderStyle.FixedSingle, }; - messageBox.ContentsResized += RTBContentsResized; - _flowLayoutPanel.Controls.Add(messageBox); + var picture = new PictureBox + { + Dock = DockStyle.Fill, + Image = image, + SizeMode = PictureBoxSizeMode.Zoom, + }; + + picturePanel.Controls.Add(picture); + _flowLayoutPanel.Controls.Add(picturePanel); } + } - Controls.Add(_flowLayoutPanel); - }); + if (pdf.PageCount > 10) + { + var messageBox = new RichTextBox + { + Name = "messageBox", + Text = Resources.PdfMorePagesMessage, + BackColor = Color.LightYellow, + Dock = DockStyle.Fill, + Multiline = true, + ReadOnly = true, + ScrollBars = RichTextBoxScrollBars.None, + BorderStyle = BorderStyle.None, + }; + messageBox.ContentsResized += RTBContentsResized; + + _flowLayoutPanel.Controls.Add(messageBox); + } + + Controls.Add(_flowLayoutPanel); } } catch (Exception ex) { if (ex.Message.Contains("Unable to update the password. The value provided as the current password is incorrect.", StringComparison.Ordinal)) { - InvokeOnControlThread(() => - { - Controls.Clear(); - _infoBar = GetTextBoxControl(Resources.PdfPasswordProtectedError); - Controls.Add(_infoBar); - }); + Controls.Clear(); + _infoBar = GetTextBoxControl(Resources.PdfPasswordProtectedError); + Controls.Add(_infoBar); } else { @@ -171,12 +167,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf { PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewError { Message = ex.Message }); - InvokeOnControlThread(() => - { - Controls.Clear(); - _infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError); - Controls.Add(_infoBar); - }); + Controls.Clear(); + _infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError); + Controls.Add(_infoBar); } finally { diff --git a/src/modules/previewpane/PdfPreviewHandler/Program.cs b/src/modules/previewpane/PdfPreviewHandler/Program.cs new file mode 100644 index 0000000000..8dadc8205e --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandler/Program.cs @@ -0,0 +1,63 @@ +// 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. + +namespace Microsoft.PowerToys.PreviewHandler.Pdf +{ + using System.Globalization; + using System.Windows.Threading; + using Common.UI; + using interop; + + internal static class Program + { + private static CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + private static PdfPreviewHandlerControl _previewHandlerControl; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 6) + { + string filePath = args[0]; + int hwnd = Convert.ToInt32(args[1], 16); + + Rectangle s = default(Rectangle); + int left = Convert.ToInt32(args[2], 10); + int right = Convert.ToInt32(args[3], 10); + int top = Convert.ToInt32(args[4], 10); + int bottom = Convert.ToInt32(args[5], 10); + + _previewHandlerControl = new PdfPreviewHandlerControl(); + _previewHandlerControl.SetWindow((IntPtr)hwnd, s); + _previewHandlerControl.DoPreview(filePath); + + NativeEventWaiter.WaitForEventLoop( + Constants.PdfPreviewResizeEvent(), + () => + { + Rectangle s = default(Rectangle); + _previewHandlerControl.SetRect(s); + }, + Dispatcher.CurrentDispatcher, + _tokenSource.Token); + } + else + { + MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + Application.Run(); + } + } +} diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.cpp b/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.cpp new file mode 100644 index 0000000000..21291c90a1 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "PdfPreviewHandler.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + PdfPreviewHandler* pExt = new (std::nothrow) PdfPreviewHandler(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.h b/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/GlobalExportFunctions.def b/src/modules/previewpane/PdfPreviewHandlerCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp new file mode 100644 index 0000000000..8ff6e50af6 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.cpp @@ -0,0 +1,261 @@ +#include "pch.h" +#include "PdfPreviewHandler.h" + +#include +#include +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +PdfPreviewHandler::PdfPreviewHandler() : + m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL) +{ + m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::PDF_PREVIEW_RESIZE_EVENT); + + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::pdfPrevLogPath); + Logger::init(LogSettings::pdfPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +PdfPreviewHandler::~PdfPreviewHandler() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP PdfPreviewHandler::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(PdfPreviewHandler, IPreviewHandler), + QITABENT(PdfPreviewHandler, IInitializeWithFile), + QITABENT(PdfPreviewHandler, IPreviewHandlerVisuals), + QITABENT(PdfPreviewHandler, IOleWindow), + QITABENT(PdfPreviewHandler, IObjectWithSite), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +PdfPreviewHandler::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +PdfPreviewHandler::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithFile + +IFACEMETHODIMP PdfPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode) +{ + m_filePath = pszFilePath; + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandler + +IFACEMETHODIMP PdfPreviewHandler::SetWindow(HWND hwnd, const RECT* prc) +{ + if (hwnd && prc) + { + m_hwndParent = hwnd; + m_rcParent = *prc; + } + return S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::SetFocus() +{ + return S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::QueryFocus(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = ::GetFocus(); + if (*phwnd) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +IFACEMETHODIMP PdfPreviewHandler::TranslateAccelerator(MSG* pmsg) +{ + HRESULT hr = S_FALSE; + IPreviewHandlerFrame* pFrame = NULL; + if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame))) + { + hr = pFrame->TranslateAccelerator(pmsg); + + pFrame->Release(); + } + return hr; +} + +IFACEMETHODIMP PdfPreviewHandler::SetRect(const RECT* prc) +{ + HRESULT hr = E_INVALIDARG; + if (prc != NULL) + { + if (!m_resizeEvent) + { + Logger::error(L"Failed to create resize event for PdfPreviewHandler"); + } + else + { + if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom) + { + if (!SetEvent(m_resizeEvent)) + { + Logger::error(L"Failed to signal resize event for PdfPreviewHandler"); + } + } + } + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP PdfPreviewHandler::DoPreview() +{ + try + { + Logger::info(L"Starting PdfPreviewHandler.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + m_filePath + L"\"" }; + cmdLine += L" "; + std::wostringstream ss; + ss << std::hex << m_hwndParent; + + cmdLine += ss.str(); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.left); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.right); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.top); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.bottom); + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.PdfPreviewHandler.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start PdfPreviewHandler.exe. Error: {}", errorMessage); + } + + return S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::Unload() +{ + Logger::info(L"Unload and terminate .exe"); + + m_hwndParent = NULL; + TerminateProcess(m_process, 0); + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandlerVisuals + +IFACEMETHODIMP PdfPreviewHandler::SetBackgroundColor(COLORREF color) +{ + return S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::SetFont(const LOGFONTW* plf) +{ + return S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::SetTextColor(COLORREF color) +{ + return S_OK; +} + +#pragma endregion + +#pragma region IOleWindow + +IFACEMETHODIMP PdfPreviewHandler::GetWindow(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = m_hwndParent; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP PdfPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +#pragma endregion + +#pragma region IObjectWithSite + +IFACEMETHODIMP PdfPreviewHandler::SetSite(IUnknown* punkSite) +{ + if (m_punkSite) + { + m_punkSite->Release(); + m_punkSite = NULL; + } + return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK; +} + +IFACEMETHODIMP PdfPreviewHandler::GetSite(REFIID riid, void** ppv) +{ + *ppv = NULL; + return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.h b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.h new file mode 100644 index 0000000000..66ebf68ee4 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandler.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class PdfPreviewHandler : + public IInitializeWithFile, + public IPreviewHandler, + public IPreviewHandlerVisuals, + public IOleWindow, + public IObjectWithSite +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithFile + IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc); + IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP QueryFocus(HWND* phwnd); + IFACEMETHODIMP TranslateAccelerator(MSG* pmsg); + IFACEMETHODIMP SetRect(const RECT* prc); + IFACEMETHODIMP DoPreview(); + IFACEMETHODIMP Unload(); + + // IPreviewHandlerVisuals + IFACEMETHODIMP SetBackgroundColor(COLORREF color); + IFACEMETHODIMP SetFont(const LOGFONTW* plf); + IFACEMETHODIMP SetTextColor(COLORREF color); + + // IOleWindow + IFACEMETHODIMP GetWindow(HWND* phwnd); + IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IObjectWithSite + IFACEMETHODIMP SetSite(IUnknown* punkSite); + IFACEMETHODIMP GetSite(REFIID riid, void** ppv); + + PdfPreviewHandler(); +protected: + ~PdfPreviewHandler(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + std::wstring m_filePath; + + // Parent window that hosts the previewer window. + // Note: do NOT DestroyWindow this. + HWND m_hwndParent; + // Bounding rect of the parent window. + RECT m_rcParent; + + // Site pointer from host, used to get IPreviewHandlerFrame. + IUnknown* m_punkSite; + + HANDLE m_process; + HANDLE m_resizeEvent; +}; \ No newline at end of file diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.rc b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj new file mode 100644 index 0000000000..2eca59b53a --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj @@ -0,0 +1,119 @@ + + + + + 16.0 + Win32Proj + {54f7c616-fd41-4e62-bff9-015686914f4d} + PdfPreviewHandlerCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + Create + + + + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj.filters b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj.filters new file mode 100644 index 0000000000..ba7aa21bca --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/dllmain.cpp b/src/modules/previewpane/PdfPreviewHandlerCpp/dllmain.cpp new file mode 100644 index 0000000000..9df21544f7 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {A5A41CC7-02CB-41D4-8C9B-9087040D6098} +static const GUID CLSID_PdfPreviewHandler = { 0xa5a41cc7, 0x2cb, 0x41d4, { 0x8c, 0x9b, 0x90, 0x87, 0x4, 0xd, 0x60, 0x98 } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_PdfPreviewHandler, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/packages.config b/src/modules/previewpane/PdfPreviewHandlerCpp/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/pch.cpp b/src/modules/previewpane/PdfPreviewHandlerCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/pch.h b/src/modules/previewpane/PdfPreviewHandlerCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/PdfPreviewHandlerCpp/resource.h b/src/modules/previewpane/PdfPreviewHandlerCpp/resource.h new file mode 100644 index 0000000000..50e5cfb9e2 --- /dev/null +++ b/src/modules/previewpane/PdfPreviewHandlerCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Pdf Preview Handler Module" +#define INTERNAL_NAME "PowerToys.PdfPreviewHandlerCpp" +#define ORIGINAL_FILENAME "PowerToys.PdfPreviewHandlerCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs index a38d6813b5..0852369f96 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs @@ -16,49 +16,50 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf /// /// PDF Thumbnail Provider. /// - [Guid("BCC13D15-9720-4CC4-8371-EA74A274741E")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class PdfThumbnailProvider : IInitializeWithStream, IThumbnailProvider + public class PdfThumbnailProvider { + public PdfThumbnailProvider(string filePath) + { + FilePath = filePath; + Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + } + + /// + /// Gets the file path to the file creating thumbnail for. + /// + public string FilePath { get; private set; } + /// /// Gets the stream object to access file. /// - public IStream Stream { get; private set; } + public Stream Stream { get; private set; } /// /// The maximum dimension (width or height) thumbnail we will generate. /// private const uint MaxThumbnailSize = 10000; - /// - public void Initialize(IStream pstream, uint grfMode) + /// + /// Generate thumbnail bitmap for provided Pdf file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + public Bitmap GetThumbnail(uint cx) { - // Ignore the grfMode always use read mode to access the file. - this.Stream = pstream; - } - - /// - public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha) - { - phbmp = IntPtr.Zero; - pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN; - if (cx == 0 || cx > MaxThumbnailSize) { - return; + return null; } if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPdfThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. - return; + return null; } - using var dataStream = new ReadonlyStream(this.Stream as IStream); using var memStream = new MemoryStream(); - dataStream.CopyTo(memStream); + this.Stream.CopyTo(memStream); memStream.Position = 0; // AsRandomAccessStream() extension method from System.Runtime.WindowsRuntime @@ -72,9 +73,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf using Bitmap thumbnail = new Bitmap(image); - phbmp = thumbnail.GetHbitmap(); - pdwAlpha = WTS_ALPHATYPE.WTSAT_RGB; + return (Bitmap)thumbnail.Clone(); } + + return null; } /// diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj index b206356328..af28de12f8 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj @@ -1,5 +1,6 @@ + enable true {11491FD8-F921-48BF-880C-7FEA185B80A1} Microsoft.PowerToys.ThumbnailHandler.Pdf @@ -7,13 +8,21 @@ PowerToys.PdfThumbnailProvider PowerToys PdfPreviewHandler net7.0-windows10.0.19041.0 - true true PowerToys PdfPreviewHandler $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ false false true + true + + + + + win10-x64 + + + win10-arm64 @@ -26,6 +35,7 @@ $(NoWarn);1591 + WinExe diff --git a/src/modules/previewpane/PdfThumbnailProvider/Program.cs b/src/modules/previewpane/PdfThumbnailProvider/Program.cs new file mode 100644 index 0000000000..0efbd0189e --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProvider/Program.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Microsoft.PowerToys.ThumbnailHandler.Pdf +{ + using System.Globalization; + + internal static class Program + { + private static PdfThumbnailProvider _thumbnailProvider; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 2) + { + string filePath = args[0]; + uint cx = Convert.ToUInt32(args[1], 10); + + _thumbnailProvider = new PdfThumbnailProvider(filePath); + Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); + filePath = filePath.Replace(".pdf", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } + else + { + MessageBox.Show("Gcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + } + } +} diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.cpp new file mode 100644 index 0000000000..4d9bd3a565 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "PdfThumbnailProvider.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + PdfThumbnailProvider* pExt = new (std::nothrow) PdfThumbnailProvider(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.h b/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/GlobalExportFunctions.def b/src/modules/previewpane/PdfThumbnailProviderCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp new file mode 100644 index 0000000000..312512a454 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp @@ -0,0 +1,181 @@ +#include "pch.h" +#include "PdfThumbnailProvider.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +PdfThumbnailProvider::PdfThumbnailProvider() : + m_cRef(1), m_pStream(NULL), m_process(NULL) +{ + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::pdfThumbLogPath); + Logger::init(LogSettings::pdfThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +PdfThumbnailProvider::~PdfThumbnailProvider() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP PdfThumbnailProvider::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(PdfThumbnailProvider, IThumbnailProvider), + QITABENT(PdfThumbnailProvider, IInitializeWithStream), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +PdfThumbnailProvider::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +PdfThumbnailProvider::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithStream + +IFACEMETHODIMP PdfThumbnailProvider::Initialize(IStream* pStream, DWORD grfMode) +{ + HRESULT hr = E_INVALIDARG; + if (pStream) + { + // Initialize can be called more than once, so release existing valid + // m_pStream. + if (m_pStream) + { + m_pStream->Release(); + m_pStream = NULL; + } + + m_pStream = pStream; + m_pStream->AddRef(); + hr = S_OK; + } + return hr; +} + +#pragma endregion + +#pragma region IThumbnailProvider + +IFACEMETHODIMP PdfThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha) +{ + // Read stream into the buffer + char buffer[4096]; + ULONG cbRead; + + Logger::trace(L"Begin"); + + GUID guid; + if (CoCreateGuid(&guid) == S_OK) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED(StringFromCLSID(guid, &guidString))) + { + Logger::info(L"Read stream and save to tmp file."); + + // {CLSID} -> CLSID + std::wstring guid = std::wstring(guidString.get()).substr(1, std::wstring(guidString.get()).size() - 2); + std::wstring filePath = PTSettingsHelper::get_local_low_folder_location() + L"\\PdfThumbnail-Temp\\"; + if (!std::filesystem::exists(filePath)) + { + std::filesystem::create_directories(filePath); + } + + std::wstring fileName = filePath + guid + L".pdf"; + + // Write data to tmp file + std::fstream file; + file.open(fileName, std::ios_base::out | std::ios_base::binary); + + if (!file.is_open()) + { + return 0; + } + + while (true) + { + auto result = m_pStream->Read(buffer, 4096, &cbRead); + + file.write(buffer, cbRead); + if (result == S_FALSE) + { + break; + } + } + file.close(); + + try + { + Logger::info(L"Start PdfThumbnailProvider.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + fileName + L"\"" }; + cmdLine += L" "; + cmdLine += std::to_wstring(cx); + + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.PdfThumbnailProvider.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + WaitForSingleObject(m_process, INFINITE); + std::filesystem::remove(fileName); + + std::wstring fileNameBmp = filePath + guid + L".bmp"; + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + + std::filesystem::remove(fileNameBmp); + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start PdfThumbnailProvider.exe. Error: {}", errorMessage); + } + } + } + + return S_OK; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h new file mode 100644 index 0000000000..9ae9b43445 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h @@ -0,0 +1,37 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class PdfThumbnailProvider : + public IInitializeWithStream, + public IThumbnailProvider +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithStream + IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); + + PdfThumbnailProvider(); +protected: + ~PdfThumbnailProvider(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + IStream* m_pStream; + + HANDLE m_process; +}; \ No newline at end of file diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.rc b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj new file mode 100644 index 0000000000..7690364542 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj @@ -0,0 +1,121 @@ + + + + + 16.0 + Win32Proj + {ca5518ed-0458-4b09-8f53-4122b9888655} + PdfThumbnailProviderCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + Create + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj.filters b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj.filters new file mode 100644 index 0000000000..25a0d0a880 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/dllmain.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/dllmain.cpp new file mode 100644 index 0000000000..e08a9c6adf --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {D8BB9942-93BD-412D-87E4-33FAB214DC1A} +static const GUID CLSID_PdfThumbnailProvider = { 0xd8bb9942, 0x93bd, 0x412d, { 0x87, 0xe4, 0x33, 0xfa, 0xb2, 0x14, 0xdc, 0x1a } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_PdfThumbnailProvider, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/packages.config b/src/modules/previewpane/PdfThumbnailProviderCpp/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/pch.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/pch.h b/src/modules/previewpane/PdfThumbnailProviderCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/resource.h b/src/modules/previewpane/PdfThumbnailProviderCpp/resource.h new file mode 100644 index 0000000000..209bddb345 --- /dev/null +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Pdf Thumbnail Provider Module" +#define INTERNAL_NAME "PowerToys.PdfThumbnailProviderCpp" +#define ORIGINAL_FILENAME "PowerToys.PdfThumbnailProviderCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/README.md b/src/modules/previewpane/README.md index 383548bd4f..10653a4811 100644 --- a/src/modules/previewpane/README.md +++ b/src/modules/previewpane/README.md @@ -6,122 +6,14 @@ ## 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 -{ - /// - /// Implementation of preview handler for .xyz files. - /// GUID = CLSID / CLASS ID. - /// - [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 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-->") -``` +- Add new C++ DLL project in the preview pane folder. This DLL is actual preview handler, i.e. implements IPreviewHandler/IThumbnailProvider interface. +- Add new .NET EXE project in the preview pane folder. This EXE is being spawned by C++ DLL to generate preview/thumbnail. ## Installation ### MSI (Recommended) -To add a new Previewer update the `Product.wxs` file in `PowerToysSetup` similar to existing Previewer to register the Preview Handler. More details about registration of Preview Handlers can be [found here.](https://learn.microsoft.com/windows/win32/shell/how-to-register-a-preview-handler) - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` +To add a new Previewer update the `Product.wxs` file in `PowerToysSetup` similar to existing Previewer to register the Preview Handler. More details about registration of Preview Handlers can be [found here.](https://learn.microsoft.com/windows/win32/shell/how-to-register-a-preview-handler) - implemented in [modulesRegistry.h](../../common/utils/modulesRegistry.h). ### Directly registering/unregistering DLL's **[Important] This method of registering Preview Handler DLL's is not recommended. It could lead to registry corruption.** diff --git a/src/modules/previewpane/StlThumbnailProvider/Program.cs b/src/modules/previewpane/StlThumbnailProvider/Program.cs new file mode 100644 index 0000000000..79e77c8913 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProvider/Program.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Microsoft.PowerToys.ThumbnailHandler.Stl +{ + using System.Globalization; + + internal static class Program + { + private static StlThumbnailProvider _thumbnailProvider; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 2) + { + string filePath = args[0]; + uint cx = Convert.ToUInt32(args[1], 10); + + _thumbnailProvider = new StlThumbnailProvider(filePath); + Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); + filePath = filePath.Replace(".stl", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } + else + { + MessageBox.Show("Gcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + } + } +} diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs index c944d7936b..c7d4ebdd14 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs @@ -1,33 +1,40 @@ // 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; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; -using Common.ComInterlop; using Common.Utilities; using HelixToolkit.Wpf; using Microsoft.PowerToys.Settings.UI.Library; using Bitmap = System.Drawing.Bitmap; +using Color = System.Windows.Media.Color; +using ColorConverter = System.Windows.Media.ColorConverter; namespace Microsoft.PowerToys.ThumbnailHandler.Stl { /// /// Stl Thumbnail Provider. /// - [Guid("8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class StlThumbnailProvider : IInitializeWithStream, IThumbnailProvider + public class StlThumbnailProvider { + public StlThumbnailProvider(string filePath) + { + FilePath = filePath; + Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + } + + /// + /// Gets the file path to the file creating thumbnail for. + /// + public string FilePath { get; private set; } + /// /// Gets the stream object to access file. /// - public IStream Stream { get; private set; } + public Stream Stream { get; private set; } /// /// The maximum dimension (width or height) thumbnail we will generate. @@ -63,7 +70,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl var viewport = new System.Windows.Controls.Viewport3D(); - viewport.Measure(new Size(cx, cx)); + viewport.Measure(new System.Windows.Size(cx, cx)); viewport.Arrange(new Rect(0, 0, cx, cx)); var modelVisual = new ModelVisual3D() @@ -105,48 +112,40 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl return thumbnail; } - /// - public void Initialize(IStream pstream, uint grfMode) + /// + /// Generate thumbnail bitmap for provided Gcode file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + public Bitmap GetThumbnail(uint cx) { - // Ignore the grfMode always use read mode to access the file. - this.Stream = pstream; - } - - /// - public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha) - { - phbmp = IntPtr.Zero; - pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN; - if (cx == 0 || cx > MaxThumbnailSize) { - return; + return null; } if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredStlThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. - return; + return null; } - using (var stream = new ReadonlyStream(this.Stream as IStream)) + using (var memStream = new MemoryStream()) { - using (var memStream = new MemoryStream()) + this.Stream.CopyTo(memStream); + + memStream.Position = 0; + + using (Bitmap thumbnail = GetThumbnail(memStream, cx)) { - stream.CopyTo(memStream); - - memStream.Position = 0; - - using (Bitmap thumbnail = GetThumbnail(memStream, cx)) + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - phbmp = thumbnail.GetHbitmap(System.Drawing.Color.Transparent); - pdwAlpha = WTS_ALPHATYPE.WTSAT_ARGB; - } + return (Bitmap)thumbnail.Clone(); } } } + + return null; } /// diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj index ebfd1c81fd..016716c50a 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj @@ -1,5 +1,6 @@  + enable true {F7C8C0F1-5431-4347-89D0-8E5354F93CF2} Microsoft.PowerToys.ThumbnailHandler.Stl @@ -7,7 +8,6 @@ PowerToys.StlThumbnailProvider PowerToys StlPreviewHandler net7.0-windows10.0.19041.0 - true true PowerToys StlPreviewHandler $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ @@ -15,6 +15,15 @@ false true true + true + + + + + win10-x64 + + + win10-arm64 @@ -27,6 +36,7 @@ $(NoWarn);1591 + WinExe diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.cpp new file mode 100644 index 0000000000..653a126aa6 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "StlThumbnailProvider.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + StlThumbnailProvider* pExt = new (std::nothrow) StlThumbnailProvider(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.h b/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/GlobalExportFunctions.def b/src/modules/previewpane/StlThumbnailProviderCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp new file mode 100644 index 0000000000..eada9c4b06 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp @@ -0,0 +1,181 @@ +#include "pch.h" +#include "StlThumbnailProvider.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +StlThumbnailProvider::StlThumbnailProvider() : + m_cRef(1), m_pStream(NULL), m_process(NULL) +{ + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::stlThumbLogPath); + Logger::init(LogSettings::stlThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +StlThumbnailProvider::~StlThumbnailProvider() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP StlThumbnailProvider::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(StlThumbnailProvider, IThumbnailProvider), + QITABENT(StlThumbnailProvider, IInitializeWithStream), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +StlThumbnailProvider::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +StlThumbnailProvider::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithStream + +IFACEMETHODIMP StlThumbnailProvider::Initialize(IStream* pStream, DWORD grfMode) +{ + HRESULT hr = E_INVALIDARG; + if (pStream) + { + // Initialize can be called more than once, so release existing valid + // m_pStream. + if (m_pStream) + { + m_pStream->Release(); + m_pStream = NULL; + } + + m_pStream = pStream; + m_pStream->AddRef(); + hr = S_OK; + } + return hr; +} + +#pragma endregion + +#pragma region IThumbnailProvider + +IFACEMETHODIMP StlThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha) +{ + // Read stream into the buffer + char buffer[4096]; + ULONG cbRead; + + Logger::trace(L"Begin"); + + GUID guid; + if (CoCreateGuid(&guid) == S_OK) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED(StringFromCLSID(guid, &guidString))) + { + Logger::info(L"Read stream and save to tmp file."); + + // {CLSID} -> CLSID + std::wstring guid = std::wstring(guidString.get()).substr(1, std::wstring(guidString.get()).size() - 2); + std::wstring filePath = PTSettingsHelper::get_local_low_folder_location() + L"\\StlThumbnail-Temp\\"; + if (!std::filesystem::exists(filePath)) + { + std::filesystem::create_directories(filePath); + } + + std::wstring fileName = filePath + guid + L".stl"; + + // Write data to tmp file + std::fstream file; + file.open(fileName, std::ios_base::out | std::ios_base::binary); + + if (!file.is_open()) + { + return 0; + } + + while (true) + { + auto result = m_pStream->Read(buffer, 4096, &cbRead); + + file.write(buffer, cbRead); + if (result == S_FALSE) + { + break; + } + } + file.close(); + + try + { + Logger::info(L"Start StlThumbnailProvider.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + fileName + L"\"" }; + cmdLine += L" "; + cmdLine += std::to_wstring(cx); + + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.StlThumbnailProvider.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + WaitForSingleObject(m_process, INFINITE); + std::filesystem::remove(fileName); + + std::wstring fileNameBmp = filePath + guid + L".bmp"; + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + + std::filesystem::remove(fileNameBmp); + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start StlThumbnailProvider.exe. Error: {}", errorMessage); + } + } + } + + return S_OK; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h new file mode 100644 index 0000000000..ea97d69c82 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h @@ -0,0 +1,37 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class StlThumbnailProvider : + public IInitializeWithStream, + public IThumbnailProvider +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithStream + IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); + + StlThumbnailProvider(); +protected: + ~StlThumbnailProvider(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + IStream* m_pStream; + + HANDLE m_process; +}; \ No newline at end of file diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.rc b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj new file mode 100644 index 0000000000..1d8956cb9f --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj @@ -0,0 +1,121 @@ + + + + + 16.0 + Win32Proj + {d6dcc3ae-18c0-488a-b978-baa9e3cff09d} + StlThumbnailProviderCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + Create + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj.filters b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj.filters new file mode 100644 index 0000000000..a787045e5e --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/dllmain.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/dllmain.cpp new file mode 100644 index 0000000000..e97a163a42 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {77257004-6F25-4521-B602-50ECC6EC62A6} +static const GUID CLSID_StlThumbnailProvider = { 0x77257004, 0x6f25, 0x4521, { 0xb6, 0x2, 0x50, 0xec, 0xc6, 0xec, 0x62, 0xa6 } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_StlThumbnailProvider, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/packages.config b/src/modules/previewpane/StlThumbnailProviderCpp/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/pch.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/pch.h b/src/modules/previewpane/StlThumbnailProviderCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/resource.h b/src/modules/previewpane/StlThumbnailProviderCpp/resource.h new file mode 100644 index 0000000000..9d8a12e219 --- /dev/null +++ b/src/modules/previewpane/StlThumbnailProviderCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Stl Thumbnail Provider Module" +#define INTERNAL_NAME "PowerToys.StlThumbnailProviderCpp" +#define ORIGINAL_FILENAME "PowerToys.StlThumbnailProviderCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/SvgPreviewHandler/Program.cs b/src/modules/previewpane/SvgPreviewHandler/Program.cs new file mode 100644 index 0000000000..f9c013b354 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/Program.cs @@ -0,0 +1,63 @@ +// 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. + +namespace Microsoft.PowerToys.PreviewHandler.Svg +{ + using System.Globalization; + using System.Windows.Threading; + using Common.UI; + using interop; + + internal static class Program + { + private static CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + private static SvgPreviewControl _previewHandlerControl; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 6) + { + string filePath = args[0]; + int hwnd = Convert.ToInt32(args[1], 16); + + Rectangle s = default(Rectangle); + int left = Convert.ToInt32(args[2], 10); + int right = Convert.ToInt32(args[3], 10); + int top = Convert.ToInt32(args[4], 10); + int bottom = Convert.ToInt32(args[5], 10); + + _previewHandlerControl = new SvgPreviewControl(); + _previewHandlerControl.SetWindow((IntPtr)hwnd, s); + _previewHandlerControl.DoPreview(filePath); + + NativeEventWaiter.WaitForEventLoop( + Constants.SvgPreviewResizeEvent(), + () => + { + Rectangle s = default(Rectangle); + _previewHandlerControl.SetRect(s); + }, + Dispatcher.CurrentDispatcher, + _tokenSource.Token); + } + else + { + MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + Application.Run(); + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/previewpane/SvgPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml index 74181a551d..d8a1a0ab2d 100644 --- a/src/modules/previewpane/SvgPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/previewpane/SvgPreviewHandler/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview win10-$(Platform) - false + true False False false diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs index cb0152f170..907dde51aa 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -2,16 +2,10 @@ // 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.IO; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices.ComTypes; -using System.Windows.Forms; using Common; using Common.Utilities; -using Microsoft.PowerToys.PreviewHandler.Svg.Properties; using Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events; using Microsoft.PowerToys.Telemetry; using Microsoft.Web.WebView2.Core; @@ -86,13 +80,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredSvgPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. Show an error message instead. - InvokeOnControlThread(() => - { - _infoBarAdded = true; - AddTextBoxControl(Properties.Resource.GpoDisabledErrorText); - Resize += FormResized; - base.DoPreview(dataSource); - }); + _infoBarAdded = true; + AddTextBoxControl(Properties.Resource.GpoDisabledErrorText); + Resize += FormResized; + base.DoPreview(dataSource); return; } @@ -104,7 +95,12 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg try { - using (var stream = new ReadonlyStream(dataSource as IStream)) + if (!(dataSource is string filePath)) + { + throw new ArgumentException($"{nameof(dataSource)} for {nameof(SvgPreviewControl)} must be a string but was a '{typeof(T)}'"); + } + + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { using (var reader = new StreamReader(stream)) { @@ -133,29 +129,26 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message }); } - InvokeOnControlThread(() => + try { - try - { - _infoBarAdded = false; + _infoBarAdded = false; - // Add a info bar on top of the Preview if any blocked element is present. - if (blocked) - { - _infoBarAdded = true; - AddTextBoxControl(Properties.Resource.BlockedElementInfoText); - } - - AddWebViewControl(svgData); - Resize += FormResized; - base.DoPreview(dataSource); - PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed()); - } - catch (Exception ex) + // Add a info bar on top of the Preview if any blocked element is present. + if (blocked) { - PreviewError(ex, dataSource); + _infoBarAdded = true; + AddTextBoxControl(Properties.Resource.BlockedElementInfoText); } - }); + + AddWebViewControl(svgData); + Resize += FormResized; + base.DoPreview(dataSource); + PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed()); + } + catch (Exception ex) + { + PreviewError(ex, dataSource); + } } /// @@ -207,49 +200,46 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg webView2EnvironmentAwaiter = CoreWebView2Environment .CreateAsync(userDataFolder: _webView2UserDataFolder, options: webView2Options) .ConfigureAwait(true).GetAwaiter(); - webView2EnvironmentAwaiter.OnCompleted(() => + webView2EnvironmentAwaiter.OnCompleted(async () => { - InvokeOnControlThread(async () => + try { - try + _webView2Environment = webView2EnvironmentAwaiter.GetResult(); + await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); + _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny); + _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; + _browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; + _browser.CoreWebView2.Settings.AreDevToolsEnabled = false; + _browser.CoreWebView2.Settings.AreHostObjectsAllowed = false; + _browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; + _browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; + _browser.CoreWebView2.Settings.IsScriptEnabled = false; + _browser.CoreWebView2.Settings.IsWebMessageEnabled = false; + + // Don't load any resources. + _browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); + _browser.CoreWebView2.WebResourceRequested += CoreWebView2_BlockExternalResources; + + // WebView2.NavigateToString() limitation + // See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks + // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes + if (svgData.Length > 1_500_000) { - _webView2Environment = webView2EnvironmentAwaiter.GetResult(); - await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny); - _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; - _browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; - _browser.CoreWebView2.Settings.AreDevToolsEnabled = false; - _browser.CoreWebView2.Settings.AreHostObjectsAllowed = false; - _browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; - _browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; - _browser.CoreWebView2.Settings.IsScriptEnabled = false; - _browser.CoreWebView2.Settings.IsWebMessageEnabled = false; - - // Don't load any resources. - _browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); - _browser.CoreWebView2.WebResourceRequested += CoreWebView2_BlockExternalResources; - - // WebView2.NavigateToString() limitation - // See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks - // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes - if (svgData.Length > 1_500_000) - { - string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; - File.WriteAllText(filename, svgData); - _localFileURI = new Uri(filename); - _browser.Source = _localFileURI; - } - else - { - _browser.NavigateToString(svgData); - } - - Controls.Add(_browser); + string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; + File.WriteAllText(filename, svgData); + _localFileURI = new Uri(filename); + _browser.Source = _localFileURI; } - catch (Exception) + else { + _browser.NavigateToString(svgData); } - }); + + Controls.Add(_browser); + } + catch (Exception) + { + } }); } diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs deleted file mode 100644 index 2061b03184..0000000000 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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; -using Microsoft.PowerToys.Telemetry; - -namespace Microsoft.PowerToys.PreviewHandler.Svg -{ - /// - /// Extends for Svg Preview Handler. - /// - [Guid("ddee2b8a-6807-48a6-bb20-2338174ff779")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class SvgPreviewHandler : StreamBasedPreviewHandler, IDisposable - { - private SvgPreviewControl _svgPreviewControl; - private bool disposedValue; - - /// - /// Initializes a new instance of the class. - /// - public SvgPreviewHandler() - { - Initialize(); - } - - /// - public override void DoPreview() - { - _svgPreviewControl.DoPreview(Stream); - } - - /// - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.SvgFileHandlerLoaded()); - _svgPreviewControl = new SvgPreviewControl(); - - return _svgPreviewControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - _svgPreviewControl.Dispose(); - } - - disposedValue = true; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj index a328991454..0bf93c0376 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj @@ -1,5 +1,7 @@ + enable + true true PowerToys.SvgPreviewHandler PowerToys SvgPreviewHandler @@ -12,12 +14,20 @@ true win10-x64;win10-arm64 net7.0-windows10.0.19041.0 + true + + + + + win10-x64 + + + win10-arm64 {DA425894-6E13-404F-8DCB-78584EC0557A} Microsoft.PowerToys.PreviewHandler.Svg - true PowerToys.SvgPreviewHandler @@ -41,10 +51,13 @@ $(NoWarn);1591 + WinExe + + diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.cpp b/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.cpp new file mode 100644 index 0000000000..9e5a4691e4 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "SvgPreviewHandler.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + SvgPreviewHandler* pExt = new (std::nothrow) SvgPreviewHandler(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.h b/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/GlobalExportFunctions.def b/src/modules/previewpane/SvgPreviewHandlerCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp new file mode 100644 index 0000000000..21841329a0 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.cpp @@ -0,0 +1,258 @@ +#include "pch.h" +#include "SvgPreviewHandler.h" + +#include +#include +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +SvgPreviewHandler::SvgPreviewHandler() : + m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL) +{ + m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::SVG_PREVIEW_RESIZE_EVENT); + + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::svgPrevLogPath); + Logger::init(LogSettings::svgPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +SvgPreviewHandler::~SvgPreviewHandler() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP SvgPreviewHandler::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(SvgPreviewHandler, IPreviewHandler), + QITABENT(SvgPreviewHandler, IInitializeWithFile), + QITABENT(SvgPreviewHandler, IPreviewHandlerVisuals), + QITABENT(SvgPreviewHandler, IOleWindow), + QITABENT(SvgPreviewHandler, IObjectWithSite), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +SvgPreviewHandler::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +SvgPreviewHandler::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithFile + +IFACEMETHODIMP SvgPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode) +{ + m_filePath = pszFilePath; + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandler + +IFACEMETHODIMP SvgPreviewHandler::SetWindow(HWND hwnd, const RECT* prc) +{ + if (hwnd && prc) + { + m_hwndParent = hwnd; + m_rcParent = *prc; + } + return S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::SetFocus() +{ + return S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::QueryFocus(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = ::GetFocus(); + if (*phwnd) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +IFACEMETHODIMP SvgPreviewHandler::TranslateAccelerator(MSG* pmsg) +{ + HRESULT hr = S_FALSE; + IPreviewHandlerFrame* pFrame = NULL; + if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame))) + { + hr = pFrame->TranslateAccelerator(pmsg); + + pFrame->Release(); + } + return hr; +} + +IFACEMETHODIMP SvgPreviewHandler::SetRect(const RECT* prc) +{ + HRESULT hr = E_INVALIDARG; + if (prc != NULL) + { + if (!m_resizeEvent) + { + Logger::error(L"Failed to create resize event for SvgPreviewHandler"); + } + else + { + if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom) + { + if (!SetEvent(m_resizeEvent)) + { + Logger::error(L"Failed to signal resize event for SvgPreviewHandler"); + } + } + } + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP SvgPreviewHandler::DoPreview() +{ + try + { + Logger::info(L"Starting SvgPreviewHandler.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + m_filePath + L"\"" }; + cmdLine += L" "; + std::wostringstream ss; + ss << std::hex << m_hwndParent; + + cmdLine += ss.str(); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.left); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.right); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.top); + cmdLine += L" "; + cmdLine += std::to_wstring(m_rcParent.bottom); + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.SvgPreviewHandler.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start SvgPreviewHandler.exe. Error: {}", errorMessage); + } + + return S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::Unload() +{ + TerminateProcess(m_process, 0); + return S_OK; +} + +#pragma endregion + +#pragma region IPreviewHandlerVisuals + +IFACEMETHODIMP SvgPreviewHandler::SetBackgroundColor(COLORREF color) +{ + return S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::SetFont(const LOGFONTW* plf) +{ + return S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::SetTextColor(COLORREF color) +{ + return S_OK; +} + +#pragma endregion + +#pragma region IOleWindow + +IFACEMETHODIMP SvgPreviewHandler::GetWindow(HWND* phwnd) +{ + HRESULT hr = E_INVALIDARG; + if (phwnd) + { + *phwnd = m_hwndParent; + hr = S_OK; + } + return hr; +} + +IFACEMETHODIMP SvgPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode) +{ + return E_NOTIMPL; +} + +#pragma endregion + +#pragma region IObjectWithSite + +IFACEMETHODIMP SvgPreviewHandler::SetSite(IUnknown* punkSite) +{ + if (m_punkSite) + { + m_punkSite->Release(); + m_punkSite = NULL; + } + return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK; +} + +IFACEMETHODIMP SvgPreviewHandler::GetSite(REFIID riid, void** ppv) +{ + *ppv = NULL; + return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.h b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.h new file mode 100644 index 0000000000..c1c20e8ad0 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandler.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class SvgPreviewHandler : + public IInitializeWithFile, + public IPreviewHandler, + public IPreviewHandlerVisuals, + public IOleWindow, + public IObjectWithSite +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithFile + IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc); + IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP QueryFocus(HWND* phwnd); + IFACEMETHODIMP TranslateAccelerator(MSG* pmsg); + IFACEMETHODIMP SetRect(const RECT* prc); + IFACEMETHODIMP DoPreview(); + IFACEMETHODIMP Unload(); + + // IPreviewHandlerVisuals + IFACEMETHODIMP SetBackgroundColor(COLORREF color); + IFACEMETHODIMP SetFont(const LOGFONTW* plf); + IFACEMETHODIMP SetTextColor(COLORREF color); + + // IOleWindow + IFACEMETHODIMP GetWindow(HWND* phwnd); + IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IObjectWithSite + IFACEMETHODIMP SetSite(IUnknown* punkSite); + IFACEMETHODIMP GetSite(REFIID riid, void** ppv); + + SvgPreviewHandler(); +protected: + ~SvgPreviewHandler(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + std::wstring m_filePath; + + // Parent window that hosts the previewer window. + // Note: do NOT DestroyWindow this. + HWND m_hwndParent; + // Bounding rect of the parent window. + RECT m_rcParent; + + // Site pointer from host, used to get IPreviewHandlerFrame. + IUnknown* m_punkSite; + + HANDLE m_process; + HANDLE m_resizeEvent; +}; \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.rc b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj new file mode 100644 index 0000000000..0bf8d3967f --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj @@ -0,0 +1,119 @@ + + + + + 16.0 + Win32Proj + {143f13e3-d2e3-4d83-b035-356612d99956} + SvgPreviewHandlerCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + Create + + + + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj.filters b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj.filters new file mode 100644 index 0000000000..e3ccce0bed --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/dllmain.cpp b/src/modules/previewpane/SvgPreviewHandlerCpp/dllmain.cpp new file mode 100644 index 0000000000..28871d2fe8 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {FCDD4EED-41AA-492F-8A84-31A1546226E0} +static const GUID CLSID_SvgPreviewHandler = { 0xfcdd4eed, 0x41aa, 0x492f, { 0x8a, 0x84, 0x31, 0xa1, 0x54, 0x62, 0x26, 0xe0 } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_SvgPreviewHandler, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/packages.config b/src/modules/previewpane/SvgPreviewHandlerCpp/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/pch.cpp b/src/modules/previewpane/SvgPreviewHandlerCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/pch.h b/src/modules/previewpane/SvgPreviewHandlerCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/SvgPreviewHandlerCpp/resource.h b/src/modules/previewpane/SvgPreviewHandlerCpp/resource.h new file mode 100644 index 0000000000..76fe6194dd --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandlerCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Svg Preview Handler Module" +#define INTERNAL_NAME "PowerToys.SvgPreviewHandlerCpp" +#define ORIGINAL_FILENAME "PowerToys.SvgPreviewHandlerCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/SvgThumbnailProvider/Program.cs b/src/modules/previewpane/SvgThumbnailProvider/Program.cs new file mode 100644 index 0000000000..18df99e29f --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProvider/Program.cs @@ -0,0 +1,39 @@ +// 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. + +namespace Microsoft.PowerToys.ThumbnailHandler.Svg +{ + using System.Globalization; + + internal static class Program + { + private static SvgThumbnailProvider _thumbnailProvider; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ApplicationConfiguration.Initialize(); + if (args != null) + { + if (args.Length == 2) + { + string filePath = args[0]; + uint cx = Convert.ToUInt32(args[1], 10); + + _thumbnailProvider = new SvgThumbnailProvider(filePath); + Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); + filePath = filePath.Replace(".svg", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } + else + { + MessageBox.Show("Gcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture)); + } + } + } + } +} diff --git a/src/modules/previewpane/SvgThumbnailProvider/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/modules/previewpane/SvgThumbnailProvider/Properties/PublishProfiles/InstallationPublishProfile.pubxml index 74181a551d..d8a1a0ab2d 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/modules/previewpane/SvgThumbnailProvider/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview win10-$(Platform) - false + true False False false diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs index 64a13d93fd..f8b8964be7 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs @@ -1,17 +1,10 @@ // 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.Drawing.Drawing2D; using System.Globalization; -using System.IO; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Windows.Forms; -using Common.ComInterlop; using Common.Utilities; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; @@ -21,15 +14,26 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg /// /// SVG Thumbnail Provider. /// - [Guid("36B27788-A8BB-4698-A756-DF9F11F64F84")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class SvgThumbnailProvider : IInitializeWithStream, IThumbnailProvider, IDisposable + public class SvgThumbnailProvider : IDisposable { + public SvgThumbnailProvider(string filePath) + { + FilePath = filePath; + if (FilePath != null && File.Exists(FilePath)) + { + Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + } + } + + /// + /// Gets the file path to the file creating thumbnail for. + /// + public string FilePath { get; private set; } + /// /// Gets the stream object to access file. /// - public IStream Stream { get; private set; } + public Stream Stream { get; private set; } /// /// The maximum dimension (width or height) thumbnail we will generate. @@ -254,47 +258,38 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg return destImage; } - /// - public void Initialize(IStream pstream, uint grfMode) + /// + /// Generate thumbnail bitmap for provided Gcode file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + public Bitmap GetThumbnail(uint cx) { - // Ignore the grfMode always use read mode to access the file. - this.Stream = pstream; - } - - /// - public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha) - { - phbmp = IntPtr.Zero; - pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN; - if (cx == 0 || cx > MaxThumbnailSize) { - return; + return null; } if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredSvgThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { // GPO is disabling this utility. - return; + return null; } string svgData = null; - using (var stream = new ReadonlyStream(this.Stream as IStream)) + using (var reader = new StreamReader(this.Stream)) { - using (var reader = new StreamReader(stream)) + svgData = reader.ReadToEnd(); + try + { + // Fixes #17527 - Inkscape v1.1 swapped order of default and svg namespaces in svg file (default first, svg after). + // That resulted in parser being unable to parse it correctly and instead of svg, text was previewed. + // MS Edge and Firefox also couldn't preview svg files with mentioned order of namespaces definitions. + svgData = SvgPreviewHandlerHelper.SwapNamespaces(svgData); + svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData); + } + catch (Exception) { - svgData = reader.ReadToEnd(); - try - { - // Fixes #17527 - Inkscape v1.1 swapped order of default and svg namespaces in svg file (default first, svg after). - // That resulted in parser being unable to parse it correctly and instead of svg, text was previewed. - // MS Edge and Firefox also couldn't preview svg files with mentioned order of namespaces definitions. - svgData = SvgPreviewHandlerHelper.SwapNamespaces(svgData); - svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData); - } - catch (Exception) - { - } } } @@ -304,11 +299,12 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg { if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - phbmp = thumbnail.GetHbitmap(); - pdwAlpha = WTS_ALPHATYPE.WTSAT_RGB; + return (Bitmap)thumbnail.Clone(); } } } + + return null; } public void Dispose() diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj index ac78934152..d3f3a9cb98 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj @@ -1,5 +1,6 @@ + enable true {8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD} Microsoft.PowerToys.ThumbnailHandler.Svg @@ -7,7 +8,6 @@ PowerToys.SvgThumbnailProvider PowerToys SvgPreviewHandler net7.0-windows10.0.19041.0 - true true PowerToys SvgPreviewHandler ..\..\..\..\$(Platform)\$(Configuration)\modules\FileExplorerPreview\ @@ -16,6 +16,15 @@ false true win10-x64;win10-arm64 + true + + + + + win10-x64 + + + win10-arm64 @@ -29,6 +38,7 @@ $(NoWarn);1591 + WinExe diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.cpp new file mode 100644 index 0000000000..bf8dc9250d --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "ClassFactory.h" +#include "SvgThumbnailProvider.h" + +#include +#include + +extern long g_cDllRef; + +ClassFactory::ClassFactory() : + m_cRef(1) +{ + InterlockedIncrement(&g_cDllRef); +} + +ClassFactory::~ClassFactory() +{ + InterlockedDecrement(&g_cDllRef); +} + +// +// IUnknown +// + +IFACEMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) +{ + static const QITAB qit[] = { + QITABENT(ClassFactory, IClassFactory), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) ClassFactory::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +// +// IClassFactory +// + +IFACEMETHODIMP ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + HRESULT hr = CLASS_E_NOAGGREGATION; + + if (pUnkOuter == NULL) + { + hr = E_OUTOFMEMORY; + + SvgThumbnailProvider* pExt = new (std::nothrow) SvgThumbnailProvider(); + if (pExt) + { + hr = pExt->QueryInterface(riid, ppv); + pExt->Release(); + } + } + + return hr; +} + +IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_cDllRef); + } + else + { + InterlockedDecrement(&g_cDllRef); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.h b/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.h new file mode 100644 index 0000000000..b393c3916e --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/ClassFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ClassFactory : public IClassFactory +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IClassFactory + IFACEMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv); + IFACEMETHODIMP LockServer(BOOL fLock); + + ClassFactory(); + +protected: + ~ClassFactory(); + +private: + long m_cRef; +}; diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/GlobalExportFunctions.def b/src/modules/previewpane/SvgThumbnailProviderCpp/GlobalExportFunctions.def new file mode 100644 index 0000000000..76fc66cac3 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/GlobalExportFunctions.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp new file mode 100644 index 0000000000..0f42b32f3b --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp @@ -0,0 +1,181 @@ +#include "pch.h" +#include "SvgThumbnailProvider.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +extern HINSTANCE g_hInst; +extern long g_cDllRef; + +SvgThumbnailProvider::SvgThumbnailProvider() : + m_cRef(1), m_pStream(NULL), m_process(NULL) +{ + std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location()); + logFilePath.append(LogSettings::svgThumbLogPath); + Logger::init(LogSettings::svgThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + InterlockedIncrement(&g_cDllRef); +} + +SvgThumbnailProvider::~SvgThumbnailProvider() +{ + InterlockedDecrement(&g_cDllRef); +} + +#pragma region IUnknown + +IFACEMETHODIMP SvgThumbnailProvider::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { + QITABENT(SvgThumbnailProvider, IThumbnailProvider), + QITABENT(SvgThumbnailProvider, IInitializeWithStream), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +IFACEMETHODIMP_(ULONG) +SvgThumbnailProvider::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +IFACEMETHODIMP_(ULONG) +SvgThumbnailProvider::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (0 == cRef) + { + delete this; + } + return cRef; +} + +#pragma endregion + +#pragma region IInitializationWithStream + +IFACEMETHODIMP SvgThumbnailProvider::Initialize(IStream* pStream, DWORD grfMode) +{ + HRESULT hr = E_INVALIDARG; + if (pStream) + { + // Initialize can be called more than once, so release existing valid + // m_pStream. + if (m_pStream) + { + m_pStream->Release(); + m_pStream = NULL; + } + + m_pStream = pStream; + m_pStream->AddRef(); + hr = S_OK; + } + return hr; +} + +#pragma endregion + +#pragma region IThumbnailProvider + +IFACEMETHODIMP SvgThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha) +{ + // Read stream into the buffer + char buffer[4096]; + ULONG cbRead; + + Logger::trace(L"Begin"); + + GUID guid; + if (CoCreateGuid(&guid) == S_OK) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED(StringFromCLSID(guid, &guidString))) + { + Logger::info(L"Read stream and save to tmp file."); + + // {CLSID} -> CLSID + std::wstring guid = std::wstring(guidString.get()).substr(1, std::wstring(guidString.get()).size() - 2); + std::wstring filePath = PTSettingsHelper::get_local_low_folder_location() + L"\\SvgThumbnailPreview-Temp\\"; + if (!std::filesystem::exists(filePath)) + { + std::filesystem::create_directories(filePath); + } + + std::wstring fileName = filePath + guid + L".svg"; + + // Write data to tmp file + std::fstream file; + file.open(fileName, std::ios_base::out | std::ios_base::binary); + + if (!file.is_open()) + { + return 0; + } + + while (true) + { + auto result = m_pStream->Read(buffer, 4096, &cbRead); + + file.write(buffer, cbRead); + if (result == S_FALSE) + { + break; + } + } + file.close(); + + try + { + Logger::info(L"Start SvgThumbnailProvider.exe"); + + STARTUPINFO info = { sizeof(info) }; + std::wstring cmdLine{ L"\"" + fileName + L"\"" }; + cmdLine += L" "; + cmdLine += std::to_wstring(cx); + + std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.SvgThumbnailProvider.exe"; + + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = appPath.c_str(); + sei.lpParameters = cmdLine.c_str(); + sei.nShow = SW_SHOWDEFAULT; + ShellExecuteEx(&sei); + m_process = sei.hProcess; + WaitForSingleObject(m_process, INFINITE); + std::filesystem::remove(fileName); + + std::wstring fileNameBmp = filePath + guid + L".bmp"; + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + + std::filesystem::remove(fileNameBmp); + } + catch (std::exception& e) + { + std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) }; + Logger::error(L"Failed to start SvgThumbnailProvider.exe. Error: {}", errorMessage); + } + } + } + + return S_OK; +} + +#pragma endregion + +#pragma region Helper Functions + +#pragma endregion diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h new file mode 100644 index 0000000000..3c8a7f1b9c --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h @@ -0,0 +1,37 @@ +#pragma once + +#include "pch.h" + +#include +#include +#include + +class SvgThumbnailProvider : + public IInitializeWithStream, + public IThumbnailProvider +{ +public: + // IUnknown + IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv); + IFACEMETHODIMP_(ULONG) AddRef(); + IFACEMETHODIMP_(ULONG) Release(); + + // IInitializeWithStream + IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); + + // IPreviewHandler + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); + + SvgThumbnailProvider(); +protected: + ~SvgThumbnailProvider(); + +private: + // Reference count of component. + long m_cRef; + + // Provided during initialization. + IStream* m_pStream; + + HANDLE m_process; +}; \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.rc b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj new file mode 100644 index 0000000000..060e178785 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj @@ -0,0 +1,121 @@ + + + + + 16.0 + Win32Proj + {2bbc9e33-21ec-401c-84da-bb6590a9b2aa} + SvgThumbnailProviderCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\ + + + PowerToys.$(ProjectName) + + + + Level3 + true + _DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ../../.. + + + Windows + true + true + true + false + GlobalExportFunctions.def + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + + Create + + + + + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj.filters b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj.filters new file mode 100644 index 0000000000..67f21bfdc2 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/dllmain.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/dllmain.cpp new file mode 100644 index 0000000000..69748f2ce9 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/dllmain.cpp @@ -0,0 +1,73 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include "ClassFactory.h" + +HINSTANCE g_hInst = NULL; +long g_cDllRef = 0; + +// {10144713-1526-46C9-88DA-1FB52807A9FF} +static const GUID CLSID_SvgThumbnailProvider = { 0x10144713, 0x1526, 0x46c9, { 0x88, 0xda, 0x1f, 0xb5, 0x28, 0x7, 0xa9, 0xff } }; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + DisableThreadLibraryCalls(hModule); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +// +// FUNCTION: DllGetClassObject +// +// PURPOSE: Create the class factory and query to the specific interface. +// +// PARAMETERS: +// * rclsid - The CLSID that will associate the correct data and code. +// * riid - A reference to the identifier of the interface that the caller +// is to use to communicate with the class object. +// * ppv - The address of a pointer variable that receives the interface +// pointer requested in riid. Upon successful return, *ppv contains the +// requested interface pointer. If an error occurs, the interface pointer +// is NULL. +// +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) +{ + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if (IsEqualCLSID(CLSID_SvgThumbnailProvider, rclsid)) + { + hr = E_OUTOFMEMORY; + + ClassFactory* pClassFactory = new ClassFactory(); + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + } + + return hr; +} + +// +// FUNCTION: DllCanUnloadNow +// +// PURPOSE: Check if we can unload the component from the memory. +// +// NOTE: The component can be unloaded from the memory when its reference +// count is zero (i.e. nobody is still using the component). +// +STDAPI DllCanUnloadNow(void) +{ + return g_cDllRef > 0 ? S_FALSE : S_OK; +} diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/packages.config b/src/modules/previewpane/SvgThumbnailProviderCpp/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/pch.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/pch.h b/src/modules/previewpane/SvgThumbnailProviderCpp/pch.h new file mode 100644 index 0000000000..125ddcdf24 --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/pch.h @@ -0,0 +1,14 @@ +// 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 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#endif //PCH_H diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/resource.h b/src/modules/previewpane/SvgThumbnailProviderCpp/resource.h new file mode 100644 index 0000000000..9c56b472ed --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTopModuleInterface.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Svg Thumbnail Provider Module" +#define INTERNAL_NAME "PowerToys.SvgThumbnailProviderCpp" +#define ORIGINAL_FILENAME "PowerToys.SvgThumbnailProviderCpp.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/GcodeThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/GcodeThumbnailProviderTests.cs index 5bce85ff7d..38f0829e74 100644 --- a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/GcodeThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/GcodeThumbnailProviderTests.cs @@ -22,48 +22,39 @@ namespace GcodeThumbnailProviderUnitTests public void GetThumbnailValidStreamGcode() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.gcode"); + var filePath = "HelperFiles/sample.gcode"; - GcodeThumbnailProvider provider = new GcodeThumbnailProvider(); + GcodeThumbnailProvider provider = new GcodeThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(256); - provider.GetThumbnail(256, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap != IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_ARGB); + Assert.IsTrue(bitmap != null); } [TestMethod] public void GetThumbnailInValidSizeGcode() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.gcode"); + var filePath = "HelperFiles/sample.gcode"; - GcodeThumbnailProvider provider = new GcodeThumbnailProvider(); + GcodeThumbnailProvider provider = new GcodeThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(0); - provider.GetThumbnail(0, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); + Assert.IsTrue(bitmap == null); } [TestMethod] public void GetThumbnailToBigGcode() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.gcode"); + var filePath = "HelperFiles/sample.gcode"; - GcodeThumbnailProvider provider = new GcodeThumbnailProvider(); + GcodeThumbnailProvider provider = new GcodeThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(10001); - provider.GetThumbnail(10001, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); + Assert.IsTrue(bitmap == null); } [TestMethod] @@ -82,30 +73,5 @@ namespace GcodeThumbnailProviderUnitTests Bitmap thumbnail = GcodeThumbnailProvider.GetThumbnail(null, 256); Assert.IsTrue(thumbnail == null); } - - private static IStream GetMockStream(byte[] sourceArray) - { - var streamMock = new Mock(); - int bytesRead = 0; - - streamMock - .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((buffer, countToRead, bytesReadPtr) => - { - int actualCountToRead = Math.Min(sourceArray.Length - bytesRead, countToRead); - if (actualCountToRead > 0) - { - Array.Copy(sourceArray, bytesRead, buffer, 0, actualCountToRead); - Marshal.WriteInt32(bytesReadPtr, actualCountToRead); - bytesRead += actualCountToRead; - } - else - { - Marshal.WriteInt32(bytesReadPtr, 0); - } - }); - - return streamMock.Object; - } } } diff --git a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs index 969943c03d..a406c89ff5 100644 --- a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; @@ -21,73 +22,39 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailValidStreamPDF() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.pdf"); + var filePath = "HelperFiles/sample.pdf"; - PdfThumbnailProvider provider = new PdfThumbnailProvider(); + PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(256); - provider.GetThumbnail(256, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap != IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_RGB); + Assert.IsTrue(bitmap != null); } [TestMethod] public void GetThumbnailInValidSizePDF() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.pdf"); + var filePath = "HelperFiles/sample.pdf"; - PdfThumbnailProvider provider = new PdfThumbnailProvider(); + PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(0); - provider.GetThumbnail(0, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); + Assert.IsTrue(bitmap == null); } [TestMethod] public void GetThumbnailToBigPDF() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.pdf"); + var filePath = "HelperFiles/sample.pdf"; - PdfThumbnailProvider provider = new PdfThumbnailProvider(); + PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(10001); - provider.GetThumbnail(10001, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); - } - - private static IStream GetMockStream(byte[] sourceArray) - { - var streamMock = new Mock(); - int bytesRead = 0; - - streamMock - .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((buffer, countToRead, bytesReadPtr) => - { - int actualCountToRead = Math.Min(sourceArray.Length - bytesRead, countToRead); - if (actualCountToRead > 0) - { - Array.Copy(sourceArray, bytesRead, buffer, 0, actualCountToRead); - Marshal.WriteInt32(bytesReadPtr, actualCountToRead); - bytesRead += actualCountToRead; - } - else - { - Marshal.WriteInt32(bytesReadPtr, 0); - } - }); - - return streamMock.Object; + Assert.IsTrue(bitmap == null); } } } diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs deleted file mode 100644 index 12a854ee49..0000000000 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FileBasedPreviewHandlerTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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 PreviewHandlerCommonUnitTests -{ - [TestClass] - public class FileBasedPreviewHandlerTests - { - internal class TestFileBasedPreviewHandler : FileBasedPreviewHandler - { - public override void DoPreview() - { - throw new NotImplementedException(); - } - - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - return new Mock().Object; - } - } - - [DataTestMethod] - [DataRow(0U)] - [DataRow(1U)] - public void FileBasedPreviewHandlerShouldSetFilePathWhenInitializeCalled(uint grfMode) - { - // Arrange - var fileBasedPreviewHandler = new TestFileBasedPreviewHandler(); - var filePath = "C:\\valid-path"; - - // Act - fileBasedPreviewHandler.Initialize(filePath, grfMode); - - // Assert - Assert.AreEqual(filePath, fileBasedPreviewHandler.FilePath); - } - } -} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs index 0f645ec430..b1fd46d5bc 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs @@ -97,22 +97,6 @@ namespace PreviewHandlerCommonUnitTests } } - [TestMethod] - public void FormHandlerControlShouldUpdateBoundsWhenSetRectCalled() - { - // Arrange - using (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 FormHandlerControlShouldSetTextColorWhenSetTextColorCalled() { diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs deleted file mode 100644 index 63dc505c5a..0000000000 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs +++ /dev/null @@ -1,394 +0,0 @@ -// 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 Common; -using Common.ComInterlop; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; - -namespace PreviewHandlerCommonUnitTests -{ - [TestClass] - public class PreviewHandlerBaseTests - { - private static IPreviewHandlerControl previewHandlerControl; - - internal class TestPreviewHandler : PreviewHandlerBase - { - public TestPreviewHandler() - { - Initialize(); - } - - public override void DoPreview() - { - throw new NotImplementedException(); - } - - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - return GetMockPreviewHandlerControl(); - } - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetWindowWhenSetWindowCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - 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(), It.IsAny())) - .Callback((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(), It.IsAny()), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetrectWhenSetRectCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - var bounds = GetRectangle(2, 2, 4, 4); - - var actualBounds = Rectangle.Empty; - mockPreviewControl - .Setup(_ => _.SetRect(It.IsAny())) - .Callback((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()), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlUnloadWhenUnloadCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - - // Act - testPreviewHandler.Unload(); - - // Assert - mockPreviewControl.Verify(_ => _.Unload(), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetBackgroundColorWhenSetBackgroundColorCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - var color = default(COLORREF); - - // Act - testPreviewHandler.SetBackgroundColor(color); - - // Assert - mockPreviewControl.Verify(_ => _.SetBackgroundColor(It.Is(c => (c == color.Color))), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetTextColorWhenSetTextColorCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - var color = default(COLORREF); - - // Act - testPreviewHandler.SetTextColor(color); - - // Assert - mockPreviewControl.Verify(_ => _.SetTextColor(It.Is(c => (c == color.Color))), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetFontWhenSetFontCalled() - { - // Arrange - Font actualFont = null; - var mockPreviewControl = new Mock(); - mockPreviewControl - .Setup(x => x.SetFont(It.IsAny())) - .Callback((font) => - { - actualFont = font; - }); - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - var logFont = GetLogFont(); - - // Act - testPreviewHandler.SetFont(ref logFont); - - // Assert - mockPreviewControl.Verify(_ => _.SetFont(It.IsAny()), Times.Once); - Assert.AreEqual(Font.FromLogFont(logFont), actualFont); - } - - [TestMethod] - public void PreviewHandlerBaseShouldCallPreviewControlSetFocusWhenSetFocusCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - - // Act - testPreviewHandler.SetFocus(); - - // Assert - mockPreviewControl.Verify(_ => _.SetFocus(), Times.Once); - } - - [TestMethod] - public void PreviewHandlerBaseShouldSetHandleOnQueryFocusWhenPreviewControlsReturnValidHandle() - { - // Arrange - var hwnd = new IntPtr(5); - var mockPreviewControl = new Mock(); - 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 PreviewHandlerBaseShouldThrowOnQueryFocusWhenPreviewControlsReturnNotValidHandle() - { - // Arrange - var hwnd = IntPtr.Zero; - var mockPreviewControl = new Mock(); - 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 PreviewHandlerBaseShouldDirectKeyStrokesToIPreviewHandlerFrameIfIPreviewHandlerFrameIsSet() - { - // Arrange - var mockPreviewControl = new Mock(); - var mockPreviewHandlerFrame = new Mock(); - var msg = default(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(0U)] - [DataRow(1U)] - public void PreviewHandlerBaseShouldReturnIPreviewHandlerFrameResponseIfIPreviewHandlerFrameIsSet(uint resultCode) - { - // Arrange - var mockPreviewControl = new Mock(); - var mockPreviewHandlerFrame = new Mock(); - var msg = default(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 PreviewHandlerBaseShouldReturnUintFalseIfIPreviewHandlerFrameIsNotSet() - { - // Arrange - var mockPreviewControl = new Mock(); - var msg = default(MSG); - uint sFalse = 1; - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - - // Act - var result = testPreviewHandler.TranslateAccelerator(ref msg); - - // Assert - Assert.AreEqual(result, sFalse); - } - - [TestMethod] - public void PreviewHandlerBaseShouldReturnPreviewControlHandleIfGetWindowCalled() - { - // Arrange - var previewControlHandle = new IntPtr(5); - var mockPreviewControl = new Mock(); - mockPreviewControl.Setup(x => x.GetWindowHandle()) - .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 PreviewHandlerBaseShouldThrowNotImplementedExceptionIfContextSensitiveHelpCalled(bool fEnterMode) - { - // Arrange - var mockPreviewControl = new Mock(); - - 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 PreviewHandlerBaseShouldReturnSiteWhenGetSiteCalled() - { - // Arrange - var mockPreviewControl = new Mock(); - - previewHandlerControl = mockPreviewControl.Object; - var testPreviewHandler = new TestPreviewHandler(); - var site = new Mock().Object; - testPreviewHandler.SetSite(site); - var riid = Guid.Empty; - - // Act - testPreviewHandler.GetSite(ref riid, out object actualSite); - - // Assert - Assert.AreEqual(actualSite, site); - } - - private static LOGFONT GetLogFont() - { - return new LOGFONT - { - LfHeight = 12, - LfWidth = 0, - LfEscapement = 0, - LfWeight = 400, // FW_NORMAL - LfItalic = Convert.ToByte(false), - LfUnderline = Convert.ToByte(false), - LfStrikeOut = Convert.ToByte(0), - LfCharSet = Convert.ToByte(0), // ANSI_CHARSET - LfOutPrecision = Convert.ToByte(0), // OUT_DEFAULT_PRECIS - LfClipPrecision = Convert.ToByte(0), - LfQuality = Convert.ToByte(0), - LfPitchAndFamily = Convert.ToByte(0), - LfFaceName = "valid-font", - }; - } - - private static RECT GetRectangle(int left, int top, int right, int bottom) - { - return new RECT - { - Left = left, - Top = top, - Right = right, - Bottom = bottom, - }; - } - - private static IPreviewHandlerControl GetMockPreviewHandlerControl() - { - return previewHandlerControl; - } - } -} diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs deleted file mode 100644 index 583947be21..0000000000 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamBasedPreviewHandlerTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -// 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 PreviewHandlerCommonUnitTests -{ - [TestClass] - public class StreamBasedPreviewHandlerTests - { - internal class TestStreamBasedPreviewHandler : StreamBasedPreviewHandler - { - public override void DoPreview() - { - throw new NotImplementedException(); - } - - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - return new Mock().Object; - } - } - - [DataTestMethod] - [DataRow(0U)] - [DataRow(1U)] - public void StreamBasedPreviewHandlerShouldSetStreamWhenInitializeCalled(uint grfMode) - { - // Arrange - var streamBasedPreviewHandler = new TestStreamBasedPreviewHandler(); - var stream = new Mock().Object; - - // Act - streamBasedPreviewHandler.Initialize(stream, grfMode); - - // Assert - Assert.AreEqual(stream, streamBasedPreviewHandler.Stream); - } - } -} diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs index 1557943494..6ffb7e2ecd 100644 --- a/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs @@ -2,16 +2,11 @@ // 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.IO; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using Common.ComInterlop; using Microsoft.PowerToys.STATestExtension; using Microsoft.PowerToys.ThumbnailHandler.Stl; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; namespace StlThumbnailProviderUnitTests { @@ -22,48 +17,39 @@ namespace StlThumbnailProviderUnitTests public void GetThumbnailValidStreamStl() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.stl"); + var filePath = "HelperFiles/sample.stl"; - StlThumbnailProvider provider = new StlThumbnailProvider(); + StlThumbnailProvider provider = new StlThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(256); - provider.GetThumbnail(256, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap != IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_ARGB); + Assert.IsTrue(bitmap != null); } [TestMethod] public void GetThumbnailInValidSizeStl() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.stl"); + var filePath = "HelperFiles/sample.stl"; - StlThumbnailProvider provider = new StlThumbnailProvider(); + StlThumbnailProvider provider = new StlThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(0); - provider.GetThumbnail(0, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); + Assert.IsTrue(bitmap == null); } [TestMethod] public void GetThumbnailToBigStl() { // Act - var file = File.ReadAllBytes("HelperFiles/sample.stl"); + var filePath = "HelperFiles/sample.stl"; - StlThumbnailProvider provider = new StlThumbnailProvider(); + StlThumbnailProvider provider = new StlThumbnailProvider(filePath); - provider.Initialize(GetMockStream(file), 0); + Bitmap bitmap = provider.GetThumbnail(10001); - provider.GetThumbnail(10001, out IntPtr bitmap, out WTS_ALPHATYPE alphaType); - - Assert.IsTrue(bitmap == IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN); + Assert.IsTrue(bitmap == null); } [TestMethod] @@ -82,30 +68,5 @@ namespace StlThumbnailProviderUnitTests Bitmap thumbnail = StlThumbnailProvider.GetThumbnail(null, 256); Assert.IsTrue(thumbnail == null); } - - private static IStream GetMockStream(byte[] sourceArray) - { - var streamMock = new Mock(); - int bytesRead = 0; - - streamMock - .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((buffer, countToRead, bytesReadPtr) => - { - int actualCountToRead = Math.Min(sourceArray.Length - bytesRead, countToRead); - if (actualCountToRead > 0) - { - Array.Copy(sourceArray, bytesRead, buffer, 0, actualCountToRead); - Marshal.WriteInt32(bytesReadPtr, actualCountToRead); - bytesRead += actualCountToRead; - } - else - { - Marshal.WriteInt32(bytesReadPtr, 0); - } - }); - - return streamMock.Object; - } } } diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file1.svg b/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file1.svg new file mode 100644 index 0000000000..467b090663 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file1.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file2.svg b/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file2.svg new file mode 100644 index 0000000000..22b8ac803c --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/HelperFiles/file2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs index 77bd6a4b3f..d6a6f911ff 100644 --- a/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/SvgPreviewControlTests.cs @@ -32,7 +32,7 @@ namespace SvgPreviewHandlerUnitTests using (var svgPreviewControl = new SvgPreviewControl()) { // Act - svgPreviewControl.DoPreview(GetMockStream("")); + svgPreviewControl.DoPreview("HelperFiles/file1.svg"); int beforeTick = Environment.TickCount; @@ -55,7 +55,7 @@ namespace SvgPreviewHandlerUnitTests using (var svgPreviewControl = new SvgPreviewControl()) { // Act - svgPreviewControl.DoPreview(GetMockStream("")); + svgPreviewControl.DoPreview("HelperFiles/file1.svg"); int beforeTick = Environment.TickCount; @@ -147,13 +147,8 @@ namespace SvgPreviewHandlerUnitTests // Arrange using (var svgPreviewControl = new SvgPreviewControl()) { - var svgBuilder = new StringBuilder(); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine("\t"); - svgBuilder.AppendLine(""); - // Act - svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString())); + svgPreviewControl.DoPreview("HelperFiles/file2.svg"); int beforeTick = Environment.TickCount; @@ -176,14 +171,7 @@ namespace SvgPreviewHandlerUnitTests // Arrange using (var svgPreviewControl = new SvgPreviewControl()) { - var svgBuilder = new StringBuilder(); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine("\t"); - svgBuilder.AppendLine("\t"); - svgBuilder.AppendLine(""); - - // Act - svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString())); + svgPreviewControl.DoPreview("HelperFiles/file1.svg"); int beforeTick = Environment.TickCount; @@ -205,11 +193,7 @@ namespace SvgPreviewHandlerUnitTests // Arrange using (var svgPreviewControl = new SvgPreviewControl()) { - var svgBuilder = new StringBuilder(); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine("\t"); - svgBuilder.AppendLine(""); - svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString())); + svgPreviewControl.DoPreview("HelperFiles/file2.svg"); int beforeTick = Environment.TickCount; @@ -233,31 +217,5 @@ namespace SvgPreviewHandlerUnitTests Assert.AreEqual(textBox.Width, finalParentWidth); } } - - private static IStream GetMockStream(string streamData) - { - var mockStream = new Mock(); - var streamBytes = Encoding.UTF8.GetBytes(streamData); - - var streamMock = new Mock(); - var firstCall = true; - streamMock - .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((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; - } } } diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj index 0fc9606b7c..3266766693 100644 --- a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj @@ -19,6 +19,11 @@ + + + + + @@ -35,4 +40,12 @@ + + + Always + + + Always + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file1.svg b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file1.svg new file mode 100644 index 0000000000..467b090663 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file1.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file2.svg b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file2.svg new file mode 100644 index 0000000000..d09fae820b --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/file2.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs index eb7e7863b4..8af94dc6db 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs @@ -27,7 +27,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine("\t"); svgBuilder.AppendLine(""); - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsNotNull(thumbnail); @@ -45,7 +45,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine("\t"); svgBuilder.AppendLine(""); - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail != null); } @@ -56,7 +56,7 @@ namespace SvgThumbnailProviderUnitTests var svgBuilder = new StringBuilder(); svgBuilder.AppendLine("

foo

"); - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail == null); } @@ -64,7 +64,7 @@ namespace SvgThumbnailProviderUnitTests [TestMethod] public void CheckNoSvgEmptyStringShouldReturnNullBitmap() { - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(string.Empty, 256); Assert.IsTrue(thumbnail == null); } @@ -72,7 +72,7 @@ namespace SvgThumbnailProviderUnitTests [TestMethod] public void CheckNoSvgNullStringShouldReturnNullBitmap() { - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(null, 256); Assert.IsTrue(thumbnail == null); } @@ -81,7 +81,7 @@ namespace SvgThumbnailProviderUnitTests public void CheckZeroSizedThumbnailShouldReturnNullBitmap() { string content = ""; - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(content, 0); Assert.IsTrue(thumbnail == null); } @@ -103,7 +103,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine(""); svgBuilder.AppendLine(""); - SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(null); Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail != null); } @@ -111,77 +111,25 @@ namespace SvgThumbnailProviderUnitTests [TestMethod] public void GetThumbnailValidStreamSVG() { - var svgBuilder = new StringBuilder(); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); + var filePath = "HelperFiles/file1.svg"; - SvgThumbnailProvider provider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(filePath); - provider.Initialize(GetMockStream(svgBuilder.ToString()), 0); + Bitmap bitmap = svgThumbnailProvider.GetThumbnail(256); - IntPtr bitmap; - WTS_ALPHATYPE alphaType; - provider.GetThumbnail(256, out bitmap, out alphaType); - - Assert.IsTrue(bitmap != IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_RGB); + Assert.IsTrue(bitmap != null); } [TestMethod] public void GetThumbnailValidStreamHTML() { - var svgBuilder = new StringBuilder(); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); - svgBuilder.AppendLine(""); + var filePath = "HelperFiles/file2.svg"; - SvgThumbnailProvider provider = new SvgThumbnailProvider(); + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(filePath); - provider.Initialize(GetMockStream(svgBuilder.ToString()), 0); + Bitmap bitmap = svgThumbnailProvider.GetThumbnail(256); - IntPtr bitmap; - WTS_ALPHATYPE alphaType; - provider.GetThumbnail(256, out bitmap, out alphaType); - - Assert.IsTrue(bitmap != IntPtr.Zero); - Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_RGB); - } - - private static IStream GetMockStream(string streamData) - { - var mockStream = new Mock(); - var streamBytes = Encoding.UTF8.GetBytes(streamData); - - var streamMock = new Mock(); - var firstCall = true; - streamMock - .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((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; + Assert.IsTrue(bitmap != null); } } } diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj index e871f68b02..d0bd05898b 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj @@ -19,6 +19,11 @@ + + + + + @@ -35,4 +40,12 @@ + + + Always + + + Always + + \ No newline at end of file diff --git a/src/modules/previewpane/common/cominterop/NativeMethods.cs b/src/modules/previewpane/common/cominterop/NativeMethods.cs index 2a23adaed3..3d7bebaef2 100644 --- a/src/modules/previewpane/common/cominterop/NativeMethods.cs +++ b/src/modules/previewpane/common/cominterop/NativeMethods.cs @@ -33,5 +33,9 @@ namespace PreviewHandlerCommon.ComInterop [DllImport("user32.dll", SetLastError = true)] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetClientRect(IntPtr hWnd, ref Common.ComInterlop.RECT rect); } } diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs index 5587385f94..7549933382 100644 --- a/src/modules/previewpane/common/controls/FormHandlerControl.cs +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -5,6 +5,7 @@ using System; using System.Drawing; using System.Windows.Forms; +using Common.ComInterlop; using PreviewHandlerCommon.ComInterop; namespace Common @@ -49,75 +50,57 @@ namespace Common public void QueryFocus(out IntPtr result) { var getResult = IntPtr.Zero; - this.InvokeOnControlThread(() => - { - getResult = NativeMethods.GetFocus(); - }); + getResult = NativeMethods.GetFocus(); result = getResult; } /// public void SetBackgroundColor(Color argbColor) { - this.InvokeOnControlThread(() => - { - this.BackColor = argbColor; - }); + this.BackColor = argbColor; } /// public void SetFocus() { - this.InvokeOnControlThread(() => - { - this.Focus(); - }); + this.Focus(); } /// public void SetFont(Font font) { - this.InvokeOnControlThread(() => - { - this.Font = font; - }); + this.Font = font; } /// - public void SetRect(Rectangle windowBounds) + public void SetRect(Rectangle rect) { - this.UpdateWindowBounds(windowBounds); + this.UpdateWindowBounds(parentHwnd); } /// public void SetTextColor(Color color) { - this.InvokeOnControlThread(() => - { - this.ForeColor = color; - }); + this.ForeColor = color; } /// public void SetWindow(IntPtr hwnd, Rectangle rect) { this.parentHwnd = hwnd; - this.UpdateWindowBounds(rect); + this.UpdateWindowBounds(hwnd); } /// public virtual void Unload() { - this.InvokeOnControlThread(() => + this.Visible = false; + foreach (Control c in this.Controls) { - this.Visible = false; - foreach (Control c in this.Controls) - { - c.Dispose(); - } + c.Dispose(); + } - this.Controls.Clear(); - }); + this.Controls.Clear(); // Call garbage collection at the time of unloading of Preview. // Which is preventing prevhost.exe to exit at the time of closing File explorer. @@ -132,33 +115,27 @@ namespace Common this.Visible = true; } - /// - /// Executes the specified delegate on the thread that owns the control's underlying window handle. - /// - /// Delegate to run. - public void InvokeOnControlThread(MethodInvoker func) - { - this.Invoke(func); - } - /// /// Update the Form Control window with the passed rectangle. /// - /// An instance of rectangle. - private void UpdateWindowBounds(Rectangle windowBounds) + public void UpdateWindowBounds(IntPtr hwnd) { - this.InvokeOnControlThread(() => + // We must set the WS_CHILD style to change the form to a control within the Explorer preview pane + int windowStyle = NativeMethods.GetWindowLong(Handle, gwlStyle); + if ((windowStyle & wsChild) == 0) { - // We must set the WS_CHILD style to change the form to a control within the Explorer preview pane - int windowStyle = NativeMethods.GetWindowLong(Handle, gwlStyle); - if ((windowStyle & wsChild) == 0) - { - _ = NativeMethods.SetWindowLong(Handle, gwlStyle, windowStyle | wsChild); - } + _ = NativeMethods.SetWindowLong(Handle, gwlStyle, windowStyle | wsChild); + } - NativeMethods.SetParent(Handle, parentHwnd); - Bounds = windowBounds; - }); + NativeMethods.SetParent(Handle, hwnd); + + RECT s = default(RECT); + NativeMethods.GetClientRect(hwnd, ref s); + + if (Bounds.Right != s.Right || Bounds.Bottom != s.Bottom || Bounds.Left != s.Left || Bounds.Top != s.Top) + { + Bounds = s.ToRectangle(); + } } } } diff --git a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs index 80ce29f877..a5e074b8db 100644 --- a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs +++ b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs @@ -8,7 +8,7 @@ using System.Drawing; namespace Common { /// - /// Interface defining methods requirement by the control. + /// Interface defining preview handler control. /// public interface IPreviewHandlerControl { diff --git a/src/modules/previewpane/common/examplehandler/CustomControlTest.cs b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs index 3d1906eba1..d682d0ef99 100644 --- a/src/modules/previewpane/common/examplehandler/CustomControlTest.cs +++ b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs @@ -55,46 +55,43 @@ namespace Common /// Path to the file. public override void DoPreview(T dataSource) { - this.InvokeOnControlThread(() => + var filePath = dataSource as string; + + _browser = new WebView2(); + _browser.Dock = DockStyle.Fill; + _browser.Visible = true; + _browser.NavigationCompleted += (object sender, CoreWebView2NavigationCompletedEventArgs args) => { - var filePath = dataSource as string; + // Put here logic needed after WebView2 control is done navigating to url/page + }; - _browser = new WebView2(); - _browser.Dock = DockStyle.Fill; - _browser.Visible = true; - _browser.NavigationCompleted += (object sender, CoreWebView2NavigationCompletedEventArgs args) => + ConfiguredTaskAwaitable.ConfiguredTaskAwaiter + webView2EnvironmentAwaiter = CoreWebView2Environment + .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\CustomControlTest-Temp") + .ConfigureAwait(true).GetAwaiter(); + webView2EnvironmentAwaiter.OnCompleted(async () => + { + try { - // Put here logic needed after WebView2 control is done navigating to url/page - }; + _webView2Environment = webView2EnvironmentAwaiter.GetResult(); + await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); + await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});"); + _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); - ConfiguredTaskAwaitable.ConfiguredTaskAwaiter - webView2EnvironmentAwaiter = CoreWebView2Environment - .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\CustomControlTest-Temp") - .ConfigureAwait(true).GetAwaiter(); - webView2EnvironmentAwaiter.OnCompleted(async () => + // Navigate to page represented as a string + _browser.NavigateToString("Test"); + + // Or navigate to Uri + _browser.Source = new Uri(filePath); + } + catch (NullReferenceException) { - try - { - _webView2Environment = webView2EnvironmentAwaiter.GetResult(); - await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});"); - _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); - - // Navigate to page represented as a string - _browser.NavigateToString("Test"); - - // Or navigate to Uri - _browser.Source = new Uri(filePath); - } - catch (NullReferenceException) - { - } - }); - - this.Controls.Add(_browser); - base.DoPreview(dataSource); + } }); + + this.Controls.Add(_browser); + base.DoPreview(dataSource); } } } diff --git a/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs b/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs deleted file mode 100644 index 6c4b8114fc..0000000000 --- a/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs +++ /dev/null @@ -1,60 +0,0 @@ -// 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 -{ - /// - /// This is a example custom handler to show how to extend the library. - /// - [Guid("22a1a8e8-e929-4732-90ce-91eaff38b614")] - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public class TestCustomHandler : FileBasedPreviewHandler, IDisposable - { - private CustomControlTest _previewHandlerControl; - private bool disposedValue; - - /// - public override void DoPreview() - { - _previewHandlerControl.DoPreview(FilePath); - } - - /// - protected override IPreviewHandlerControl CreatePreviewHandlerControl() - { - _previewHandlerControl = new CustomControlTest(); - - return _previewHandlerControl; - } - - /// - /// Disposes objects - /// - /// Is Disposing - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - _previewHandlerControl.Dispose(); - } - - disposedValue = true; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs b/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs deleted file mode 100644 index 9824538c96..0000000000 --- a/src/modules/previewpane/common/handlers/FileBasedPreviewHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 -{ - /// - /// Extends the by implementing IInitializeWithFile. - /// - public abstract class FileBasedPreviewHandler : PreviewHandlerBase, IInitializeWithFile - { - /// - /// Gets the file path. - /// - public string FilePath { get; private set; } - - /// - public void Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, uint grfMode) - { - // Ignore the grfMode always use read mode to access the file. - this.FilePath = pszFilePath; - } - } -} diff --git a/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs b/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs deleted file mode 100644 index a9312dade7..0000000000 --- a/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs +++ /dev/null @@ -1,163 +0,0 @@ -// 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 Common.ComInterlop; - -namespace Common -{ - /// - /// Preview Handler base class implementing interfaces required by Preview Handler. - /// - public abstract class PreviewHandlerBase : IPreviewHandler, IOleWindow, IObjectWithSite, IPreviewHandlerVisuals - { - /// - /// An instance of Preview Control Used by the Handler. - /// - private IPreviewHandlerControl previewControl; - - /// - /// Hold reference for the window handle. - /// - private IntPtr parentHwnd; - - /// - /// Hold the bounds of the window. - /// - private Rectangle windowBounds; - - /// - /// Holds the site pointer. - /// - private object unkSite; - - /// - /// Holds reference for the IPreviewHandlerFrame. - /// - private IPreviewHandlerFrame frame; - - /// - /// Initializes a new instance of the class. - /// - public PreviewHandlerBase() - { - } - - /// - /// Initializes a new instance of the class. - /// - public void Initialize() - { - previewControl = CreatePreviewHandlerControl(); - } - - /// - public abstract void DoPreview(); - - /// - public void SetWindow(IntPtr hwnd, ref RECT rect) - { - this.parentHwnd = hwnd; - this.windowBounds = rect.ToRectangle(); - this.previewControl.SetWindow(hwnd, this.windowBounds); - } - - /// - public void SetRect(ref RECT rect) - { - this.windowBounds = rect.ToRectangle(); - this.previewControl.SetRect(this.windowBounds); - } - - /// - public void Unload() - { - this.previewControl.Unload(); - } - - /// - public void SetFocus() - { - this.previewControl.SetFocus(); - } - - /// - public void QueryFocus(out IntPtr phwnd) - { - this.previewControl.QueryFocus(out IntPtr result); - phwnd = result; - if (phwnd == IntPtr.Zero) - { - throw new Win32Exception(); - } - } - - /// - 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://learn.microsoft.com/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; - } - - /// - public void GetWindow(out IntPtr phwnd) - { - phwnd = this.previewControl.GetWindowHandle(); - } - - /// - public void ContextSensitiveHelp(bool fEnterMode) - { - // Should always return NotImplementedException. Source: https://learn.microsoft.com/windows/win32/shell/building-preview-handlers#iolewindowcontextsensitivehelp - throw new NotImplementedException(); - } - - /// - public void SetSite(object pUnkSite) - { - // Implementation logic details: https://learn.microsoft.com/windows/win32/shell/building-preview-handlers#iobjectwithsitesetsite - this.unkSite = pUnkSite; - this.frame = this.unkSite as IPreviewHandlerFrame; - } - - /// - public void GetSite(ref Guid riid, out object ppvSite) - { - ppvSite = this.unkSite; - } - - /// - public void SetBackgroundColor(COLORREF color) - { - this.previewControl.SetBackgroundColor(color.Color); - } - - /// - public void SetFont(ref LOGFONT plf) - { - this.previewControl.SetFont(Font.FromLogFont(plf)); - } - - /// - public void SetTextColor(COLORREF color) - { - this.previewControl.SetTextColor(color.Color); - } - - /// - /// Provide instance of the implementation of . Should be overridden by the implementation class with a control object to be used. - /// - /// Instance of the . - protected abstract IPreviewHandlerControl CreatePreviewHandlerControl(); - } -} diff --git a/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs b/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs deleted file mode 100644 index ebcf396cf4..0000000000 --- a/src/modules/previewpane/common/handlers/StreamBasedPreviewHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 -{ - /// - /// Extends the by implementing IInitializeWithStream. - /// - public abstract class StreamBasedPreviewHandler : PreviewHandlerBase, IInitializeWithStream - { - /// - /// Gets the stream object to access file. - /// - public IStream Stream { get; private set; } - - /// - public void Initialize(IStream pstream, uint grfMode) - { - // Ignore the grfMode always use read mode to access the file. - this.Stream = pstream; - } - } -} diff --git a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj index 8f89e8ec61..9b5bbdc3c8 100644 --- a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj +++ b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj @@ -9,7 +9,15 @@ PowerToys PowerToys Settings UI Library PowerToys.Settings.UI.Lib - false + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 76664c58f2..bb726333d4 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -16,6 +16,15 @@ true ..\..\..\$(Platform)\$(Configuration)\Settings + true + + + + + win10-x64 + + + win10-arm64 diff --git a/src/settings-ui/Settings.UI/Properties/PublishProfiles/InstallationPublishProfile.pubxml b/src/settings-ui/Settings.UI/Properties/PublishProfiles/InstallationPublishProfile.pubxml index ebff0236d3..ee0a3433a5 100644 --- a/src/settings-ui/Settings.UI/Properties/PublishProfiles/InstallationPublishProfile.pubxml +++ b/src/settings-ui/Settings.UI/Properties/PublishProfiles/InstallationPublishProfile.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. net7.0-windows10.0.19041.0 $(PowerToysRoot)\$(Platform)\$(Configuration)\Settings win10-$(Platform) - false + true False False false diff --git a/tools/BugReportTool/BugReportTool/ProcessesList.cpp b/tools/BugReportTool/BugReportTool/ProcessesList.cpp index ac0b3e8596..c6e0fce3d6 100644 --- a/tools/BugReportTool/BugReportTool/ProcessesList.cpp +++ b/tools/BugReportTool/BugReportTool/ProcessesList.cpp @@ -22,5 +22,14 @@ std::vector processes = L"PowerToys.Update.exe", L"PowerToys.ActionRunner.exe", L"PowerToys.AlwaysOnTop.exe", - L"PowerToys.Hosts.exe" + L"PowerToys.Hosts.exe", + L"PowerToys.GcodePreviewHandler.exe", + L"PowerToys.GcodeThumbnailProvider.exe", + L"PowerToys.MarkdownPreviewHandler.exe", + L"PowerToys.MonacoPreviewHandler.exe", + L"PowerToys.PdfPreviewHandler.exe", + L"PowerToys.PdfThumbnailProvider.exe", + L"PowerToys.StlThumbnailProvider.exe", + L"PowerToys.SvgPreviewHandler.exe", + L"PowerToys.SvgThumbnailProvider.exe" };