Adds BgcodeThumbnailProvider and BgcodePreviewHandler (#38667)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] **Closes:** #30352
- [X] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [X] **Tests:** Added/updated and all pass
- [X] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [X] **New binaries:** Added on the required places
- [X] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [X] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments


![image](https://github.com/user-attachments/assets/62c0cbbb-fbca-4bb3-82fe-696ba40da83d)


![image](https://github.com/user-attachments/assets/3f2f1346-91fb-4f49-85b9-8cd6e19e68e9)

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

- Close PowerToys if installed in your machine
- Full build the solution: PowerToys.sln
- Start PowerToys from PowerToys\x64\Debug\PowerToys.exe or
PowerToys\x64\Release\PowerToys.exe
- Toggle the "Binary G-code thumbnail previewer" setting to enable
- Open HelperFiles folder on the tests and check if the icon changes to
an image
- Check explorer preview to see if image is also shown there

---------

Co-authored-by: leileizhang <leilzh@microsoft.com>
This commit is contained in:
Pedro Lamas
2025-07-10 10:20:30 +01:00
committed by GitHub
parent 1feb7d5e5c
commit 071f5d7bcc
86 changed files with 3440 additions and 10 deletions

View File

@@ -29,11 +29,14 @@ RUS
AYUV AYUV
bak bak
Bcl Bcl
bgcode
Deflatealgorithm
exa exa
exabyte exabyte
Gbits Gbits
Gbps Gbps
gcode gcode
Heatshrink
Mbits Mbits
MBs MBs
mkv mkv

View File

@@ -18,6 +18,7 @@
/TestFiles/ /TestFiles/
[^/]\.cur$ [^/]\.cur$
[^/]\.gcode$ [^/]\.gcode$
[^/]\.bgcode$
[^/]\.rgs$ [^/]\.rgs$
\.a$ \.a$
\.ai$ \.ai$

View File

@@ -66,6 +66,12 @@
"PowerToys.GcodeThumbnailProvider.dll", "PowerToys.GcodeThumbnailProvider.dll",
"PowerToys.GcodeThumbnailProvider.exe", "PowerToys.GcodeThumbnailProvider.exe",
"PowerToys.GcodeThumbnailProviderCpp.dll", "PowerToys.GcodeThumbnailProviderCpp.dll",
"PowerToys.BgcodePreviewHandler.dll",
"PowerToys.BgcodePreviewHandler.exe",
"PowerToys.BgcodePreviewHandlerCpp.dll",
"PowerToys.BgcodeThumbnailProvider.dll",
"PowerToys.BgcodeThumbnailProvider.exe",
"PowerToys.BgcodeThumbnailProviderCpp.dll",
"PowerToys.ManagedTelemetry.dll", "PowerToys.ManagedTelemetry.dll",
"PowerToys.MarkdownPreviewHandler.dll", "PowerToys.MarkdownPreviewHandler.dll",
"PowerToys.MarkdownPreviewHandler.exe", "PowerToys.MarkdownPreviewHandler.exe",

View File

@@ -383,6 +383,18 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.GcodeFilePreviewError</td> <td>Microsoft.PowerToys.GcodeFilePreviewError</td>
<td>Triggered when there is an error previewing a G-code file.</td> <td>Triggered when there is an error previewing a G-code file.</td>
</tr> </tr>
<tr>
<td>Microsoft.PowerToys.BgcodeFileHandlerLoaded</td>
<td>Triggered when a Binary G-code file handler is loaded.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.BgcodeFilePreviewed</td>
<td>Occurs when a Binary G-code file is previewed in File Explorer.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.BgcodeFilePreviewError</td>
<td>Triggered when there is an error previewing a Binary G-code file.</td>
</tr>
<tr> <tr>
<td>Microsoft.PowerToys.MarkdownFileHandlerLoaded</td> <td>Microsoft.PowerToys.MarkdownFileHandlerLoaded</td>
<td>Occurs when a Markdown file handler is loaded.</td> <td>Occurs when a Markdown file handler is loaded.</td>

View File

@@ -714,6 +714,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "sr
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BgcodePreviewHandler", "src\modules\previewpane\BgcodePreviewHandler\BgcodePreviewHandler.csproj", "{9E0CBC06-F29A-4810-B93C-97D53863B95E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BgcodePreviewHandlerCpp", "src\modules\previewpane\BgcodePreviewHandlerCpp\BgcodePreviewHandlerCpp.vcxproj", "{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BgcodeThumbnailProviderCpp", "src\modules\previewpane\BgcodeThumbnailProviderCpp\BgcodeThumbnailProviderCpp.vcxproj", "{47B0678C-806B-4FE1-9F50-46BA88989532}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BgcodeThumbnailProvider", "src\modules\previewpane\BgcodeThumbnailProvider\BgcodeThumbnailProvider.csproj", "{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-BgcodePreviewHandler", "src\modules\previewpane\UnitTests-BgcodePreviewHandler\UnitTests-BgcodePreviewHandler.csproj", "{99CA1509-FB73-456E-AFAF-AB89C017BD72}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-BgcodeThumbnailProvider", "src\modules\previewpane\UnitTests-BgcodeThumbnailProvider\UnitTests-BgcodeThumbnailProvider.csproj", "{61CBF221-9452-4934-B685-146285E080D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseUtils.UITests", "src\modules\MouseUtils\MouseUtils.UITests\MouseUtils.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseUtils.UITests", "src\modules\MouseUtils\MouseUtils.UITests\MouseUtils.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesEditorUITest", "src\modules\Workspaces\WorkspacesEditorUITest\WorkspacesEditorUITest.csproj", "{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesEditorUITest", "src\modules\Workspaces\WorkspacesEditorUITest\WorkspacesEditorUITest.csproj", "{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}"
@@ -2626,6 +2638,54 @@ Global
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Debug|ARM64.Build.0 = Debug|ARM64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Debug|x64.ActiveCfg = Debug|x64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Debug|x64.Build.0 = Debug|x64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Release|ARM64.ActiveCfg = Release|ARM64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Release|ARM64.Build.0 = Release|ARM64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Release|x64.ActiveCfg = Release|x64
{9E0CBC06-F29A-4810-B93C-97D53863B95E}.Release|x64.Build.0 = Release|x64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Debug|ARM64.Build.0 = Debug|ARM64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Debug|x64.ActiveCfg = Debug|x64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Debug|x64.Build.0 = Debug|x64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Release|ARM64.ActiveCfg = Release|ARM64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Release|ARM64.Build.0 = Release|ARM64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Release|x64.ActiveCfg = Release|x64
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}.Release|x64.Build.0 = Release|x64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Debug|ARM64.ActiveCfg = Debug|ARM64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Debug|ARM64.Build.0 = Debug|ARM64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Debug|x64.ActiveCfg = Debug|x64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Debug|x64.Build.0 = Debug|x64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Release|ARM64.ActiveCfg = Release|ARM64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Release|ARM64.Build.0 = Release|ARM64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Release|x64.ActiveCfg = Release|x64
{47B0678C-806B-4FE1-9F50-46BA88989532}.Release|x64.Build.0 = Release|x64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Debug|ARM64.Build.0 = Debug|ARM64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Debug|x64.ActiveCfg = Debug|x64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Debug|x64.Build.0 = Debug|x64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Release|ARM64.ActiveCfg = Release|ARM64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Release|ARM64.Build.0 = Release|ARM64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Release|x64.ActiveCfg = Release|x64
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}.Release|x64.Build.0 = Release|x64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Debug|ARM64.ActiveCfg = Debug|ARM64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Debug|ARM64.Build.0 = Debug|ARM64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Debug|x64.ActiveCfg = Debug|x64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Debug|x64.Build.0 = Debug|x64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Release|ARM64.ActiveCfg = Release|ARM64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Release|ARM64.Build.0 = Release|ARM64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Release|x64.ActiveCfg = Release|x64
{99CA1509-FB73-456E-AFAF-AB89C017BD72}.Release|x64.Build.0 = Release|x64
{61CBF221-9452-4934-B685-146285E080D7}.Debug|ARM64.ActiveCfg = Debug|ARM64
{61CBF221-9452-4934-B685-146285E080D7}.Debug|ARM64.Build.0 = Debug|ARM64
{61CBF221-9452-4934-B685-146285E080D7}.Debug|x64.ActiveCfg = Debug|x64
{61CBF221-9452-4934-B685-146285E080D7}.Debug|x64.Build.0 = Debug|x64
{61CBF221-9452-4934-B685-146285E080D7}.Release|ARM64.ActiveCfg = Release|ARM64
{61CBF221-9452-4934-B685-146285E080D7}.Release|ARM64.Build.0 = Release|ARM64
{61CBF221-9452-4934-B685-146285E080D7}.Release|x64.ActiveCfg = Release|x64
{61CBF221-9452-4934-B685-146285E080D7}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64 {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64 {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
@@ -2940,6 +3000,12 @@ Global
{0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79} {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
{9E0CBC06-F29A-4810-B93C-97D53863B95E} = {2F305555-C296-497E-AC20-5FA1B237996A}
{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C} = {2F305555-C296-497E-AC20-5FA1B237996A}
{47B0678C-806B-4FE1-9F50-46BA88989532} = {2F305555-C296-497E-AC20-5FA1B237996A}
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA} = {2F305555-C296-497E-AC20-5FA1B237996A}
{99CA1509-FB73-456E-AFAF-AB89C017BD72} = {2F305555-C296-497E-AC20-5FA1B237996A}
{61CBF221-9452-4934-B685-146285E080D7} = {2F305555-C296-497E-AC20-5FA1B237996A}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A} {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482} {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482}

View File

@@ -18,6 +18,7 @@ This script checks the preview handler registration for the following file types
* .svgz * .svgz
* .pdf * .pdf
* .gcode * .gcode
* .bgcode
* .stl * .stl
* .txt * .txt
* .ini * .ini

View File

@@ -233,6 +233,15 @@
</RegistryKey> </RegistryKey>
<File Id="GcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.GcodePreviewHandler.resources.dll" /> <File Id="GcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.GcodePreviewHandler.resources.dll" />
</Component> </Component>
<Component
Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component"
Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER"
Guid="$(var.CompGUIDPrefix)07">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.BgcodePreviewHandler.resources.dll" />
</Component>
<!-- PowerToys Run aka Launcher plugin resources --> <!-- PowerToys Run aka Launcher plugin resources -->
<Component <Component
Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component" Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component"

View File

@@ -1170,7 +1170,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
} }
processes.resize(bytes / sizeof(processes[0])); processes.resize(bytes / sizeof(processes[0]));
std::array<std::wstring_view, 39> processesToTerminate = { std::array<std::wstring_view, 41> processesToTerminate = {
L"PowerToys.PowerLauncher.exe", L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe", L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe", L"PowerToys.AdvancedPaste.exe",
@@ -1186,12 +1186,14 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.PowerRename.exe", L"PowerToys.PowerRename.exe",
L"PowerToys.ImageResizer.exe", L"PowerToys.ImageResizer.exe",
L"PowerToys.GcodeThumbnailProvider.exe", L"PowerToys.GcodeThumbnailProvider.exe",
L"PowerToys.BgcodeThumbnailProvider.exe",
L"PowerToys.PdfThumbnailProvider.exe", L"PowerToys.PdfThumbnailProvider.exe",
L"PowerToys.MonacoPreviewHandler.exe", L"PowerToys.MonacoPreviewHandler.exe",
L"PowerToys.MarkdownPreviewHandler.exe", L"PowerToys.MarkdownPreviewHandler.exe",
L"PowerToys.StlThumbnailProvider.exe", L"PowerToys.StlThumbnailProvider.exe",
L"PowerToys.SvgThumbnailProvider.exe", L"PowerToys.SvgThumbnailProvider.exe",
L"PowerToys.GcodePreviewHandler.exe", L"PowerToys.GcodePreviewHandler.exe",
L"PowerToys.BgcodePreviewHandler.exe",
L"PowerToys.QoiPreviewHandler.exe", L"PowerToys.QoiPreviewHandler.exe",
L"PowerToys.PdfPreviewHandler.exe", L"PowerToys.PdfPreviewHandler.exe",
L"PowerToys.QoiThumbnailProvider.exe", L"PowerToys.QoiThumbnailProvider.exe",

View File

@@ -0,0 +1,16 @@
// 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.FilePreviewCommon
{
public enum BgcodeBlockType
{
FileMetadataBlock = 0,
GCodeBlock = 1,
SlicerMetadataBlock = 2,
PrinterMetadataBlock = 3,
PrintMetadataBlock = 4,
ThumbnailBlock = 5,
}
}

View File

@@ -0,0 +1,12 @@
// 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.FilePreviewCommon
{
public enum BgcodeChecksumType
{
None = 0,
CRC32 = 1,
}
}

View File

@@ -0,0 +1,14 @@
// 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.FilePreviewCommon
{
public enum BgcodeCompressionType
{
NoCompression = 0,
DeflateAlgorithm = 1,
HeatshrinkAlgorithm11 = 2,
HeatshrinkAlgorithm12 = 3,
}
}

View File

@@ -0,0 +1,129 @@
// 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.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace Microsoft.PowerToys.FilePreviewCommon
{
/// <summary>
/// Bgcode file helper class.
/// </summary>
public static class BgcodeHelper
{
private const uint MagicNumber = 'G' | 'C' << 8 | 'D' << 16 | 'E' << 24;
/// <summary>
/// Gets any thumbnails found in a bgcode file.
/// </summary>
/// <param name="reader">The <see cref="BinaryReader"/> instance to the bgcode file.</param>
/// <returns>The thumbnails found in a bgcode file.</returns>
public static IEnumerable<BgcodeThumbnail> GetThumbnails(BinaryReader reader)
{
var magicNumber = reader.ReadUInt32();
if (magicNumber != MagicNumber)
{
throw new InvalidDataException("Invalid magic number.");
}
var version = reader.ReadUInt32();
if (version != 1)
{
// Version 1 is the only one that exists
throw new InvalidDataException("Unsupported version.");
}
var checksum = (BgcodeChecksumType)reader.ReadUInt16();
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
var blockType = (BgcodeBlockType)reader.ReadUInt16();
var compression = (BgcodeCompressionType)reader.ReadUInt16();
var uncompressedSize = reader.ReadUInt32();
var size = compression == BgcodeCompressionType.NoCompression ? uncompressedSize : reader.ReadUInt32();
switch (blockType)
{
case BgcodeBlockType.FileMetadataBlock:
case BgcodeBlockType.PrinterMetadataBlock:
case BgcodeBlockType.PrintMetadataBlock:
case BgcodeBlockType.SlicerMetadataBlock:
case BgcodeBlockType.GCodeBlock:
reader.BaseStream.Seek(2 + size, SeekOrigin.Current); // Skip
break;
case BgcodeBlockType.ThumbnailBlock:
var format = (BgcodeThumbnailFormat)reader.ReadUInt16();
reader.BaseStream.Seek(4, SeekOrigin.Current); // Skip width and height
var data = ReadAndDecompressData(reader, compression, (int)size);
if (data != null)
{
yield return new BgcodeThumbnail(format, data);
}
break;
}
if (checksum == BgcodeChecksumType.CRC32)
{
reader.BaseStream.Seek(4, SeekOrigin.Current); // Skip checksum
}
}
}
/// <summary>
/// Gets the best thumbnail available in a bgcode file.
/// </summary>
/// <param name="reader">The <see cref="BinaryReader"/> instance to the gcode file.</param>
/// <returns>The best thumbnail available in the gcode file.</returns>
public static BgcodeThumbnail? GetBestThumbnail(BinaryReader reader)
{
return GetThumbnails(reader)
.OrderByDescending(x => x.Format switch
{
BgcodeThumbnailFormat.PNG => 2,
BgcodeThumbnailFormat.QOI => 1,
BgcodeThumbnailFormat.JPG => 0,
_ => 0,
})
.ThenByDescending(x => x.Data.Length)
.FirstOrDefault();
}
private static byte[]? ReadAndDecompressData(BinaryReader reader, BgcodeCompressionType compression, int size)
{
// Though the spec doesn't actually mention it, the reference encoder code never applies compression to thumbnails data
// which makes complete sense as this data is PNG, JPEG or QOI encoded so already compressed as much as possible!
switch (compression)
{
case BgcodeCompressionType.NoCompression:
return reader.ReadBytes(size);
case BgcodeCompressionType.DeflateAlgorithm:
var buffer = new byte[size];
using (var deflateStream = new DeflateStream(reader.BaseStream, CompressionMode.Decompress, true))
{
deflateStream.ReadExactly(buffer, 0, size);
}
return buffer;
default:
reader.BaseStream.Seek(size, SeekOrigin.Current); // Skip unknown or unsupported compression types
return null;
}
}
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.IO;
namespace Microsoft.PowerToys.FilePreviewCommon
{
/// <summary>
/// Represents a bgcode thumbnail.
/// </summary>
public class BgcodeThumbnail
{
/// <summary>
/// Gets the bgcode thumbnail image format.
/// </summary>
public BgcodeThumbnailFormat Format { get; }
/// <summary>
/// Gets the bgcode thumbnail image data.
/// </summary>
public byte[] Data { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BgcodeThumbnail"/> class.
/// </summary>
/// <param name="format">The bgcode thumbnail image format.</param>
/// <param name="data">The bgcode thumbnail image data.</param>
public BgcodeThumbnail(BgcodeThumbnailFormat format, byte[] data)
{
Format = format;
Data = data;
}
/// <summary>
/// Gets a <see cref="Bitmap"/> representing this thumbnail.
/// </summary>
/// <returns>A <see cref="Bitmap"/> representing this thumbnail.</returns>
public Bitmap? GetBitmap()
{
switch (Format)
{
case BgcodeThumbnailFormat.JPG:
case BgcodeThumbnailFormat.PNG:
return BitmapFromByteArray();
case BgcodeThumbnailFormat.QOI:
return BitmapFromQoiByteArray();
default:
return null;
}
}
private Bitmap BitmapFromByteArray()
{
return new Bitmap(new MemoryStream(Data));
}
private Bitmap BitmapFromQoiByteArray()
{
return QoiImage.FromStream(new MemoryStream(Data));
}
}
}

View File

@@ -0,0 +1,24 @@
// 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.FilePreviewCommon
{
public enum BgcodeThumbnailFormat
{
/// <summary>
/// PNG image format.
/// </summary>
PNG = 0,
/// <summary>
/// JPG image format.
/// </summary>
JPG = 1,
/// <summary>
/// QOI image format.
/// </summary>
QOI = 2,
}
}

View File

@@ -56,6 +56,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{ {
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGcodePreviewEnabledValue()); return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGcodePreviewEnabledValue());
} }
GpoRuleConfigured GPOWrapper::GetConfiguredBgcodePreviewEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredBgcodePreviewEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredSvgThumbnailsEnabledValue() GpoRuleConfigured GPOWrapper::GetConfiguredSvgThumbnailsEnabledValue()
{ {
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue()); return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue());
@@ -68,6 +72,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{ {
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue()); return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue());
} }
GpoRuleConfigured GPOWrapper::GetConfiguredBgcodeThumbnailsEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredBgcodeThumbnailsEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredStlThumbnailsEnabledValue() GpoRuleConfigured GPOWrapper::GetConfiguredStlThumbnailsEnabledValue()
{ {
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue()); return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue());

View File

@@ -21,9 +21,11 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredMouseWithoutBordersEnabledValue(); static GpoRuleConfigured GetConfiguredMouseWithoutBordersEnabledValue();
static GpoRuleConfigured GetConfiguredPdfPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredPdfPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredGcodePreviewEnabledValue(); static GpoRuleConfigured GetConfiguredGcodePreviewEnabledValue();
static GpoRuleConfigured GetConfiguredBgcodePreviewEnabledValue();
static GpoRuleConfigured GetConfiguredSvgThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredSvgThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredPdfThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredPdfThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredGcodeThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredGcodeThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredBgcodeThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredStlThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredStlThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredHostsFileEditorEnabledValue(); static GpoRuleConfigured GetConfiguredHostsFileEditorEnabledValue();
static GpoRuleConfigured GetConfiguredImageResizerEnabledValue(); static GpoRuleConfigured GetConfiguredImageResizerEnabledValue();

View File

@@ -24,9 +24,11 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredMonacoPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredPdfPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredPdfPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredGcodePreviewEnabledValue(); static GpoRuleConfigured GetConfiguredGcodePreviewEnabledValue();
static GpoRuleConfigured GetConfiguredBgcodePreviewEnabledValue();
static GpoRuleConfigured GetConfiguredSvgThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredSvgThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredPdfThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredPdfThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredGcodeThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredGcodeThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredBgcodeThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredStlThumbnailsEnabledValue(); static GpoRuleConfigured GetConfiguredStlThumbnailsEnabledValue();
static GpoRuleConfigured GetConfiguredHostsFileEditorEnabledValue(); static GpoRuleConfigured GetConfiguredHostsFileEditorEnabledValue();
static GpoRuleConfigured GetConfiguredImageResizerEnabledValue(); static GpoRuleConfigured GetConfiguredImageResizerEnabledValue();

View File

@@ -127,6 +127,10 @@ namespace winrt::PowerToys::Interop::implementation
{ {
return CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT; return CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT;
} }
hstring Constants::BgcodePreviewResizeEvent()
{
return CommonSharedConstants::BGCODE_PREVIEW_RESIZE_EVENT;
}
hstring Constants::QoiPreviewResizeEvent() hstring Constants::QoiPreviewResizeEvent()
{ {
return CommonSharedConstants::QOI_PREVIEW_RESIZE_EVENT; return CommonSharedConstants::QOI_PREVIEW_RESIZE_EVENT;

View File

@@ -35,6 +35,7 @@ namespace winrt::PowerToys::Interop::implementation
static hstring RegistryPreviewTriggerEvent(); static hstring RegistryPreviewTriggerEvent();
static hstring MeasureToolTriggerEvent(); static hstring MeasureToolTriggerEvent();
static hstring GcodePreviewResizeEvent(); static hstring GcodePreviewResizeEvent();
static hstring BgcodePreviewResizeEvent();
static hstring QoiPreviewResizeEvent(); static hstring QoiPreviewResizeEvent();
static hstring DevFilesPreviewResizeEvent(); static hstring DevFilesPreviewResizeEvent();
static hstring MarkdownPreviewResizeEvent(); static hstring MarkdownPreviewResizeEvent();

View File

@@ -32,6 +32,7 @@ namespace PowerToys
static String RegistryPreviewTriggerEvent(); static String RegistryPreviewTriggerEvent();
static String MeasureToolTriggerEvent(); static String MeasureToolTriggerEvent();
static String GcodePreviewResizeEvent(); static String GcodePreviewResizeEvent();
static String BgcodePreviewResizeEvent();
static String QoiPreviewResizeEvent(); static String QoiPreviewResizeEvent();
static String DevFilesPreviewResizeEvent(); static String DevFilesPreviewResizeEvent();
static String MarkdownPreviewResizeEvent(); static String MarkdownPreviewResizeEvent();

View File

@@ -92,6 +92,9 @@ namespace CommonSharedConstants
// Path to the event used by GcodePreviewHandler // Path to the event used by GcodePreviewHandler
const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d"; const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d";
// Path to the event used by BgcodePreviewHandler
const wchar_t BGCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysBgcodePreviewResizeEvent-1a76a553-919a-49e0-8179-776582d8e476";
// Path to the event used by QoiPreviewHandler // Path to the event used by QoiPreviewHandler
const wchar_t QOI_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysQoiPreviewResizeEvent-579518d1-8c8b-494f-8143-04f43d761ead"; const wchar_t QOI_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysQoiPreviewResizeEvent-579518d1-8c8b-494f-8143-04f43d761ead";

View File

@@ -19,6 +19,10 @@ struct LogSettings
inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.log"; inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.log";
inline const static std::string gcodeThumbLoggerName = "GcodeThumbnailProvider"; inline const static std::string gcodeThumbLoggerName = "GcodeThumbnailProvider";
inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.log"; inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.log";
inline const static std::string bgcodePrevLoggerName = "bgcodePrevHandler";
inline const static std::wstring bgcodePrevLogPath = L"logs\\FileExplorer_localLow\\BgcodePreviewHandler\\bgcode-prev-handler-log.log";
inline const static std::string bgcodeThumbLoggerName = "BgcodeThumbnailProvider";
inline const static std::wstring bgcodeThumbLogPath = L"logs\\FileExplorer_localLow\\BgcodeThumbnailProvider\\bgcode-thumbnail-provider-log.log";
inline const static std::string mdPrevLoggerName = "MDPrevHandler"; inline const static std::string mdPrevLoggerName = "MDPrevHandler";
inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.log"; inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.log";
inline const static std::string monacoPrevLoggerName = "MonacoPrevHandler"; inline const static std::string monacoPrevLoggerName = "MonacoPrevHandler";

View File

@@ -37,9 +37,11 @@ namespace powertoys_gpo
const std::wstring POLICY_CONFIGURE_ENABLED_MONACO_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMonacoPreview"; const std::wstring POLICY_CONFIGURE_ENABLED_MONACO_PREVIEW = L"ConfigureEnabledUtilityFileExplorerMonacoPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_PDF_PREVIEW = L"ConfigureEnabledUtilityFileExplorerPDFPreview"; const std::wstring POLICY_CONFIGURE_ENABLED_PDF_PREVIEW = L"ConfigureEnabledUtilityFileExplorerPDFPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_GCODE_PREVIEW = L"ConfigureEnabledUtilityFileExplorerGcodePreview"; const std::wstring POLICY_CONFIGURE_ENABLED_GCODE_PREVIEW = L"ConfigureEnabledUtilityFileExplorerGcodePreview";
const std::wstring POLICY_CONFIGURE_ENABLED_BGCODE_PREVIEW = L"ConfigureEnabledUtilityFileExplorerBgcodePreview";
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerSVGThumbnails"; const std::wstring POLICY_CONFIGURE_ENABLED_SVG_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerSVGThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_PDF_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerPDFThumbnails"; const std::wstring POLICY_CONFIGURE_ENABLED_PDF_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerPDFThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_GCODE_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerGcodeThumbnails"; const std::wstring POLICY_CONFIGURE_ENABLED_GCODE_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerGcodeThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_BGCODE_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerBgcodeThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_STL_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerSTLThumbnails"; const std::wstring POLICY_CONFIGURE_ENABLED_STL_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerSTLThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_HOSTS_FILE_EDITOR = L"ConfigureEnabledUtilityHostsFileEditor"; const std::wstring POLICY_CONFIGURE_ENABLED_HOSTS_FILE_EDITOR = L"ConfigureEnabledUtilityHostsFileEditor";
const std::wstring POLICY_CONFIGURE_ENABLED_IMAGE_RESIZER = L"ConfigureEnabledUtilityImageResizer"; const std::wstring POLICY_CONFIGURE_ENABLED_IMAGE_RESIZER = L"ConfigureEnabledUtilityImageResizer";
@@ -328,6 +330,11 @@ namespace powertoys_gpo
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GCODE_PREVIEW); return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GCODE_PREVIEW);
} }
inline gpo_rule_configured_t getConfiguredBgcodePreviewEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_BGCODE_PREVIEW);
}
inline gpo_rule_configured_t getConfiguredSvgThumbnailsEnabledValue() inline gpo_rule_configured_t getConfiguredSvgThumbnailsEnabledValue()
{ {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_SVG_THUMBNAILS); return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_SVG_THUMBNAILS);
@@ -343,6 +350,11 @@ namespace powertoys_gpo
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GCODE_THUMBNAILS); return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GCODE_THUMBNAILS);
} }
inline gpo_rule_configured_t getConfiguredBgcodeThumbnailsEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_BGCODE_THUMBNAILS);
}
inline gpo_rule_configured_t getConfiguredStlThumbnailsEnabledValue() inline gpo_rule_configured_t getConfiguredStlThumbnailsEnabledValue()
{ {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_STL_THUMBNAILS); return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_STL_THUMBNAILS);

View File

@@ -17,6 +17,7 @@ namespace NonLocalizable
const static std::vector<std::wstring> ExtMarkdown = { L".md", L".markdown", L".mdown", L".mkdn", L".mkd", L".mdwn", L".mdtxt", L".mdtext" }; const static std::vector<std::wstring> ExtMarkdown = { L".md", L".markdown", L".mdown", L".mkdn", L".mkd", L".mdwn", L".mdtxt", L".mdtext" };
const static std::vector<std::wstring> ExtPDF = { L".pdf" }; const static std::vector<std::wstring> ExtPDF = { L".pdf" };
const static std::vector<std::wstring> ExtGCode = { L".gcode" }; const static std::vector<std::wstring> ExtGCode = { L".gcode" };
const static std::vector<std::wstring> ExtBGCode = { L".bgcode" };
const static std::vector<std::wstring> ExtSTL = { L".stl" }; const static std::vector<std::wstring> ExtSTL = { L".stl" };
const static std::vector<std::wstring> ExtQOI = { L".qoi" }; const static std::vector<std::wstring> ExtQOI = { L".qoi" };
const static std::vector<std::wstring> ExtNoNoNo = { const static std::vector<std::wstring> ExtNoNoNo = {
@@ -146,6 +147,19 @@ inline registry::ChangeSet getGcodePreviewHandlerChangeSet(const std::wstring in
NonLocalizable::ExtGCode); NonLocalizable::ExtGCode);
} }
inline registry::ChangeSet getBgcodePreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{
using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::preview,
perUser,
L"{0e6d5bdd-d5f8-4692-a089-8bb88cdd37f4}",
get_std_product_version(),
(fs::path{ installationDir } / LR"d(PowerToys.BgcodePreviewHandlerCpp.dll)d").wstring(),
L"BgcodePreviewHandler",
L"Binary G-code Preview Handler",
NonLocalizable::ExtBGCode);
}
inline registry::ChangeSet getQoiPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getQoiPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
@@ -200,6 +214,19 @@ inline registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring
NonLocalizable::ExtGCode); NonLocalizable::ExtGCode);
} }
inline registry::ChangeSet getBgcodeThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{
using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::thumbnail,
perUser,
L"{5c93a1e4-99d0-4fb3-991c-6c296a27be21}",
get_std_product_version(),
(fs::path{ installationDir } / LR"d(PowerToys.BgcodeThumbnailProviderCpp.dll)d").wstring(),
L"BgcodeThumbnailProvider",
L"Binary G-code Thumbnail Provider",
NonLocalizable::ExtBGCode);
}
inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
@@ -275,9 +302,11 @@ inline std::vector<registry::ChangeSet> getAllOnByDefaultModulesChangeSets(const
getMdPreviewHandlerChangeSet(installationDir, PER_USER), getMdPreviewHandlerChangeSet(installationDir, PER_USER),
getMonacoPreviewHandlerChangeSet(installationDir, PER_USER), getMonacoPreviewHandlerChangeSet(installationDir, PER_USER),
getGcodePreviewHandlerChangeSet(installationDir, PER_USER), getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
getBgcodePreviewHandlerChangeSet(installationDir, PER_USER),
getQoiPreviewHandlerChangeSet(installationDir, PER_USER), getQoiPreviewHandlerChangeSet(installationDir, PER_USER),
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
getBgcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
getStlThumbnailHandlerChangeSet(installationDir, PER_USER), getStlThumbnailHandlerChangeSet(installationDir, PER_USER),
getQoiThumbnailHandlerChangeSet(installationDir, PER_USER), getQoiThumbnailHandlerChangeSet(installationDir, PER_USER),
getRegistryPreviewChangeSet(installationDir, PER_USER) }; getRegistryPreviewChangeSet(installationDir, PER_USER) };
@@ -291,10 +320,12 @@ inline std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstri
getMonacoPreviewHandlerChangeSet(installationDir, PER_USER), getMonacoPreviewHandlerChangeSet(installationDir, PER_USER),
getPdfPreviewHandlerChangeSet(installationDir, PER_USER), getPdfPreviewHandlerChangeSet(installationDir, PER_USER),
getGcodePreviewHandlerChangeSet(installationDir, PER_USER), getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
getBgcodePreviewHandlerChangeSet(installationDir, PER_USER),
getQoiPreviewHandlerChangeSet(installationDir, PER_USER), getQoiPreviewHandlerChangeSet(installationDir, PER_USER),
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
getPdfThumbnailHandlerChangeSet(installationDir, PER_USER), getPdfThumbnailHandlerChangeSet(installationDir, PER_USER),
getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
getBgcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
getStlThumbnailHandlerChangeSet(installationDir, PER_USER), getStlThumbnailHandlerChangeSet(installationDir, PER_USER),
getQoiThumbnailHandlerChangeSet(installationDir, PER_USER), getQoiThumbnailHandlerChangeSet(installationDir, PER_USER),
getRegistryPreviewChangeSet(installationDir, PER_USER), getRegistryPreviewChangeSet(installationDir, PER_USER),

View File

@@ -54,6 +54,8 @@ properties:
EnablePdfThumbnail: false EnablePdfThumbnail: false
EnableGcodePreview: false EnableGcodePreview: false
EnableGcodeThumbnail: false EnableGcodeThumbnail: false
EnableBgcodePreview: false
EnableBgcodeThumbnail: false
EnableStlThumbnail: false EnableStlThumbnail: false
EnableQoiPreview: false EnableQoiPreview: false
EnableQoiThumbnail: false EnableQoiThumbnail: false

View File

@@ -54,6 +54,8 @@ properties:
EnablePdfThumbnail: true EnablePdfThumbnail: true
EnableGcodePreview: true EnableGcodePreview: true
EnableGcodeThumbnail: true EnableGcodeThumbnail: true
EnableBgcodePreview: true
EnableBgcodeThumbnail: true
EnableStlThumbnail: true EnableStlThumbnail: true
EnableQoiPreview: true EnableQoiPreview: true
EnableQoiThumbnail: true EnableQoiThumbnail: true

View File

@@ -217,6 +217,16 @@
<decimal value="0" /> <decimal value="0" />
</disabledValue> </disabledValue>
</policy> </policy>
<policy name="ConfigureEnabledUtilityFileExplorerBgcodePreview" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerBgcodePreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerBgcodePreview">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_93_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityFileExplorerSVGThumbnails" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerSVGThumbnails)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerSVGThumbnails"> <policy name="ConfigureEnabledUtilityFileExplorerSVGThumbnails" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerSVGThumbnails)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerSVGThumbnails">
<parentCategory ref="PowerToys" /> <parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" /> <supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />
@@ -247,6 +257,16 @@
<decimal value="0" /> <decimal value="0" />
</disabledValue> </disabledValue>
</policy> </policy>
<policy name="ConfigureEnabledUtilityFileExplorerBgcodeThumbnails" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerBgcodeThumbnails)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerBgcodeThumbnails">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_93_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityFileExplorerQOIPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerQOIPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerQOIPreview"> <policy name="ConfigureEnabledUtilityFileExplorerQOIPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityFileExplorerQOIPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityFileExplorerQOIPreview">
<parentCategory ref="PowerToys" /> <parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_76_0" /> <supportedOn ref="SUPPORTED_POWERTOYS_0_76_0" />

View File

@@ -253,9 +253,11 @@ If you don't configure this policy, the user will be able to control the setting
<string id="ConfigureEnabledUtilityFileExplorerMonacoPreview">Source code file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerMonacoPreview">Source code file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerPDFPreview">PDF file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerPDFPreview">PDF file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerGcodePreview">Gcode file preview: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerGcodePreview">Gcode file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerBgcodePreview">BGcode file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerSVGThumbnails">SVG file thumbnail: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerSVGThumbnails">SVG file thumbnail: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerPDFThumbnails">PDF file thumbnail: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerPDFThumbnails">PDF file thumbnail: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerGcodeThumbnails">Gcode file thumbnail: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerGcodeThumbnails">Gcode file thumbnail: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerBgcodeThumbnails">BGcode file thumbnail: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerSTLThumbnails">STL file thumbnail: Configure enabled state</string> <string id="ConfigureEnabledUtilityFileExplorerSTLThumbnails">STL file thumbnail: Configure enabled state</string>
<string id="ConfigureEnabledUtilityHostsFileEditor">Hosts file editor: Configure enabled state</string> <string id="ConfigureEnabledUtilityHostsFileEditor">Hosts file editor: Configure enabled state</string>
<string id="ConfigureEnabledUtilityImageResizer">Image Resizer: Configure enabled state</string> <string id="ConfigureEnabledUtilityImageResizer">Image Resizer: Configure enabled state</string>

View File

@@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Common.SelfContained.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyTitle>PowerToys.BgcodePreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys BgcodePreviewHandler</AssemblyDescription>
<Description>PowerToys BgcodePreviewHandler</Description>
<DocumentationFile>..\..\..\..\$(Platform)\$(Configuration)\BgcodePreviewPaneDocumentation.xml</DocumentationFile>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AssemblyName>PowerToys.BgcodePreviewHandler</AssemblyName>
<ProjectGuid>{9E0CBC06-F29A-4810-B93C-97D53863B95E}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Bgcode</RootNamespace>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
</PropertyGroup>
<ItemGroup>
<Compile Update="BgcodePreviewHandlerControl.cs" />
<Compile Update="Properties\Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<PropertyGroup>
<!-- Disable missing comment warning. WinRT/C++ libraries added won't have comments on their reflections. -->
<NoWarn>$(NoWarn);1591</NoWarn>
<OutputType>WinExe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\FilePreviewCommon\FilePreviewCommon.csproj" />
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,179 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Common;
using Microsoft.PowerToys.FilePreviewCommon;
using Microsoft.PowerToys.PreviewHandler.Bgcode.Telemetry.Events;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.PreviewHandler.Bgcode
{
/// <summary>
/// Implementation of Control for Bgcode Preview Handler.
/// </summary>
public class BgcodePreviewHandlerControl : FormHandlerControl
{
/// <summary>
/// Picture box control to display the Binary G-code thumbnail.
/// </summary>
private PictureBox _pictureBox;
/// <summary>
/// Text box to display the information about blocked elements from Svg.
/// </summary>
private RichTextBox _textBox;
/// <summary>
/// Represent if a text box info bar is added for showing message.
/// </summary>
private bool _infoBarAdded;
/// <summary>
/// Initializes a new instance of the <see cref="BgcodePreviewHandlerControl"/> class.
/// </summary>
public BgcodePreviewHandlerControl()
{
SetBackgroundColor(Settings.BackgroundColor);
}
/// <summary>
/// Start the preview on the Control.
/// </summary>
/// <param name="dataSource">Stream reference to access source file.</param>
public override void DoPreview<T>(T dataSource)
{
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredBgcodePreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility. Show an error message instead.
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.GpoDisabledErrorText);
Resize += FormResized;
base.DoPreview(dataSource);
return;
}
try
{
Bitmap thumbnail = null;
if (!(dataSource is string filePath))
{
throw new ArgumentException($"{nameof(dataSource)} for {nameof(BgcodePreviewHandlerControl)} must be a string but was a '{typeof(T)}'");
}
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using (var reader = new BinaryReader(fs))
{
var bgcodeThumbnail = BgcodeHelper.GetBestThumbnail(reader);
thumbnail = bgcodeThumbnail?.GetBitmap();
}
_infoBarAdded = false;
if (thumbnail == null)
{
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.BgcodeWithoutEmbeddedThumbnails);
}
else
{
AddPictureBoxControl(thumbnail);
}
Resize += FormResized;
base.DoPreview(fs);
try
{
PowerToysTelemetry.Log.WriteEvent(new BgcodeFilePreviewed());
}
catch
{ // Should not crash if sending telemetry is failing. Ignore the exception.
}
}
catch (Exception ex)
{
PreviewError(ex, dataSource);
}
}
/// <summary>
/// Occurs when RichtextBox is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the ContentsResized event.</param>
private void RTBContentsResized(object sender, ContentsResizedEventArgs e)
{
var richTextBox = sender as RichTextBox;
richTextBox.Height = e.NewRectangle.Height + 5;
}
/// <summary>
/// Occurs when form is resized.
/// </summary>
/// <param name="sender">Reference to resized control.</param>
/// <param name="e">Provides data for the resize event.</param>
private void FormResized(object sender, EventArgs e)
{
if (_infoBarAdded)
{
_textBox.Width = Width;
}
}
/// <summary>
/// Adds a PictureBox Control to Control Collection.
/// </summary>
/// <param name="image">Image to display on PictureBox Control.</param>
private void AddPictureBoxControl(Image image)
{
_pictureBox = new PictureBox();
_pictureBox.BackgroundImage = image;
_pictureBox.BackgroundImageLayout = Width >= image.Width && Height >= image.Height ? ImageLayout.Center : ImageLayout.Zoom;
_pictureBox.Dock = DockStyle.Fill;
Controls.Add(_pictureBox);
}
/// <summary>
/// Adds a Text Box in Controls for showing information about blocked elements.
/// </summary>
/// <param name="message">Message to be displayed in textbox.</param>
private void AddTextBoxControl(string message)
{
_textBox = new RichTextBox();
_textBox.Text = message;
_textBox.BackColor = Color.LightYellow;
_textBox.Multiline = true;
_textBox.Dock = DockStyle.Top;
_textBox.ReadOnly = true;
_textBox.ContentsResized += RTBContentsResized;
_textBox.ScrollBars = RichTextBoxScrollBars.None;
_textBox.BorderStyle = BorderStyle.None;
Controls.Add(_textBox);
}
/// <summary>
/// Called when an error occurs during preview.
/// </summary>
/// <param name="exception">The exception which occurred.</param>
/// <param name="dataSource">Stream reference to access source file.</param>
private void PreviewError<T>(Exception exception, T dataSource)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new BgcodeFilePreviewError { Message = exception.Message });
}
catch
{ // Should not crash if sending telemetry is failing. Ignore the exception.
}
Controls.Clear();
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.BgcodeNotPreviewedError);
base.DoPreview(dataSource);
}
}
}

View File

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

View File

@@ -0,0 +1,80 @@
// 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.Globalization;
using System.Windows.Threading;
using Common.UI;
using Microsoft.PowerToys.Telemetry;
using PowerToys.Interop;
namespace Microsoft.PowerToys.PreviewHandler.Bgcode
{
internal static class Program
{
private static CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static BgcodePreviewHandlerControl _previewHandlerControl;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main(string[] args)
{
ApplicationConfiguration.Initialize();
if (args != null)
{
if (args.Length == 6)
{
ETWTrace etwTrace = new ETWTrace(Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "LocalLow", "Microsoft", "PowerToys", "etw"));
string filePath = args[0];
IntPtr hwnd = IntPtr.Parse(args[1], NumberStyles.HexNumber, CultureInfo.InvariantCulture);
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);
Rectangle s = new Rectangle(left, top, right - left, bottom - top);
_previewHandlerControl = new BgcodePreviewHandlerControl();
if (!_previewHandlerControl.SetWindow(hwnd, s))
{
return;
}
_previewHandlerControl.DoPreview(filePath);
NativeEventWaiter.WaitForEventLoop(
Constants.GcodePreviewResizeEvent(),
() =>
{
Rectangle s = default;
if (!_previewHandlerControl.SetRect(s))
{
etwTrace?.Dispose();
// When the parent HWND became invalid, the application won't respond to Application.Exit().
Environment.Exit(0);
}
},
Dispatcher.CurrentDispatcher,
_tokenSource.Token);
etwTrace?.Dispose();
}
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();
}
}
}

View File

@@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.PowerToys.PreviewHandler.Bgcode.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerToys.PreviewHandler.Bgcode.Properties.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to This Binary G-code could not be previewed due to an internal error..
/// </summary>
internal static string BgcodeNotPreviewedError {
get {
return ResourceManager.GetString("BgcodeNotPreviewedError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This Binary G-code does not contain any embedded thumbnails..
/// </summary>
internal static string BgcodeWithoutEmbeddedThumbnails {
get {
return ResourceManager.GetString("BgcodeWithoutEmbeddedThumbnails", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator..
/// </summary>
internal static string GpoDisabledErrorText {
get {
return ResourceManager.GetString("GpoDisabledErrorText", resourceCulture);
}
}
}
}

View File

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

View File

@@ -0,0 +1,38 @@
// 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.Bgcode
{
internal sealed class Settings
{
/// <summary>
/// Gets the color of the window background.
/// Even though this is not a setting yet, it's retrieved from a "Settings" class to be aligned with other preview handlers that contain this setting.
/// It's possible it can be converted into a setting in the future.
/// </summary>
public static Color BackgroundColor
{
get
{
if (GetTheme() == "dark")
{
return Color.FromArgb(30, 30, 30); // #1e1e1e
}
else
{
return Color.White;
}
}
}
/// <summary>
/// Returns the theme.
/// </summary>
/// <returns>Theme that should be used.</returns>
public static string GetTheme()
{
return Common.UI.ThemeManager.GetWindowsBaseColor().ToLowerInvariant();
}
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace Microsoft.PowerToys.PreviewHandler.Bgcode.Telemetry.Events
{
/// <summary>
/// A telemetry event to be raised when a bgcode file has been viewed in the preview pane.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class BgcodeFileHandlerLoaded : EventBase, IEvent
{
/// <inheritdoc/>
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace Microsoft.PowerToys.PreviewHandler.Bgcode.Telemetry.Events
{
/// <summary>
/// A telemetry event to be raised when an error has occurred in the preview pane.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class BgcodeFilePreviewError : EventBase, IEvent
{
/// <summary>
/// Gets or sets the error message to log as part of the telemetry event.
/// </summary>
public string Message { get; set; }
/// <inheritdoc/>
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance;
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace Microsoft.PowerToys.PreviewHandler.Bgcode.Telemetry.Events
{
/// <summary>
/// A telemetry event to be raised when a bgcode file has been viewed in the preview pane.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class BgcodeFilePreviewed : EventBase, IEvent
{
/// <inheritdoc/>
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -0,0 +1,284 @@
#include "pch.h"
#include "BgcodePreviewHandler.h"
#include "../powerpreview/powerpreviewConstants.h"
#include <shellapi.h>
#include <Shlwapi.h>
#include <string>
#include <common/interop/shared_constants.h>
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/process_path.h>
#include <common/Themes/windows_colors.h>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
BgcodePreviewHandler::BgcodePreviewHandler() :
m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL)
{
m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::BGCODE_PREVIEW_RESIZE_EVENT);
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::bgcodePrevLogPath);
Logger::init(LogSettings::bgcodePrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
BgcodePreviewHandler::~BgcodePreviewHandler()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP BgcodePreviewHandler::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(BgcodePreviewHandler, IPreviewHandler),
QITABENT(BgcodePreviewHandler, IInitializeWithFile),
QITABENT(BgcodePreviewHandler, IPreviewHandlerVisuals),
QITABENT(BgcodePreviewHandler, IOleWindow),
QITABENT(BgcodePreviewHandler, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
BgcodePreviewHandler::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
BgcodePreviewHandler::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithFile
IFACEMETHODIMP BgcodePreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode)
{
m_filePath = pszFilePath;
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandler
IFACEMETHODIMP BgcodePreviewHandler::SetWindow(HWND hwnd, const RECT* prc)
{
if (hwnd && prc)
{
m_hwndParent = hwnd;
m_rcParent = *prc;
}
return S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::SetFocus()
{
return S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::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 BgcodePreviewHandler::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 BgcodePreviewHandler::SetRect(const RECT* prc)
{
HRESULT hr = E_INVALIDARG;
if (prc != NULL)
{
if (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0 && (prc->left != 0 || prc->top != 0 || prc->right != 0 || prc->bottom != 0))
{
// BgcodePreviewHandler position first initialisation, do the first preview
m_rcParent = *prc;
DoPreview();
}
if (!m_resizeEvent)
{
Logger::error(L"Failed to create resize event for BgcodePreviewHandler");
}
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 BgcodePreviewHandler");
}
}
}
m_rcParent = *prc;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP BgcodePreviewHandler::DoPreview()
{
try
{
if (m_hwndParent == NULL || (m_rcParent.left == 0 && m_rcParent.top == 0 && m_rcParent.right == 0 && m_rcParent.bottom == 0))
{
// Postponing Start BgcodePreviewHandler.exe, parent and position not yet initialized. Preview will be done after initialisation.
return S_OK;
}
Logger::info(L"Starting BgcodePreviewHandler.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.BgcodePreviewHandler.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);
// Prevent to leak processes: preview is called multiple times when minimizing and restoring Explorer window
if (m_process)
{
TerminateProcess(m_process, 0);
}
m_process = sei.hProcess;
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start BgcodePreviewHandler.exe. Error: {}", errorMessage);
}
return S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::Unload()
{
Logger::info(L"Unload and terminate .exe");
m_hwndParent = NULL;
TerminateProcess(m_process, 0);
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandlerVisuals
IFACEMETHODIMP BgcodePreviewHandler::SetBackgroundColor(COLORREF color)
{
HBRUSH brush = CreateSolidBrush(WindowsColors::is_dark_mode() ? powerpreviewConstants::DARK_THEME_COLOR : powerpreviewConstants::LIGHT_THEME_COLOR);
SetClassLongPtr(m_hwndParent, GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(brush));
return S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::SetFont(const LOGFONTW* plf)
{
return S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::SetTextColor(COLORREF color)
{
return S_OK;
}
#pragma endregion
#pragma region IOleWindow
IFACEMETHODIMP BgcodePreviewHandler::GetWindow(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = m_hwndParent;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP BgcodePreviewHandler::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP BgcodePreviewHandler::SetSite(IUnknown* punkSite)
{
if (m_punkSite)
{
m_punkSite->Release();
m_punkSite = NULL;
}
return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK;
}
IFACEMETHODIMP BgcodePreviewHandler::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

View File

@@ -0,0 +1,69 @@
#pragma once
#include "pch.h"
#include <filesystem>
#include <ShlObj.h>
#include <string>
class BgcodePreviewHandler :
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);
BgcodePreviewHandler();
protected:
~BgcodePreviewHandler();
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;
};

View File

@@ -0,0 +1,40 @@
#include <windows.h>
#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

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{F6088A11-1C9E-4420-AA90-CF7E78DD7F1C}</ProjectGuid>
<RootNamespace>BgcodePreviewHandlerCpp</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup>
<TargetName>PowerToys.$(ProjectName)</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>GlobalExportFunctions.def</ModuleDefinitionFile>
<AdditionalDependencies>Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>GlobalExportFunctions.def</ModuleDefinitionFile>
<AdditionalDependencies>Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ClassFactory.h" />
<ClInclude Include="BgcodePreviewHandler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="BgcodePreviewHandler.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="BgcodePreviewHandlerCpp.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ClassFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BgcodePreviewHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Resource Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ClassFactory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BgcodePreviewHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def">
<Filter>Source Files</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="BgcodePreviewHandlerCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "BgcodePreviewHandler.h"
#include <new>
#include <Shlwapi.h>
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;
BgcodePreviewHandler* pExt = new (std::nothrow) BgcodePreviewHandler();
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;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <Unknwn.h>
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;
};

View File

@@ -0,0 +1,3 @@
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE

View File

@@ -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;
// {0e6d5bdd-d5f8-4692-a089-8bb88cdd37f4}
static const GUID CLSID_BgcodePreviewHandler = { 0x0e6d5bdd, 0xd5f8, 0x4692, { 0xa0, 0x89, 0x8b, 0xb8, 0x8c, 0xdd, 0x37, 0xf4 } };
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_BgcodePreviewHandler, 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;
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
</packages>

View File

@@ -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.

View File

@@ -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 <windows.h>
#endif //PCH_H

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by AlwaysOnTopModuleInterface.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys Bgcode Preview Handler Module"
#define INTERNAL_NAME "PowerToys.BgcodePreviewHandlerCpp"
#define ORIGINAL_FILENAME "PowerToys.BgcodePreviewHandlerCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,146 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using Microsoft.PowerToys.FilePreviewCommon;
namespace Microsoft.PowerToys.ThumbnailHandler.Bgcode
{
/// <summary>
/// Binary G-code Thumbnail Provider.
/// </summary>
public class BgcodeThumbnailProvider
{
public BgcodeThumbnailProvider(string filePath)
{
FilePath = filePath;
Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
}
/// <summary>
/// Gets the file path to the file creating thumbnail for.
/// </summary>
public string FilePath { get; private set; }
/// <summary>
/// Gets the stream object to access file.
/// </summary>
public Stream Stream { get; private set; }
/// <summary>
/// The maximum dimension (width or height) thumbnail we will generate.
/// </summary>
private const uint MaxThumbnailSize = 10000;
/// <summary>
/// Reads the Binary G-code content searching for thumbnails and returns the largest.
/// </summary>
/// <param name="reader">The BinaryReader instance for the Binary G-code content.</param>
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
/// <returns>A thumbnail extracted from the Binary G-code content.</returns>
public static Bitmap GetThumbnail(BinaryReader reader, uint cx)
{
if (cx > MaxThumbnailSize || reader == null || reader.BaseStream.Length == 0)
{
return null;
}
Bitmap thumbnail = null;
try
{
var bgcodeThumbnail = BgcodeHelper.GetBestThumbnail(reader);
thumbnail = bgcodeThumbnail?.GetBitmap();
}
catch (Exception)
{
// TODO: add logger
}
if (thumbnail != null && (
((thumbnail.Width != cx || thumbnail.Height > cx) && (thumbnail.Height != cx || thumbnail.Width > cx)) ||
thumbnail.PixelFormat != PixelFormat.Format32bppArgb))
{
// We are not the appropriate size for caller. Resize now while
// respecting the aspect ratio.
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
int scaleWidth = (int)(thumbnail.Width * scale);
int scaleHeight = (int)(thumbnail.Height * scale);
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
}
return thumbnail;
}
/// <summary>
/// Resize the image with high quality to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height)
{
if (width <= 0 ||
height <= 0 ||
width > MaxThumbnailSize ||
height > MaxThumbnailSize ||
image == null)
{
return null;
}
Bitmap destImage = new Bitmap(width, height, PixelFormat.Format32bppArgb);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, 0, 0, width, height);
}
image.Dispose();
return destImage;
}
/// <summary>
/// Generate thumbnail bitmap for provided Bgcode file/stream.
/// </summary>
/// <param name="cx">Maximum thumbnail size, in pixels.</param>
/// <returns>Generated bitmap</returns>
public Bitmap GetThumbnail(uint cx)
{
if (cx == 0 || cx > MaxThumbnailSize)
{
return null;
}
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredBgcodeThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility.
return null;
}
using (var reader = new BinaryReader(this.Stream))
{
Bitmap thumbnail = GetThumbnail(reader, cx);
if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0)
{
return thumbnail;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Common.SelfContained.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<ProjectGuid>{9BC1C986-1E97-4D07-A7B1-CE226C239EFA}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.ThumbnailHandler.Bgcode</RootNamespace>
<AssemblyName>PowerToys.BgcodeThumbnailProvider</AssemblyName>
<AssemblyTitle>PowerToys.BgcodeThumbnailProvider</AssemblyTitle>
<AssemblyDescription>PowerToys BgcodePreviewHandler</AssemblyDescription>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Description>PowerToys BgcodePreviewHandler</Description>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<PropertyGroup>
<!-- Disable missing comment warning. WinRT/C++ libraries added won't have comments on their reflections. -->
<NoWarn>$(NoWarn);1591</NoWarn>
<OutputType>WinExe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\FilePreviewCommon\FilePreviewCommon.csproj" />
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\Common\PreviewHandlerCommon.csproj" />
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,42 @@
// 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.Globalization;
namespace Microsoft.PowerToys.ThumbnailHandler.Bgcode
{
internal static class Program
{
private static BgcodeThumbnailProvider _thumbnailProvider;
/// <summary>
/// The main entry point for the application.
/// </summary>
[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 BgcodeThumbnailProvider(filePath);
Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx);
if (thumbnail != null)
{
filePath = filePath.Replace(".bgcode", ".bmp");
thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp);
}
}
else
{
MessageBox.Show("Bgcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
}
}
}

View File

@@ -0,0 +1,201 @@
#include "pch.h"
#include "BgcodeThumbnailProvider.h"
#include <filesystem>
#include <fstream>
#include <shellapi.h>
#include <Shlwapi.h>
#include <string>
#include <wil/com.h>
#include <common/utils/process_path.h>
#include <common/interop/shared_constants.h>
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/process_path.h>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
BgcodeThumbnailProvider::BgcodeThumbnailProvider() :
m_cRef(1), m_pStream(NULL), m_process(NULL)
{
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::bgcodeThumbLogPath);
Logger::init(LogSettings::bgcodeThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
BgcodeThumbnailProvider::~BgcodeThumbnailProvider()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP BgcodeThumbnailProvider::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(BgcodeThumbnailProvider, IThumbnailProvider),
QITABENT(BgcodeThumbnailProvider, IInitializeWithStream),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
BgcodeThumbnailProvider::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
BgcodeThumbnailProvider::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithStream
IFACEMETHODIMP BgcodeThumbnailProvider::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 BgcodeThumbnailProvider::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"\\BgcodeThumbnail-Temp\\";
if (!std::filesystem::exists(filePath))
{
std::filesystem::create_directories(filePath);
}
std::wstring fileName = filePath + guid + L".bgcode";
// 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();
m_pStream->Release();
m_pStream = NULL;
try
{
Logger::info(L"Start BgcodeThumbnailProvider.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.BgcodeThumbnailProvider.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";
if (std::filesystem::exists(fileNameBmp))
{
*phbmp = static_cast<HBITMAP>(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
*pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB;
std::filesystem::remove(fileNameBmp);
}
else
{
Logger::info(L"Bmp file not generated.");
return E_FAIL;
}
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start BgcodeThumbnailProvider.exe. Error: {}", errorMessage);
}
}
}
// ensure releasing the stream (not all if branches contain it)
if (m_pStream)
{
m_pStream->Release();
m_pStream = NULL;
}
return S_OK;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,37 @@
#pragma once
#include "pch.h"
#include <ShlObj.h>
#include <string>
#include <thumbcache.h>
class BgcodeThumbnailProvider :
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);
// IThumbnailProvider
IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha);
BgcodeThumbnailProvider();
protected:
~BgcodeThumbnailProvider();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
IStream* m_pStream;
HANDLE m_process;
};

View File

@@ -0,0 +1,40 @@
#include <windows.h>
#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

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{47B0678C-806B-4FE1-9F50-46BA88989532}</ProjectGuid>
<RootNamespace>BgcodeThumbnailProviderCpp</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup>
<TargetName>PowerToys.$(ProjectName)</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>GlobalExportFunctions.def</ModuleDefinitionFile>
<AdditionalDependencies>Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;MARKDOWNPREVIEWHANDLERCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>GlobalExportFunctions.def</ModuleDefinitionFile>
<AdditionalDependencies>Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ClassFactory.h" />
<ClInclude Include="BgcodeThumbnailProvider.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="BgcodeThumbnailProvider.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="BgcodeThumbnailProviderCpp.rc" />
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ClassFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BgcodeThumbnailProvider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Resource Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ClassFactory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BgcodeThumbnailProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def">
<Filter>Source Files</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="BgcodeThumbnailProviderCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "BgcodeThumbnailProvider.h"
#include <new>
#include <Shlwapi.h>
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;
BgcodeThumbnailProvider* pExt = new (std::nothrow) BgcodeThumbnailProvider();
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;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <Unknwn.h>
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;
};

View File

@@ -0,0 +1,3 @@
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE

View File

@@ -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;
// {5c93a1e4-99d0-4fb3-991c-6c296a27be21}
static const GUID CLSID_BgcodeThumbnailProvider = { 0x5c93a1e4, 0x99d0, 0x4fb3, { 0x99, 0x1c, 0x6c, 0x29, 0x6a, 0x27, 0xbe, 0x21 } };
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_BgcodeThumbnailProvider, 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;
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -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.

View File

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

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by AlwaysOnTopModuleInterface.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys Bgcode Thumbnail Provider Module"
#define INTERNAL_NAME "PowerToys.BgcodeThumbnailProviderCpp"
#define ORIGINAL_FILENAME "PowerToys.BgcodeThumbnailProviderCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,93 @@
// 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.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Microsoft.PowerToys.PreviewHandler.Bgcode;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace BgcodePreviewHandlerUnitTests
{
[STATestClass]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "new Exception() is fine in test projects.")]
public class BgcodePreviewHandlerTest
{
[DataTestMethod]
[DataRow("HelperFiles/sample.bgcode")]
public void BgcodePreviewHandlerControlAddsControlsToFormWhenDoPreviewIsCalled(string filePath)
{
// Arrange
using (var bgcodePreviewHandlerControl = new BgcodePreviewHandlerControl())
{
// Act
var file = File.ReadAllBytes(filePath);
bgcodePreviewHandlerControl.DoPreview<IStream>(GetMockStream(file));
var flowLayoutPanel = bgcodePreviewHandlerControl.Controls[0] as FlowLayoutPanel;
// Assert
Assert.AreEqual(1, bgcodePreviewHandlerControl.Controls.Count);
}
}
[TestMethod]
public void BgcodePreviewHandlerControlShouldAddValidInfoBarIfBgcodePreviewThrows()
{
// Arrange
using (var bgcodePreviewHandlerControl = new BgcodePreviewHandlerControl())
{
var mockStream = new Mock<IStream>();
mockStream
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Throws(new Exception());
// Act
bgcodePreviewHandlerControl.DoPreview(mockStream.Object);
var textBox = bgcodePreviewHandlerControl.Controls[0] as RichTextBox;
// Assert
Assert.IsFalse(string.IsNullOrWhiteSpace(textBox.Text));
Assert.AreEqual(1, bgcodePreviewHandlerControl.Controls.Count);
Assert.AreEqual(DockStyle.Top, textBox.Dock);
Assert.AreEqual(Color.LightYellow, textBox.BackColor);
Assert.IsTrue(textBox.Multiline);
Assert.IsTrue(textBox.ReadOnly);
Assert.AreEqual(RichTextBoxScrollBars.None, textBox.ScrollBars);
Assert.AreEqual(BorderStyle.None, textBox.BorderStyle);
}
}
private static IStream GetMockStream(byte[] sourceArray)
{
var streamMock = new Mock<IStream>();
int bytesRead = 0;
streamMock
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<byte[], int, IntPtr>((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;
}
}
}

View File

@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<AssemblyTitle>UnitTests-BgcodePreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys UnitTests-BgcodePreviewHandler</AssemblyDescription>
<Description>PowerToys UnitTests-BgcodePreviewHandler</Description>
<ProjectGuid>{99CA1509-FB73-456E-AFAF-AB89C017BD72}</ProjectGuid>
<RootNamespace>BgcodePreviewHandlerUnitTests</RootNamespace>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
</PropertyGroup>
<ItemGroup>
<None Remove="HelperFiles\sample.bgcode" />
<None Remove="HelperFiles\sample_JPG.bgcode" />
<None Remove="HelperFiles\sample_QOI.bgcode" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj" />
<ProjectReference Include="..\BgcodePreviewHandler\BgcodePreviewHandler.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="HelperFiles\sample.bgcode">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.IO;
using Microsoft.PowerToys.ThumbnailHandler.Bgcode;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BgcodeThumbnailProviderUnitTests
{
[STATestClass]
public class BgcodeThumbnailProviderTests
{
[DataTestMethod]
[DataRow("HelperFiles/sample.bgcode")]
public void GetThumbnailValidStreamBgcode(string filePath)
{
// Act
BgcodeThumbnailProvider provider = new BgcodeThumbnailProvider(filePath);
Bitmap bitmap = provider.GetThumbnail(256);
Assert.IsTrue(bitmap != null);
}
[TestMethod]
public void GetThumbnailInValidSizeBgcode()
{
// Act
var filePath = "HelperFiles/sample.bgcode";
BgcodeThumbnailProvider provider = new BgcodeThumbnailProvider(filePath);
Bitmap bitmap = provider.GetThumbnail(0);
Assert.IsTrue(bitmap == null);
}
[TestMethod]
public void GetThumbnailToBigBgcode()
{
// Act
var filePath = "HelperFiles/sample.bgcode";
BgcodeThumbnailProvider provider = new BgcodeThumbnailProvider(filePath);
Bitmap bitmap = provider.GetThumbnail(10001);
Assert.IsTrue(bitmap == null);
}
[TestMethod]
public void CheckNoBgcodeEmptyDataShouldReturnNullBitmap()
{
using (var reader = new BinaryReader(new MemoryStream()))
{
Bitmap thumbnail = BgcodeThumbnailProvider.GetThumbnail(reader, 256);
Assert.IsTrue(thumbnail == null);
}
}
[TestMethod]
public void CheckNoBgcodeNullStringShouldReturnNullBitmap()
{
Bitmap thumbnail = BgcodeThumbnailProvider.GetThumbnail(null, 256);
Assert.IsTrue(thumbnail == null);
}
}
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<AssemblyTitle>UnitTests-BgcodeThumbnailProvider</AssemblyTitle>
<AssemblyDescription>PowerToys UnitTests-BgcodeThumbnailProvider</AssemblyDescription>
<AssemblyTitle>UnitTests-BgcodeThumbnailProvider</AssemblyTitle>
<Description>PowerToys UnitTests-BgcodeThumbnailProvider</Description>
<ProjectGuid>{61CBF221-9452-4934-B685-146285E080D7}</ProjectGuid>
<RootNamespace>BgcodeThumbnailProviderUnitTests</RootNamespace>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
</PropertyGroup>
<ItemGroup>
<None Remove="HelperFiles\sample.bgcode" />
<None Remove="HelperFiles\sample_JPG.bgcode" />
<None Remove="HelperFiles\sample_QOI.bgcode" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\PreviewHandlerCommon.csproj" />
<ProjectReference Include="..\BgcodeThumbnailProvider\BgcodeThumbnailProvider.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="HelperFiles\sample.bgcode">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -37,6 +37,12 @@ const CLSID CLSID_SHIMActivateGcodePreviewHandler = { 0x516cb24f, 0x562f, 0x422f
// ec52dea8-7c9f-4130-a77b-1737d0418507 // ec52dea8-7c9f-4130-a77b-1737d0418507
const CLSID CLSID_GcodePreviewHandler = { 0xec52dea8, 0x7c9f, 0x4130, { 0xa7, 0x7b, 0x17, 0x37, 0xd0, 0x41, 0x85, 0x07 } }; const CLSID CLSID_GcodePreviewHandler = { 0xec52dea8, 0x7c9f, 0x4130, { 0xa7, 0x7b, 0x17, 0x37, 0xd0, 0x41, 0x85, 0x07 } };
// 8fae8d5d-6bd1-46b4-a74f-cbebba4c7b62
const CLSID CLSID_SHIMActivateBgcodePreviewHandler = { 0x8fae8d5d, 0x6bd1, 0x46b4, { 0xa7, 0x4f, 0xcb, 0xeb, 0xba, 0x4c, 0x7b, 0x62 } };
// dd8de316-7b01-48e7-ba21-e92c646704af
const GUID CLSID_BgcodePreviewHandler = { 0xdd8de316, 0x7b01, 0x48e7, { 0xba, 0x21, 0xe9, 0x2c, 0x64, 0x67, 0x04, 0xaf } };
// F498BE36-5C94-4EC9-A65A-AD1CF4C38271 // F498BE36-5C94-4EC9-A65A-AD1CF4C38271
const GUID CLSID_SHIMActivateQoiPreviewHandler = { 0xf498be36, 0x5c94, 0x4ec9, { 0xa6, 0x5a, 0xad, 0x1c, 0xf4, 0xc3, 0x82, 0x71 } }; const GUID CLSID_SHIMActivateQoiPreviewHandler = { 0xf498be36, 0x5c94, 0x4ec9, { 0xa6, 0x5a, 0xad, 0x1c, 0xf4, 0xc3, 0x82, 0x71 } };
@@ -46,6 +52,9 @@ const GUID CLSID_QoiPreviewHandler = { 0x8aa07897, 0xc30b, 0x4543, { 0x86, 0x5b,
// BFEE99B4-B74D-4348-BCA5-E757029647FF // BFEE99B4-B74D-4348-BCA5-E757029647FF
const GUID CLSID_GcodeThumbnailProvider = { 0xbfee99b4, 0xb74d, 0x4348, { 0xbc, 0xa5, 0xe7, 0x57, 0x02, 0x96, 0x47, 0xff } }; const GUID CLSID_GcodeThumbnailProvider = { 0xbfee99b4, 0xb74d, 0x4348, { 0xbc, 0xa5, 0xe7, 0x57, 0x02, 0x96, 0x47, 0xff } };
// c28761a0-8420-43ad-bff3-40400543e2d4
const GUID CLSID_BgcodeThumbnailProvider = {0xc28761a0, 0x8420, 0x43ad, {0xbf, 0xf3, 0x40, 0x40, 0x05, 0x43, 0xe2, 0xd4}};
// 8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF // 8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF
const GUID CLSID_StlThumbnailProvider = { 0x8bc8afc2, 0x4e7c, 0x4695, { 0x81, 0x8e, 0x8c, 0x1f, 0xfd, 0xce, 0xa2, 0xaf } }; const GUID CLSID_StlThumbnailProvider = { 0x8bc8afc2, 0x4e7c, 0x4695, { 0x81, 0x8e, 0x8c, 0x1f, 0xfd, 0xce, 0xa2, 0xaf } };

View File

@@ -143,7 +143,7 @@
</data> </data>
<data name="Prevpane_Monaco_Settings_Description" xml:space="preserve"> <data name="Prevpane_Monaco_Settings_Description" xml:space="preserve">
<value>Developer files Previewer</value> <value>Developer files Previewer</value>
</data> </data>
<data name="Prevpane_Md_Settings_Displayname" xml:space="preserve"> <data name="Prevpane_Md_Settings_Displayname" xml:space="preserve">
<value>Markdown Previewer</value> <value>Markdown Previewer</value>
</data> </data>
@@ -198,4 +198,13 @@
<data name="Qoi_Thumbnail_Provider_Settings_Description" xml:space="preserve"> <data name="Qoi_Thumbnail_Provider_Settings_Description" xml:space="preserve">
<value>Qoi Thumbnail Provider</value> <value>Qoi Thumbnail Provider</value>
</data> </data>
<data name="Prevpane_Bgcode_Settings_Displayname" xml:space="preserve">
<value>Binary G-code Previewer</value>
</data>
<data name="Prevpane_Bgcode_Settings_Description" xml:space="preserve">
<value>Binary G-code Previewer</value>
</data>
<data name="Bgcode_Thumbnail_Provider_Settings_Description" xml:space="preserve">
<value>Binary G-code Thumbnail Provider</value>
</data>
</root> </root>

View File

@@ -50,6 +50,11 @@ PowerPreviewModule::PowerPreviewModule() :
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredGcodePreviewEnabledValue, .checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredGcodePreviewEnabledValue,
.registryChanges = getGcodePreviewHandlerChangeSet(installationDir, installPerUser) }); .registryChanges = getGcodePreviewHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"bgcode-previewer-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_BGCODE_SETTINGS_DESCRIPTION),
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredBgcodePreviewEnabledValue,
.registryChanges = getBgcodePreviewHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"svg-thumbnail-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"svg-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue, .checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue,
@@ -65,6 +70,11 @@ PowerPreviewModule::PowerPreviewModule() :
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue, .checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue,
.registryChanges = getGcodeThumbnailHandlerChangeSet(installationDir, installPerUser) }); .registryChanges = getGcodeThumbnailHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"bgcode-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_BGCODE_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredBgcodeThumbnailsEnabledValue,
.registryChanges = getBgcodeThumbnailHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"stl-thumbnail-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"stl-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_STL_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_STL_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredStlThumbnailsEnabledValue, .checkModuleGPOEnabledRuleFunction = powertoys_gpo::getConfiguredStlThumbnailsEnabledValue,

View File

@@ -86,7 +86,9 @@
<None Include="powerpreview.def" /> <None Include="powerpreview.def" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources.resx" /> <None Include="Resources.resx">
<SubType>Designer</SubType>
</None>
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" /> <Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -223,6 +223,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
} }
} }
private bool enableBgcodePreview = true;
[JsonPropertyName("bgcode-previewer-toggle-setting")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableBgcodePreview
{
get => enableBgcodePreview;
set
{
if (value != enableBgcodePreview)
{
LogTelemetryEvent(value);
enableBgcodePreview = value;
}
}
}
private bool enableGcodeThumbnail = true; private bool enableGcodeThumbnail = true;
[JsonPropertyName("gcode-thumbnail-toggle-setting")] [JsonPropertyName("gcode-thumbnail-toggle-setting")]
@@ -240,6 +257,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
} }
} }
private bool enableBgcodeThumbnail = true;
[JsonPropertyName("bgcode-thumbnail-toggle-setting")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableBgcodeThumbnail
{
get => enableBgcodeThumbnail;
set
{
if (value != enableBgcodeThumbnail)
{
LogTelemetryEvent(value);
enableBgcodeThumbnail = value;
}
}
}
private bool enableStlThumbnail = true; private bool enableStlThumbnail = true;
[JsonPropertyName("stl-thumbnail-toggle-setting")] [JsonPropertyName("stl-thumbnail-toggle-setting")]

View File

@@ -61,11 +61,13 @@ namespace ViewModelTests
Assert.AreEqual(originalSettings.Properties.EnableMonacoPreview, viewModel.MonacoRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableMonacoPreview, viewModel.MonacoRenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnablePdfPreview, viewModel.PDFRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnablePdfPreview, viewModel.PDFRenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableGcodePreview, viewModel.GCODERenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableGcodePreview, viewModel.GCODERenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableBgcodePreview, viewModel.BGCODERenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableQoiPreview, viewModel.QOIRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableQoiPreview, viewModel.QOIRenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableSvgPreview, viewModel.SVGRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableSvgPreview, viewModel.SVGRenderIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableSvgThumbnail, viewModel.SVGThumbnailIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableSvgThumbnail, viewModel.SVGThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnablePdfThumbnail, viewModel.PDFThumbnailIsEnabled); Assert.AreEqual(originalSettings.Properties.EnablePdfThumbnail, viewModel.PDFThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableGcodeThumbnail, viewModel.GCODEThumbnailIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableGcodeThumbnail, viewModel.GCODEThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableBgcodeThumbnail, viewModel.BGCODEThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableStlThumbnail, viewModel.STLThumbnailIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableStlThumbnail, viewModel.STLThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableQoiThumbnail, viewModel.QOIThumbnailIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableQoiThumbnail, viewModel.QOIThumbnailIsEnabled);
@@ -146,6 +148,24 @@ namespace ViewModelTests
viewModel.GCODEThumbnailIsEnabled = true; viewModel.GCODEThumbnailIsEnabled = true;
} }
[TestMethod]
public void BGCODEThumbnailIsEnabledShouldPrevHandlerWhenSuccessful()
{
// Assert
Func<string, int> sendMockIPCConfigMSG = msg =>
{
SndModuleSettings<SndPowerPreviewSettings> snd = JsonSerializer.Deserialize<SndModuleSettings<SndPowerPreviewSettings>>(msg);
Assert.IsTrue(snd.PowertoysSetting.FileExplorerPreviewSettings.Properties.EnableBgcodeThumbnail);
return 0;
};
// arrange
PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository<PowerPreviewSettings>.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), sendMockIPCConfigMSG, PowerPreviewSettings.ModuleName);
// act
viewModel.BGCODEThumbnailIsEnabled = true;
}
[TestMethod] [TestMethod]
public void STLThumbnailIsEnabledShouldPrevHandlerWhenSuccessful() public void STLThumbnailIsEnabledShouldPrevHandlerWhenSuccessful()
{ {
@@ -254,6 +274,24 @@ namespace ViewModelTests
viewModel.GCODERenderIsEnabled = true; viewModel.GCODERenderIsEnabled = true;
} }
[TestMethod]
public void BGCODERenderIsEnabledShouldPrevHandlerWhenSuccessful()
{
// Assert
Func<string, int> sendMockIPCConfigMSG = msg =>
{
SndModuleSettings<SndPowerPreviewSettings> snd = JsonSerializer.Deserialize<SndModuleSettings<SndPowerPreviewSettings>>(msg);
Assert.IsTrue(snd.PowertoysSetting.FileExplorerPreviewSettings.Properties.EnableBgcodePreview);
return 0;
};
// arrange
PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository<PowerPreviewSettings>.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), sendMockIPCConfigMSG, PowerPreviewSettings.ModuleName);
// act
viewModel.BGCODERenderIsEnabled = true;
}
[TestMethod] [TestMethod]
public void QOIRenderIsEnabledShouldPrevHandlerWhenSuccessful() public void QOIRenderIsEnabledShouldPrevHandlerWhenSuccessful()
{ {

View File

@@ -147,8 +147,18 @@
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
<tkcontrols:SettingsCard <tkcontrols:SettingsCard
x:Uid="FileExplorerPreview_ToggleSwitch_Preview_QOI" x:Uid="FileExplorerPreview_ToggleSwitch_Preview_BGCODE"
HeaderIcon="{ui:FontIcon Glyph=&#xE914;}" HeaderIcon="{ui:FontIcon Glyph=&#xE914;}"
IsEnabled="{x:Bind ViewModel.BGCODERenderIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch
x:Uid="ToggleSwitch"
IsEnabled="{x:Bind ViewModel.BGCODERenderIsGpoEnabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsOn="{x:Bind ViewModel.BGCODERenderIsEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="FileExplorerPreview_ToggleSwitch_Preview_QOI"
HeaderIcon="{ui:FontIcon Glyph=&#xE91B;}"
IsEnabled="{x:Bind ViewModel.QOIRenderIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> IsEnabled="{x:Bind ViewModel.QOIRenderIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch <ToggleSwitch
x:Uid="ToggleSwitch" x:Uid="ToggleSwitch"
@@ -212,8 +222,18 @@
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
<tkcontrols:SettingsCard <tkcontrols:SettingsCard
x:Uid="FileExplorerPreview_ToggleSwitch_Thumbnail_QOI" x:Uid="FileExplorerPreview_ToggleSwitch_Thumbnail_BGCODE"
HeaderIcon="{ui:FontIcon Glyph=&#xE914;}" HeaderIcon="{ui:FontIcon Glyph=&#xE914;}"
IsEnabled="{x:Bind ViewModel.BGCODEThumbnailIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch
x:Uid="ToggleSwitch"
IsEnabled="{x:Bind ViewModel.BGCODEThumbnailIsGpoEnabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsOn="{x:Bind ViewModel.BGCODEThumbnailIsEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="FileExplorerPreview_ToggleSwitch_Thumbnail_QOI"
HeaderIcon="{ui:FontIcon Glyph=&#xE91B;}"
IsEnabled="{x:Bind ViewModel.QOIThumbnailIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> IsEnabled="{x:Bind ViewModel.QOIThumbnailIsGpoDisabled, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch <ToggleSwitch
x:Uid="ToggleSwitch" x:Uid="ToggleSwitch"
@@ -244,7 +264,7 @@
</controls:SettingsPageControl.PrimaryLinks> </controls:SettingsPageControl.PrimaryLinks>
<controls:SettingsPageControl.SecondaryLinks> <controls:SettingsPageControl.SecondaryLinks>
<controls:PageLink Link="https://noraajunker.ch" Text="Noraa Junker's work on developer file preview" /> <controls:PageLink Link="https://noraajunker.ch" Text="Noraa Junker's work on developer file preview" />
<controls:PageLink Link="https://www.pedrolamas.com" Text="Pedro Lamas's work on G-Code, STL, and QOI" /> <controls:PageLink Link="https://www.pedrolamas.com" Text="Pedro Lamas's work on G-Code, Binary G-Code, STL, and QOI" />
</controls:SettingsPageControl.SecondaryLinks> </controls:SettingsPageControl.SecondaryLinks>
</controls:SettingsPageControl> </controls:SettingsPageControl>
</Page> </Page>

View File

@@ -2002,7 +2002,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>File Locksmith lists which processes are using the selected files or directories and allows closing those processes.</value> <value>File Locksmith lists which processes are using the selected files or directories and allows closing those processes.</value>
</data> </data>
<data name="Oobe_FileExplorer.Description" xml:space="preserve"> <data name="Oobe_FileExplorer.Description" xml:space="preserve">
<value>PowerToys introduces add-ons to the Windows File Explorer that will enable files like Markdown (.md), PDF (.pdf), SVG (.svg), STL (.stl), G-code (.gcode) and developer files to be viewed in the preview pane. It introduces File Explorer thumbnail support for a number of these file types as well.</value> <value>PowerToys introduces add-ons to the Windows File Explorer that will enable files like Markdown (.md), PDF (.pdf), SVG (.svg), STL (.stl), G-code (.gcode), Binary G-code (.bgcode), and developer files to be viewed in the preview pane. It introduces File Explorer thumbnail support for a number of these file types as well.</value>
</data> </data>
<data name="Oobe_ImageResizer.Description" xml:space="preserve"> <data name="Oobe_ImageResizer.Description" xml:space="preserve">
<value>Image Resizer is a Windows shell extension for simple bulk image-resizing.</value> <value>Image Resizer is a Windows shell extension for simple bulk image-resizing.</value>
@@ -5015,6 +5015,18 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="BugReportUnderConstruction" xml:space="preserve"> <data name="BugReportUnderConstruction" xml:space="preserve">
<value>Bug report package is being created</value> <value>Bug report package is being created</value>
</data> </data>
<data name="FileExplorerPreview_ToggleSwitch_Preview_BGCODE.Header" xml:space="preserve">
<value>Binary Geometric Code</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_Preview_BGCODE.Description" xml:space="preserve">
<value>.bgcode</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_Thumbnail_BGCODE.Description" xml:space="preserve">
<value>.bgcode</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_Thumbnail_BGCODE.Header" xml:space="preserve">
<value>Binary Geometric Code</value>
</data>
<data name="HighlightMode.Description" xml:space="preserve"> <data name="HighlightMode.Description" xml:space="preserve">
<value>Highlight the cursor or dim the screen to spotlight it</value> <value>Highlight the cursor or dim the screen to spotlight it</value>
</data> </data>

View File

@@ -125,6 +125,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_gcodeRenderIsEnabled = Settings.Properties.EnableGcodePreview; _gcodeRenderIsEnabled = Settings.Properties.EnableGcodePreview;
} }
_bgcodeRenderEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredBgcodePreviewEnabledValue();
if (_bgcodeRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _bgcodeRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
_bgcodeRenderEnabledStateIsGPOConfigured = true;
_bgcodeRenderIsEnabled = _bgcodeRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
_bgcodeRenderIsGpoEnabled = _bgcodeRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
_bgcodeRenderIsGpoDisabled = _bgcodeRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled;
}
else
{
_bgcodeRenderIsEnabled = Settings.Properties.EnableBgcodePreview;
}
_qoiRenderEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredQoiPreviewEnabledValue(); _qoiRenderEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredQoiPreviewEnabledValue();
if (_qoiRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _qoiRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) if (_qoiRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _qoiRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{ {
@@ -181,6 +195,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_gcodeThumbnailIsEnabled = Settings.Properties.EnableGcodeThumbnail; _gcodeThumbnailIsEnabled = Settings.Properties.EnableGcodeThumbnail;
} }
_bgcodeThumbnailEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredBgcodeThumbnailsEnabledValue();
if (_bgcodeThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _bgcodeThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
_bgcodeThumbnailEnabledStateIsGPOConfigured = true;
_bgcodeThumbnailIsEnabled = _bgcodeThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
_bgcodeThumbnailIsGpoEnabled = _bgcodeThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
_bgcodeThumbnailIsGpoDisabled = _bgcodeThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled;
}
else
{
_bgcodeThumbnailIsEnabled = Settings.Properties.EnableBgcodeThumbnail;
}
_stlThumbnailEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredStlThumbnailsEnabledValue(); _stlThumbnailEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredStlThumbnailsEnabledValue();
if (_stlThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _stlThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) if (_stlThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _stlThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{ {
@@ -251,6 +279,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private bool _gcodeRenderIsGpoDisabled; private bool _gcodeRenderIsGpoDisabled;
private bool _gcodeRenderIsEnabled; private bool _gcodeRenderIsEnabled;
private GpoRuleConfigured _bgcodeRenderEnabledGpoRuleConfiguration;
private bool _bgcodeRenderEnabledStateIsGPOConfigured;
private bool _bgcodeRenderIsGpoEnabled;
private bool _bgcodeRenderIsGpoDisabled;
private bool _bgcodeRenderIsEnabled;
private GpoRuleConfigured _qoiRenderEnabledGpoRuleConfiguration; private GpoRuleConfigured _qoiRenderEnabledGpoRuleConfiguration;
private bool _qoiRenderEnabledStateIsGPOConfigured; private bool _qoiRenderEnabledStateIsGPOConfigured;
private bool _qoiRenderIsGpoEnabled; private bool _qoiRenderIsGpoEnabled;
@@ -275,6 +309,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private bool _gcodeThumbnailIsGpoDisabled; private bool _gcodeThumbnailIsGpoDisabled;
private bool _gcodeThumbnailIsEnabled; private bool _gcodeThumbnailIsEnabled;
private GpoRuleConfigured _bgcodeThumbnailEnabledGpoRuleConfiguration;
private bool _bgcodeThumbnailEnabledStateIsGPOConfigured;
private bool _bgcodeThumbnailIsGpoEnabled;
private bool _bgcodeThumbnailIsGpoDisabled;
private bool _bgcodeThumbnailIsEnabled;
private GpoRuleConfigured _stlThumbnailEnabledGpoRuleConfiguration; private GpoRuleConfigured _stlThumbnailEnabledGpoRuleConfiguration;
private bool _stlThumbnailEnabledStateIsGPOConfigured; private bool _stlThumbnailEnabledStateIsGPOConfigured;
private bool _stlThumbnailIsGpoEnabled; private bool _stlThumbnailIsGpoEnabled;
@@ -294,7 +334,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{ {
return _svgRenderEnabledStateIsGPOConfigured || _mdRenderEnabledStateIsGPOConfigured return _svgRenderEnabledStateIsGPOConfigured || _mdRenderEnabledStateIsGPOConfigured
|| _monacoRenderEnabledStateIsGPOConfigured || _pdfRenderEnabledStateIsGPOConfigured || _monacoRenderEnabledStateIsGPOConfigured || _pdfRenderEnabledStateIsGPOConfigured
|| _gcodeRenderEnabledStateIsGPOConfigured || _qoiRenderEnabledStateIsGPOConfigured; || _gcodeRenderEnabledStateIsGPOConfigured || _qoiRenderEnabledStateIsGPOConfigured
|| _bgcodeRenderEnabledStateIsGPOConfigured;
} }
} }
@@ -304,7 +345,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{ {
return _svgThumbnailEnabledStateIsGPOConfigured || _pdfThumbnailEnabledStateIsGPOConfigured return _svgThumbnailEnabledStateIsGPOConfigured || _pdfThumbnailEnabledStateIsGPOConfigured
|| _gcodeThumbnailEnabledStateIsGPOConfigured || _stlThumbnailEnabledStateIsGPOConfigured || _gcodeThumbnailEnabledStateIsGPOConfigured || _stlThumbnailEnabledStateIsGPOConfigured
|| _qoiThumbnailEnabledStateIsGPOConfigured; || _qoiThumbnailEnabledStateIsGPOConfigured || _bgcodeThumbnailEnabledStateIsGPOConfigured;
} }
} }
@@ -778,6 +819,48 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
} }
} }
public bool BGCODERenderIsEnabled
{
get
{
return _bgcodeRenderIsEnabled;
}
set
{
if (_bgcodeRenderEnabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
}
if (value != _bgcodeRenderIsEnabled)
{
_bgcodeRenderIsEnabled = value;
Settings.Properties.EnableBgcodePreview = value;
RaisePropertyChanged();
}
}
}
// Used to only disable enabled button on forced enabled state. (With this users still able to change the utility properties.)
public bool BGCODERenderIsGpoEnabled
{
get
{
return _bgcodeRenderIsGpoEnabled;
}
}
// Used to disable the settings card on forced disabled state.
public bool BGCODERenderIsGpoDisabled
{
get
{
return _bgcodeRenderIsGpoDisabled;
}
}
public bool GCODEThumbnailIsEnabled public bool GCODEThumbnailIsEnabled
{ {
get get
@@ -820,6 +903,48 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
} }
} }
public bool BGCODEThumbnailIsEnabled
{
get
{
return _bgcodeThumbnailIsEnabled;
}
set
{
if (_bgcodeThumbnailEnabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
}
if (value != _bgcodeThumbnailIsEnabled)
{
_bgcodeThumbnailIsEnabled = value;
Settings.Properties.EnableBgcodeThumbnail = value;
RaisePropertyChanged();
}
}
}
// Used to only disable enabled button on forced enabled state. (With this users still able to change the utility properties.)
public bool BGCODEThumbnailIsGpoEnabled
{
get
{
return _bgcodeThumbnailIsGpoEnabled;
}
}
// Used to disable the settings card on forced disabled state.
public bool BGCODEThumbnailIsGpoDisabled
{
get
{
return _bgcodeThumbnailIsGpoDisabled;
}
}
public bool STLThumbnailIsEnabled public bool STLThumbnailIsEnabled
{ {
get get

View File

@@ -27,6 +27,8 @@ std::vector<std::wstring> processes =
L"PowerToys.Hosts.exe", L"PowerToys.Hosts.exe",
L"PowerToys.GcodePreviewHandler.exe", L"PowerToys.GcodePreviewHandler.exe",
L"PowerToys.GcodeThumbnailProvider.exe", L"PowerToys.GcodeThumbnailProvider.exe",
L"PowerToys.BgcodePreviewHandler.exe",
L"PowerToys.BgcodeThumbnailProvider.exe",
L"PowerToys.MarkdownPreviewHandler.exe", L"PowerToys.MarkdownPreviewHandler.exe",
L"PowerToys.MonacoPreviewHandler.exe", L"PowerToys.MonacoPreviewHandler.exe",
L"PowerToys.PdfPreviewHandler.exe", L"PowerToys.PdfPreviewHandler.exe",

View File

@@ -17,9 +17,11 @@ namespace
{ HKEY_CLASSES_ROOT, L"AppID\\{CF142243-F059-45AF-8842-DBBE9783DB14}" }, { HKEY_CLASSES_ROOT, L"AppID\\{CF142243-F059-45AF-8842-DBBE9783DB14}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{07665729-6243-4746-95b7-79579308d1b2}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{07665729-6243-4746-95b7-79579308d1b2}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{ec52dea8-7c9f-4130-a77b-1737d0418507}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{ec52dea8-7c9f-4130-a77b-1737d0418507}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{dd8de316-7b01-48e7-ba21-e92c646704af}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{8AA07897-C30B-4543-865B-00A0E5A1B32D}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{8AA07897-C30B-4543-865B-00A0E5A1B32D}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{BCC13D15-9720-4CC4-8371-EA74A274741E}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{BCC13D15-9720-4CC4-8371-EA74A274741E}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{BFEE99B4-B74D-4348-BCA5-E757029647FF}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{BFEE99B4-B74D-4348-BCA5-E757029647FF}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{c28761a0-8420-43ad-bff3-40400543e2d4}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\\InprocServer32" }, { HKEY_CLASSES_ROOT, L"CLSID\\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\\InprocServer32" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{0440049F-D1DC-4E46-B27B-98393D79486B}" }, { HKEY_CLASSES_ROOT, L"CLSID\\{0440049F-D1DC-4E46-B27B-98393D79486B}" },
@@ -41,6 +43,7 @@ namespace
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}" }, { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{07665729-6243-4746-95b7-79579308d1b2}" }, { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{07665729-6243-4746-95b7-79579308d1b2}" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{ec52dea8-7c9f-4130-a77b-1737d0418507}" }, { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{ec52dea8-7c9f-4130-a77b-1737d0418507}" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{dd8de316-7b01-48e7-ba21-e92c646704af}" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{8AA07897-C30B-4543-865B-00A0E5A1B32D}" }, { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{8AA07897-C30B-4543-865B-00A0E5A1B32D}" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"prevhost.exe" }, { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"prevhost.exe" },
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"dllhost.exe" } { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"dllhost.exe" }

View File

@@ -55,9 +55,11 @@ void ReportGPOValues(const std::filesystem::path &tmpDir)
report << "getConfiguredMonacoPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()) << std::endl; report << "getConfiguredMonacoPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()) << std::endl;
report << "getConfiguredPdfPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPdfPreviewEnabledValue()) << std::endl; report << "getConfiguredPdfPreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPdfPreviewEnabledValue()) << std::endl;
report << "getConfiguredGcodePreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredGcodePreviewEnabledValue()) << std::endl; report << "getConfiguredGcodePreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredGcodePreviewEnabledValue()) << std::endl;
report << "getConfiguredBgcodePreviewEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredBgcodePreviewEnabledValue()) << std::endl;
report << "getConfiguredSvgThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue()) << std::endl; report << "getConfiguredSvgThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue()) << std::endl;
report << "getConfiguredPdfThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue()) << std::endl; report << "getConfiguredPdfThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue()) << std::endl;
report << "getConfiguredGcodeThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue()) << std::endl; report << "getConfiguredGcodeThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue()) << std::endl;
report << "getConfiguredBgcodeThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredBgcodeThumbnailsEnabledValue()) << std::endl;
report << "getConfiguredStlThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue()) << std::endl; report << "getConfiguredStlThumbnailsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue()) << std::endl;
report << "getConfiguredHostsFileEditorEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredHostsFileEditorEnabledValue()) << std::endl; report << "getConfiguredHostsFileEditorEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredHostsFileEditorEnabledValue()) << std::endl;
report << "getConfiguredImageResizerEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredImageResizerEnabledValue()) << std::endl; report << "getConfiguredImageResizerEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredImageResizerEnabledValue()) << std::endl;

View File

@@ -15,12 +15,13 @@ function PublicStaticVoidMain {
[String] $MachineWideHandler [String] $MachineWideHandler
} }
[String[]]$TypesToCheck = @(".markdown", ".mdtext", ".mdtxt", ".mdown", ".mkdn", ".mdwn", ".mkd", ".md", ".svg", ".svgz", ".pdf", ".gcode", ".stl", ".txt", ".ini", ".qoi") [String[]]$TypesToCheck = @(".markdown", ".mdtext", ".mdtxt", ".mdown", ".mkdn", ".mdwn", ".mkd", ".md", ".svg", ".svgz", ".pdf", ".gcode", ".bgcode", ".stl", ".txt", ".ini", ".qoi")
$IPREVIEW_HANDLER_CLSID = '{8895b1c6-b41f-4c1c-a562-0d564250836f}' $IPREVIEW_HANDLER_CLSID = '{8895b1c6-b41f-4c1c-a562-0d564250836f}'
$PowerToysHandlers = @{ $PowerToysHandlers = @{
'{07665729-6243-4746-95b7-79579308d1b2}' = "PowerToys PDF handler" '{07665729-6243-4746-95b7-79579308d1b2}' = "PowerToys PDF handler"
'{ddee2b8a-6807-48a6-bb20-2338174ff779}' = "PowerToys SVG handler" '{ddee2b8a-6807-48a6-bb20-2338174ff779}' = "PowerToys SVG handler"
'{ec52dea8-7c9f-4130-a77b-1737d0418507}' = "PowerToys GCode handler" '{ec52dea8-7c9f-4130-a77b-1737d0418507}' = "PowerToys GCode handler"
'{dd8de316-7b01-48e7-ba21-e92c646704af}' = "PowerToys BGCode handler"
'{8AA07897-C30B-4543-865B-00A0E5A1B32D}' = "PowerToys QOI handler" '{8AA07897-C30B-4543-865B-00A0E5A1B32D}' = "PowerToys QOI handler"
'{45769bcc-e8fd-42d0-947e-02beef77a1f5}' = "PowerToys Markdown handler" '{45769bcc-e8fd-42d0-947e-02beef77a1f5}' = "PowerToys Markdown handler"
'{afbd5a44-2520-4ae0-9224-6cfce8fe4400}' = "PowerToys Monaco fallback handler" '{afbd5a44-2520-4ae0-9224-6cfce8fe4400}' = "PowerToys Monaco fallback handler"