Self-contained .NET (#22217)

* dotnet sc

* MD preview - C# app

 - working self-contained

* Gcode preview - C# app

* DevFiles preview - C# app

* Fix passing path with spaces as cmd arg and monacocpp proj file

* Pdf preview - C# app

* Svg preview - C# app

* Fix comment

* Gcode thumbnail - C# app

TODO:
	- installer
	- why IThumbnailProvider and IIntializeWithFile doesn't work?

* Pdf thumbnail - C# app

TODO:
        - installer
        - why IThumbnailProvider and IIntializeWithFile doesn't work?

* Pdf thumbnail - C# app

TODO:
        - installer
        - why IThumbnailProvider and IIntializeWithFile doesn't work?

* Fix GcodeThumbnailProviderCpp.vcxproj

* Svg thumbnail - C# app

TODO:
        - installer
        - why IThumbnailProvider and IIntializeWithFile doesn't work?

* Fix Svg tests

* Thumbnail providers - installer

* Self-contained Hosts and FileLocksmith

* Fix hardcoded <RuntimeIdentifier>

* Remove unneeded files

* Try to fix Nuget in PR CI

* Prefix new dlls with PowerToys.
Sign new dlls and exes

* Add new .exe files to ProcessList

* ci: debug by listing all env vars

* ci: try setting variable in the right ci file

* Bring back hardcoded RuntimeIdentifier

* ci: Add comment and remove debug action

* Remove unneeded lib

* [WIP] Platform conditional dotnet files & hardlinks

* Cleanup

* Update expect.txt

* Test fix - ARM installer

* Fix uninstall bug

* Update docs

* Fix failing test

* Add dll details

* Minor cleanup

* Improve resizing

* Add some logs

* Test fix - release build

* Remove InvokeOnControlThread

* Test fix: logger initialization

* Fix arm64 installer

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
This commit is contained in:
Stefan Markovic
2022-12-14 13:37:23 +01:00
committed by GitHub
parent a2c0febccc
commit 6ac508fb93
215 changed files with 9060 additions and 2328 deletions

View File

@@ -21,6 +21,15 @@
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<ApplicationIcon>Assets/Icon.ico</ApplicationIcon>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>

View File

@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileLocksmith</PublishDir>
<RuntimeIdentifier>win10-$(Platform)</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>

View File

@@ -20,6 +20,15 @@
<AssemblyName>PowerToys.Hosts</AssemblyName>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
<ApplicationIcon>Assets/Hosts.ico</ApplicationIcon>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->

View File

@@ -13,7 +13,6 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;arm64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
@@ -22,6 +21,14 @@
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->

View File

@@ -10,6 +10,15 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>

View File

@@ -17,6 +17,15 @@
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<PackageProjectUrl>https://awake.den.dev</PackageProjectUrl>
<RepositoryUrl>https://github.com/microsoft/powertoys</RepositoryUrl>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->

View File

@@ -9,7 +9,17 @@
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<UseWPF>true</UseWPF>
<StartupObject>ColorPicker.Program</StartupObject>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{BA58206B-1493-4C75-BFEA-A85768A1E156}</ProjectGuid>
<OutputType>WinExe</OutputType>

View File

@@ -11,9 +11,18 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\FancyZones</OutputPath>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\FancyZones</OutputPath>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}</ProjectGuid>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>

View File

@@ -8,8 +8,17 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<UseWPF>true</UseWPF>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}</ProjectGuid>
<OutputType>WinExe</OutputType>

View File

@@ -19,8 +19,16 @@
<AssemblyName>PowerToys.PowerLauncher</AssemblyName>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\modules\launcher\</OutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>

View File

@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\modules\launcher</PublishDir>
<RuntimeIdentifier>win-$(Platform)</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>

View File

@@ -7,8 +7,18 @@
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<UseWindowsForms>true</UseWindowsForms>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<CsWinRTIncludes>PowerToys.GPOWrapper;PowerToys.PowerAccentKeyboardService</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>

View File

@@ -10,10 +10,19 @@
<ApplicationIcon>icon.ico</ApplicationIcon>
<AssemblyName>PowerToys.PowerAccent</AssemblyName>
<XamlDebuggingInformation>True</XamlDebuggingInformation>
<StartupObject>PowerAccent.UI.Program</StartupObject>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<StartupObject>PowerAccent.UI.Program</StartupObject>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,71 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Common;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.PreviewHandler.Gcode
{
/// <summary>
/// Extends <see cref="StreamBasedPreviewHandler"/> for Gcode Preview Handler.
/// </summary>
[Guid("ec52dea8-7c9f-4130-a77b-1737d0418507")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class GcodePreviewHandler : StreamBasedPreviewHandler, IDisposable
{
private GcodePreviewHandlerControl _gcodePreviewControl;
private bool disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="GcodePreviewHandler"/> class.
/// </summary>
public GcodePreviewHandler()
{
Initialize();
}
/// <inheritdoc/>
public override void DoPreview()
{
_gcodePreviewControl.DoPreview(Stream);
}
/// <inheritdoc/>
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.GcodeFileHandlerLoaded());
_gcodePreviewControl = new GcodePreviewHandlerControl();
return _gcodePreviewControl;
}
/// <summary>
/// Disposes objects
/// </summary>
/// <param name="disposing">Is Disposing</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_gcodePreviewControl.Dispose();
}
disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyTitle>PowerToys.GcodePreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys GcodePreviewHandler</AssemblyDescription>
@@ -11,13 +12,21 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AssemblyName>PowerToys.GcodePreviewHandler</AssemblyName>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{805306FF-A562-4415-8DEF-E493BDC45918}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Gcode</RootNamespace>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<EnableComHosting>true</EnableComHosting>
</PropertyGroup>
<Import Project="..\..\..\Version.props" />
@@ -40,6 +49,7 @@
<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>
@@ -48,7 +58,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.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>

View File

@@ -46,52 +46,50 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGcodePreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility. Show an error message instead.
InvokeOnControlThread(() =>
{
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.GpoDisabledErrorText);
Resize += FormResized;
base.DoPreview(dataSource);
});
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.GpoDisabledErrorText);
Resize += FormResized;
base.DoPreview(dataSource);
return;
}
InvokeOnControlThread(() =>
try
{
try
Bitmap thumbnail = null;
if (!(dataSource is string filePath))
{
Bitmap thumbnail = null;
using (var stream = new ReadonlyStream(dataSource as IStream))
{
using (var reader = new StreamReader(stream))
{
thumbnail = GetThumbnail(reader);
}
}
_infoBarAdded = false;
if (thumbnail == null)
{
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.GcodeWithoutEmbeddedThumbnails);
}
else
{
AddPictureBoxControl(thumbnail);
}
Resize += FormResized;
base.DoPreview(dataSource);
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed());
throw new ArgumentException($"{nameof(dataSource)} for {nameof(GcodePreviewHandlerControl)} must be a string but was a '{typeof(T)}'");
}
catch (Exception ex)
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using (var reader = new StreamReader(fs))
{
PreviewError(ex, dataSource);
thumbnail = GetThumbnail(reader);
}
});
_infoBarAdded = false;
if (thumbnail == null)
{
_infoBarAdded = true;
AddTextBoxControl(Properties.Resource.GcodeWithoutEmbeddedThumbnails);
}
else
{
AddPictureBoxControl(thumbnail);
}
Resize += FormResized;
base.DoPreview(fs);
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed());
}
catch (Exception ex)
{
PreviewError(ex, dataSource);
}
}
/// <summary>

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.PreviewHandler.Gcode
{
using System.Globalization;
using System.Windows.Threading;
using Common.UI;
using interop;
internal static class Program
{
private static CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static GcodePreviewHandlerControl _previewHandlerControl;
/// <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)
{
string filePath = args[0];
int hwnd = Convert.ToInt32(args[1], 16);
Rectangle s = default(Rectangle);
int left = Convert.ToInt32(args[2], 10);
int right = Convert.ToInt32(args[3], 10);
int top = Convert.ToInt32(args[4], 10);
int bottom = Convert.ToInt32(args[5], 10);
_previewHandlerControl = new GcodePreviewHandlerControl();
_previewHandlerControl.SetWindow((IntPtr)hwnd, s);
_previewHandlerControl.DoPreview(filePath);
NativeEventWaiter.WaitForEventLoop(
Constants.GcodePreviewResizeEvent(),
() =>
{
Rectangle s = default(Rectangle);
_previewHandlerControl.SetRect(s);
},
Dispatcher.CurrentDispatcher,
_tokenSource.Token);
}
else
{
MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
Application.Run();
}
}
}

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "GcodePreviewHandler.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;
GcodePreviewHandler* pExt = new (std::nothrow) GcodePreviewHandler();
if (pExt)
{
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement(&g_cDllRef);
}
else
{
InterlockedDecrement(&g_cDllRef);
}
return S_OK;
}

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,262 @@
#include "pch.h"
#include "GcodePreviewHandler.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>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
GcodePreviewHandler::GcodePreviewHandler() :
m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL)
{
m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT);
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::gcodePrevLogPath);
Logger::init(LogSettings::gcodePrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
GcodePreviewHandler::~GcodePreviewHandler()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP GcodePreviewHandler::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(GcodePreviewHandler, IPreviewHandler),
QITABENT(GcodePreviewHandler, IInitializeWithFile),
QITABENT(GcodePreviewHandler, IPreviewHandlerVisuals),
QITABENT(GcodePreviewHandler, IOleWindow),
QITABENT(GcodePreviewHandler, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
GcodePreviewHandler::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
GcodePreviewHandler::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithFile
IFACEMETHODIMP GcodePreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode)
{
m_filePath = pszFilePath;
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandler
IFACEMETHODIMP GcodePreviewHandler::SetWindow(HWND hwnd, const RECT* prc)
{
if (hwnd && prc)
{
m_hwndParent = hwnd;
m_rcParent = *prc;
}
return S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::SetFocus()
{
return S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::QueryFocus(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = ::GetFocus();
if (*phwnd)
{
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
IFACEMETHODIMP GcodePreviewHandler::TranslateAccelerator(MSG* pmsg)
{
HRESULT hr = S_FALSE;
IPreviewHandlerFrame* pFrame = NULL;
if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame)))
{
hr = pFrame->TranslateAccelerator(pmsg);
pFrame->Release();
}
return hr;
}
IFACEMETHODIMP GcodePreviewHandler::SetRect(const RECT* prc)
{
HRESULT hr = E_INVALIDARG;
if (prc != NULL)
{
if (!m_resizeEvent)
{
Logger::error(L"Failed to create resize event for GcodePreviewHandler");
}
else
{
if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom)
{
if (!SetEvent(m_resizeEvent))
{
Logger::error(L"Failed to signal resize event for GcodePreviewHandler");
}
}
}
m_rcParent = *prc;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP GcodePreviewHandler::DoPreview()
{
try
{
Logger::info(L"Starting GcodePreviewHandler.exe");
STARTUPINFO info = { sizeof(info) };
std::wstring cmdLine{ L"\"" + m_filePath + L"\"" };
cmdLine += L" ";
std::wostringstream ss;
ss << std::hex << m_hwndParent;
cmdLine += ss.str();
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.left);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.right);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.top);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.bottom);
std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.GcodePreviewHandler.exe";
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = appPath.c_str();
sei.lpParameters = cmdLine.c_str();
sei.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&sei);
m_process = sei.hProcess;
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start GcodePreviewHandler.exe. Error: {}", errorMessage);
}
return S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::Unload()
{
Logger::info(L"Unload and terminate .exe");
m_hwndParent = NULL;
TerminateProcess(m_process, 0);
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandlerVisuals
IFACEMETHODIMP GcodePreviewHandler::SetBackgroundColor(COLORREF color)
{
return S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::SetFont(const LOGFONTW* plf)
{
return S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::SetTextColor(COLORREF color)
{
return S_OK;
}
#pragma endregion
#pragma region IOleWindow
IFACEMETHODIMP GcodePreviewHandler::GetWindow(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = m_hwndParent;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP GcodePreviewHandler::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP GcodePreviewHandler::SetSite(IUnknown* punkSite)
{
if (m_punkSite)
{
m_punkSite->Release();
m_punkSite = NULL;
}
return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK;
}
IFACEMETHODIMP GcodePreviewHandler::GetSite(REFIID riid, void** ppv)
{
*ppv = NULL;
return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,69 @@
#pragma once
#include "pch.h"
#include <filesystem>
#include <ShlObj.h>
#include <string>
class GcodePreviewHandler :
public IInitializeWithFile,
public IPreviewHandler,
public IPreviewHandlerVisuals,
public IOleWindow,
public IObjectWithSite
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithFile
IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode);
// IPreviewHandler
IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc);
IFACEMETHODIMP SetFocus();
IFACEMETHODIMP QueryFocus(HWND* phwnd);
IFACEMETHODIMP TranslateAccelerator(MSG* pmsg);
IFACEMETHODIMP SetRect(const RECT* prc);
IFACEMETHODIMP DoPreview();
IFACEMETHODIMP Unload();
// IPreviewHandlerVisuals
IFACEMETHODIMP SetBackgroundColor(COLORREF color);
IFACEMETHODIMP SetFont(const LOGFONTW* plf);
IFACEMETHODIMP SetTextColor(COLORREF color);
// IOleWindow
IFACEMETHODIMP GetWindow(HWND* phwnd);
IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* punkSite);
IFACEMETHODIMP GetSite(REFIID riid, void** ppv);
GcodePreviewHandler();
protected:
~GcodePreviewHandler();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
std::wstring m_filePath;
// Parent window that hosts the previewer window.
// Note: do NOT DestroyWindow this.
HWND m_hwndParent;
// Bounding rect of the parent window.
RECT m_rcParent;
// Site pointer from host, used to get IPreviewHandlerFrame.
IUnknown* m_punkSite;
HANDLE m_process;
HANDLE m_resizeEvent;
};

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.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{5a5dd09d-723a-44d3-8f2b-293584c3d731}</ProjectGuid>
<RootNamespace>GcodePreviewHandlerCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</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>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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="GcodePreviewHandler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="GcodePreviewHandler.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GcodePreviewHandlerCpp.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>
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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="GcodePreviewHandler.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="GcodePreviewHandler.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="GcodePreviewHandlerCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

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;
// {A0257634-8812-4CE8-AF11-FA69ACAEAFAE}
static const GUID CLSID_GcodePreviewHandler = { 0xa0257634, 0x8812, 0x4ce8, { 0xaf, 0x11, 0xfa, 0x69, 0xac, 0xae, 0xaf, 0xae } };
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// FUNCTION: DllGetClassObject
//
// PURPOSE: Create the class factory and query to the specific interface.
//
// PARAMETERS:
// * rclsid - The CLSID that will associate the correct data and code.
// * riid - A reference to the identifier of the interface that the caller
// is to use to communicate with the class object.
// * ppv - The address of a pointer variable that receives the interface
// pointer requested in riid. Upon successful return, *ppv contains the
// requested interface pointer. If an error occurs, the interface pointer
// is NULL.
//
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
if (IsEqualCLSID(CLSID_GcodePreviewHandler, rclsid))
{
hr = E_OUTOFMEMORY;
ClassFactory* pClassFactory = new ClassFactory();
if (pClassFactory)
{
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
}
return hr;
}
//
// FUNCTION: DllCanUnloadNow
//
// PURPOSE: Check if we can unload the component from the memory.
//
// NOTE: The component can be unloaded from the memory when its reference
// count is zero (i.e. nobody is still using the component).
//
STDAPI DllCanUnloadNow(void)
{
return g_cDllRef > 0 ? S_FALSE : S_OK;
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" 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 Gcode Preview Handler Module"
#define INTERNAL_NAME "PowerToys.GcodePreviewHandlerCpp"
#define ORIGINAL_FILENAME "PowerToys.GcodePreviewHandlerCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -1,32 +1,31 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Common.ComInterlop;
using Common.Utilities;
namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
{
/// <summary>
/// G-code Thumbnail Provider.
/// </summary>
[Guid("BFEE99B4-B74D-4348-BCA5-E757029647FF")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class GcodeThumbnailProvider : IInitializeWithStream, IThumbnailProvider
public class GcodeThumbnailProvider
{
public GcodeThumbnailProvider(string filePath)
{
FilePath = filePath;
Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
}
/// <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 IStream Stream { get; private set; }
public Stream Stream { get; private set; }
/// <summary>
/// The maximum dimension (width or height) thumbnail we will generate.
@@ -140,44 +139,36 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
return destImage;
}
/// <inheritdoc/>
public void Initialize(IStream pstream, uint grfMode)
/// <summary>
/// Generate thumbnail bitmap for provided Gcode file/stream.
/// </summary>
/// <param name="cx">Maximum thumbnail size, in pixels.</param>
/// <returns>Generated bitmap</returns>
public Bitmap GetThumbnail(uint cx)
{
// Ignore the grfMode always use read mode to access the file.
this.Stream = pstream;
}
/// <inheritdoc/>
public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha)
{
phbmp = IntPtr.Zero;
pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN;
if (cx == 0 || cx > MaxThumbnailSize)
{
return;
return null;
}
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGcodeThumbnailsEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility.
return;
return null;
}
using (var stream = new ReadonlyStream(this.Stream as IStream))
using (var reader = new StreamReader(this.Stream))
{
using (var reader = new StreamReader(stream))
using (Bitmap thumbnail = GetThumbnail(reader, cx))
{
using (Bitmap thumbnail = GetThumbnail(reader, cx))
if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0)
{
if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0)
{
phbmp = thumbnail.GetHbitmap(Color.Transparent);
pdwAlpha = WTS_ALPHATYPE.WTSAT_ARGB;
}
return (Bitmap)thumbnail.Clone();
}
}
}
return null;
}
}
}

View File

@@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<ProjectGuid>{809AA252-E17A-4FA2-B0A1-0450976B763F}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.ThumbnailHandler.Gcode</RootNamespace>
@@ -7,13 +8,21 @@
<AssemblyTitle>PowerToys.GcodeThumbnailProvider</AssemblyTitle>
<AssemblyDescription>PowerToys GcodePreviewHandler</AssemblyDescription>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<EnableComHosting>true</EnableComHosting>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Description>PowerToys GcodePreviewHandler</Description>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<Import Project="..\..\..\Version.props" />
@@ -27,6 +36,7 @@
<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>

View File

@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
{
using System.Globalization;
internal static class Program
{
private static GcodeThumbnailProvider _thumbnailProvider;
/// <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 GcodeThumbnailProvider(filePath);
Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx);
filePath = filePath.Replace(".gcode", ".bmp");
thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp);
}
else
{
MessageBox.Show("Gcode thumbnail - wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
}
}
}

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "GcodeThumbnailProvider.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;
GcodeThumbnailProvider* pExt = new (std::nothrow) GcodeThumbnailProvider();
if (pExt)
{
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement(&g_cDllRef);
}
else
{
InterlockedDecrement(&g_cDllRef);
}
return S_OK;
}

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,185 @@
#include "pch.h"
#include "GcodeThumbnailProvider.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;
GcodeThumbnailProvider::GcodeThumbnailProvider() :
m_cRef(1), m_pStream(NULL), m_process(NULL)
{
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::gcodeThumbLogPath);
Logger::init(LogSettings::gcodeThumbLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
GcodeThumbnailProvider::~GcodeThumbnailProvider()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP GcodeThumbnailProvider::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(GcodeThumbnailProvider, IThumbnailProvider),
QITABENT(GcodeThumbnailProvider, IInitializeWithStream),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
GcodeThumbnailProvider::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
GcodeThumbnailProvider::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithStream
IFACEMETHODIMP GcodeThumbnailProvider::Initialize(IStream* pStream, DWORD grfMode)
{
HRESULT hr = E_INVALIDARG;
if (pStream)
{
// Initialize can be called more than once, so release existing valid
// m_pStream.
if (m_pStream)
{
m_pStream->Release();
m_pStream = NULL;
}
m_pStream = pStream;
m_pStream->AddRef();
hr = S_OK;
}
return hr;
}
#pragma endregion
#pragma region IThumbnailProvider
IFACEMETHODIMP GcodeThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha)
{
// Read stream into the buffer
char buffer[4096];
ULONG cbRead;
Logger::trace(L"Begin");
GUID guid;
if (CoCreateGuid(&guid) == S_OK)
{
wil::unique_cotaskmem_string guidString;
if (SUCCEEDED(StringFromCLSID(guid, &guidString)))
{
Logger::info(L"Read stream and save to tmp file.");
// {CLSID} -> CLSID
std::wstring guid = std::wstring(guidString.get()).substr(1, std::wstring(guidString.get()).size() - 2);
std::wstring filePath = PTSettingsHelper::get_local_low_folder_location() + L"\\GCodeThumbnail-Temp\\";
if (!std::filesystem::exists(filePath))
{
std::filesystem::create_directories(filePath);
}
std::wstring fileName = filePath + guid + L".gcode";
// Write data to tmp file
std::fstream file;
file.open(fileName, std::ios_base::out | std::ios_base::binary);
if (!file.is_open())
{
return 0;
}
while (true)
{
auto result = m_pStream->Read(buffer, 4096, &cbRead);
file.write(buffer, cbRead);
if (result == S_FALSE)
{
break;
}
}
file.close();
try
{
Logger::info(L"Start GcodeThumbnailProvider.exe");
STARTUPINFO info = { sizeof(info) };
std::wstring cmdLine{ L"\"" + fileName + L"\"" };
cmdLine += L" ";
cmdLine += std::to_wstring(cx);
std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.GcodeThumbnailProvider.exe";
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = appPath.c_str();
sei.lpParameters = cmdLine.c_str();
sei.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&sei);
m_process = sei.hProcess;
WaitForSingleObject(m_process, INFINITE);
std::filesystem::remove(fileName);
std::wstring fileNameBmp = filePath + guid + L".bmp";
*phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
*pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB;
std::filesystem::remove(fileNameBmp);
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start GcodeThumbnailProvider.exe. Error: {}", errorMessage);
}
}
}
return S_OK;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,37 @@
#pragma once
#include "pch.h"
#include <ShlObj.h>
#include <string>
#include <thumbcache.h>
class GcodeThumbnailProvider :
public IInitializeWithStream,
public IThumbnailProvider
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithStream
IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode);
// IPreviewHandler
IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha);
GcodeThumbnailProvider();
protected:
~GcodeThumbnailProvider();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
IStream* m_pStream;
HANDLE m_process;
};

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,121 @@
<?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.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{56cc2f10-6e41-453d-be16-c593a5e58482}</ProjectGuid>
<RootNamespace>GcodeThumbnailProviderCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</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>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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="GcodeThumbnailProvider.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="GcodeThumbnailProvider.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>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="GcodeThumbnailProviderCpp.rc" />
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220914.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220914.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.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220914.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220914.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="GcodeThumbnailProvider.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="GcodeThumbnailProvider.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="GcodeThumbnailProviderCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

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;
// {F2847CBE-CD03-4C83-A359-1A8052C1B9D5}
static const GUID CLSID_GcodeThumbnailProvider = { 0xf2847cbe, 0xcd03, 0x4c83, { 0xa3, 0x59, 0x1a, 0x80, 0x52, 0xc1, 0xb9, 0xd5 } };
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// FUNCTION: DllGetClassObject
//
// PURPOSE: Create the class factory and query to the specific interface.
//
// PARAMETERS:
// * rclsid - The CLSID that will associate the correct data and code.
// * riid - A reference to the identifier of the interface that the caller
// is to use to communicate with the class object.
// * ppv - The address of a pointer variable that receives the interface
// pointer requested in riid. Upon successful return, *ppv contains the
// requested interface pointer. If an error occurs, the interface pointer
// is NULL.
//
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
if (IsEqualCLSID(CLSID_GcodeThumbnailProvider, rclsid))
{
hr = E_OUTOFMEMORY;
ClassFactory* pClassFactory = new ClassFactory();
if (pClassFactory)
{
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
}
return hr;
}
//
// FUNCTION: DllCanUnloadNow
//
// PURPOSE: Check if we can unload the component from the memory.
//
// NOTE: The component can be unloaded from the memory when its reference
// count is zero (i.e. nobody is still using the component).
//
STDAPI DllCanUnloadNow(void)
{
return g_cDllRef > 0 ? S_FALSE : S_OK;
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220914.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 Gcode Thumbnail Provider Module"
#define INTERNAL_NAME "PowerToys.GcodeThumbnailProviderCpp"
#define ORIGINAL_FILENAME "PowerToys.GcodeThumbnailProviderCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -1,71 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Common;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.PreviewHandler.Markdown
{
/// <summary>
/// Implementation of preview handler for markdown files.
/// </summary>
[Guid("45769bcc-e8fd-42d0-947e-02beef77a1f5")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class MarkdownPreviewHandler : FileBasedPreviewHandler, IDisposable
{
private MarkdownPreviewHandlerControl _markdownPreviewHandlerControl;
private bool disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPreviewHandler"/> class.
/// </summary>
public MarkdownPreviewHandler()
{
Initialize();
}
/// <inheritdoc />
public override void DoPreview()
{
_markdownPreviewHandlerControl.DoPreview(FilePath);
}
/// <inheritdoc />
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.MarkdownFileHandlerLoaded());
_markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
return _markdownPreviewHandlerControl;
}
/// <summary>
/// Disposes objects
/// </summary>
/// <param name="disposing">Is Disposing</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_markdownPreviewHandlerControl.Dispose();
}
disposedValue = true;
}
}
/// <inheritdoc />
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyTitle>PowerToys.MarkdownPreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys MarkdownPreviewHandler</AssemblyDescription>
@@ -12,12 +13,20 @@
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Markdown</RootNamespace>
<EnableComHosting>true</EnableComHosting>
<AssemblyName>PowerToys.MarkdownPreviewHandler</AssemblyName>
</PropertyGroup>
@@ -46,6 +55,7 @@
<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>
@@ -58,6 +68,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\common\PreviewHandlerCommon.csproj" />
</ItemGroup>

View File

@@ -133,14 +133,11 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMarkdownPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility. Show an error message instead.
InvokeOnControlThread(() =>
{
_infoBarDisplayed = true;
_infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText);
Resize += FormResized;
Controls.Add(_infoBar);
base.DoPreview(dataSource);
});
_infoBarDisplayed = true;
_infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText);
Resize += FormResized;
Controls.Add(_infoBar);
base.DoPreview(dataSource);
return;
}
@@ -153,7 +150,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
{
if (!(dataSource is string filePath))
{
throw new ArgumentException($"{nameof(dataSource)} for {nameof(MarkdownPreviewHandler)} must be a string but was a '{typeof(T)}'");
throw new ArgumentException($"{nameof(dataSource)} for {nameof(MarkdownPreviewHandlerControl)} must be a string but was a '{typeof(T)}'");
}
string fileText = File.ReadAllText(filePath);
@@ -174,80 +171,74 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
Dock = DockStyle.Fill,
};
InvokeOnControlThread(() =>
{
var webView2Options = new CoreWebView2EnvironmentOptions("--block-new-web-contents");
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
var webView2Options = new CoreWebView2EnvironmentOptions("--block-new-web-contents");
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: _webView2UserDataFolder, options: webView2Options)
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
webView2EnvironmentAwaiter.OnCompleted(async () =>
{
try
{
InvokeOnControlThread(async () =>
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny);
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
_browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
_browser.CoreWebView2.Settings.AreDevToolsEnabled = false;
_browser.CoreWebView2.Settings.AreHostObjectsAllowed = false;
_browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
_browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
_browser.CoreWebView2.Settings.IsScriptEnabled = false;
_browser.CoreWebView2.Settings.IsWebMessageEnabled = false;
// Don't load any resources.
_browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All);
_browser.CoreWebView2.WebResourceRequested += (object sender, CoreWebView2WebResourceRequestedEventArgs e) =>
{
try
// Show local file we've saved with the markdown contents. Block all else.
if (new Uri(e.Request.Uri) != _localFileURI)
{
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Deny);
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
_browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
_browser.CoreWebView2.Settings.AreDevToolsEnabled = false;
_browser.CoreWebView2.Settings.AreHostObjectsAllowed = false;
_browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
_browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
_browser.CoreWebView2.Settings.IsScriptEnabled = false;
_browser.CoreWebView2.Settings.IsWebMessageEnabled = false;
// Don't load any resources.
_browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All);
_browser.CoreWebView2.WebResourceRequested += (object sender, CoreWebView2WebResourceRequestedEventArgs e) =>
{
// Show local file we've saved with the markdown contents. Block all else.
if (new Uri(e.Request.Uri) != _localFileURI)
{
e.Response = _browser.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", null);
}
};
// WebView2.NavigateToString() limitation
// See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks
// While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes
if (markdownHTML.Length > 1_500_000)
{
string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html";
File.WriteAllText(filename, markdownHTML);
_localFileURI = new Uri(filename);
_browser.Source = _localFileURI;
}
else
{
_browser.NavigateToString(markdownHTML);
}
Controls.Add(_browser);
_browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) =>
{
if (args.Uri != null && args.Uri != _localFileURI?.ToString() && args.IsUserInitiated)
{
args.Cancel = true;
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
};
if (_infoBarDisplayed)
{
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
Resize += FormResized;
Controls.Add(_infoBar);
}
e.Response = _browser.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", null);
}
catch (NullReferenceException)
};
// WebView2.NavigateToString() limitation
// See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks
// While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes
if (markdownHTML.Length > 1_500_000)
{
string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html";
File.WriteAllText(filename, markdownHTML);
_localFileURI = new Uri(filename);
_browser.Source = _localFileURI;
}
else
{
_browser.NavigateToString(markdownHTML);
}
Controls.Add(_browser);
_browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) =>
{
if (args.Uri != null && args.Uri != _localFileURI?.ToString() && args.IsUserInitiated)
{
args.Cancel = true;
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
});
});
};
if (_infoBarDisplayed)
{
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
Resize += FormResized;
Controls.Add(_infoBar);
}
}
catch (NullReferenceException)
{
}
});
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());
@@ -256,14 +247,11 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
{
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewError { Message = ex.Message });
InvokeOnControlThread(() =>
{
Controls.Clear();
_infoBarDisplayed = true;
_infoBar = GetTextBoxControl(Resources.MarkdownNotPreviewedError);
Resize += FormResized;
Controls.Add(_infoBar);
});
Controls.Clear();
_infoBarDisplayed = true;
_infoBar = GetTextBoxControl(Resources.MarkdownNotPreviewedError);
Resize += FormResized;
Controls.Add(_infoBar);
}
finally
{

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.PreviewHandler.Markdown
{
using System.Globalization;
using System.Windows.Threading;
using Common.UI;
using interop;
internal static class Program
{
private static CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static MarkdownPreviewHandlerControl _previewHandlerControl;
/// <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)
{
string filePath = args[0];
int hwnd = Convert.ToInt32(args[1], 16);
Rectangle s = default(Rectangle);
int left = Convert.ToInt32(args[2], 10);
int right = Convert.ToInt32(args[3], 10);
int top = Convert.ToInt32(args[4], 10);
int bottom = Convert.ToInt32(args[5], 10);
_previewHandlerControl = new MarkdownPreviewHandlerControl();
_previewHandlerControl.SetWindow((IntPtr)hwnd, s);
_previewHandlerControl.DoPreview(filePath);
NativeEventWaiter.WaitForEventLoop(
Constants.MarkdownPreviewResizeEvent(),
() =>
{
Rectangle s = default(Rectangle);
_previewHandlerControl.SetRect(s);
},
Dispatcher.CurrentDispatcher,
_tokenSource.Token);
}
else
{
MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
Application.Run();
}
}
}

View File

@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview</PublishDir>
<RuntimeIdentifier>win10-$(Platform)</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "MarkdownPreviewHandler.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;
MarkdownPreviewHandler* pExt = new (std::nothrow) MarkdownPreviewHandler();
if (pExt)
{
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement(&g_cDllRef);
}
else
{
InterlockedDecrement(&g_cDllRef);
}
return S_OK;
}

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,263 @@
#include "pch.h"
#include "MarkdownPreviewHandler.h"
#include "Generated Files/resource.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>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
MarkdownPreviewHandler::MarkdownPreviewHandler() :
m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL)
{
m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MARKDOWN_PREVIEW_RESIZE_EVENT);
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::mdPrevLogPath);
Logger::init(LogSettings::mdPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
MarkdownPreviewHandler::~MarkdownPreviewHandler()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP MarkdownPreviewHandler::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(MarkdownPreviewHandler, IPreviewHandler),
QITABENT(MarkdownPreviewHandler, IInitializeWithFile),
QITABENT(MarkdownPreviewHandler, IPreviewHandlerVisuals),
QITABENT(MarkdownPreviewHandler, IOleWindow),
QITABENT(MarkdownPreviewHandler, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
MarkdownPreviewHandler::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
MarkdownPreviewHandler::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithFile
IFACEMETHODIMP MarkdownPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode)
{
m_filePath = pszFilePath;
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandler
IFACEMETHODIMP MarkdownPreviewHandler::SetWindow(HWND hwnd, const RECT* prc)
{
if (hwnd && prc)
{
m_hwndParent = hwnd;
m_rcParent = *prc;
}
return S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::SetFocus()
{
return S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::QueryFocus(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = ::GetFocus();
if (*phwnd)
{
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
IFACEMETHODIMP MarkdownPreviewHandler::TranslateAccelerator(MSG* pmsg)
{
HRESULT hr = S_FALSE;
IPreviewHandlerFrame* pFrame = NULL;
if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame)))
{
hr = pFrame->TranslateAccelerator(pmsg);
pFrame->Release();
}
return hr;
}
IFACEMETHODIMP MarkdownPreviewHandler::SetRect(const RECT* prc)
{
HRESULT hr = E_INVALIDARG;
if (prc != NULL)
{
if (!m_resizeEvent)
{
Logger::error(L"Failed to create resize event for MDPreviewHandler");
}
else
{
if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom)
{
if (!SetEvent(m_resizeEvent))
{
Logger::error(L"Failed to signal resize event for MDPreviewHandler");
}
}
}
m_rcParent = *prc;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP MarkdownPreviewHandler::DoPreview()
{
try
{
Logger::info(L"Starting MarkdownPreviewHandler.exe");
STARTUPINFO info = { sizeof(info) };
std::wstring cmdLine{ L"\"" + m_filePath + L"\"" };
cmdLine += L" ";
std::wostringstream ss;
ss << std::hex << m_hwndParent;
cmdLine += ss.str();
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.left);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.right);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.top);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.bottom);
std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.MarkdownPreviewHandler.exe";
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = appPath.c_str();
sei.lpParameters = cmdLine.c_str();
sei.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&sei);
m_process = sei.hProcess;
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start MarkdownPreviewHandler.exe. Error: {}", errorMessage);
}
return S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::Unload()
{
Logger::info(L"Unload and terminate .exe");
TerminateProcess(m_process, 0);
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandlerVisuals
IFACEMETHODIMP MarkdownPreviewHandler::SetBackgroundColor(COLORREF color)
{
return S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::SetFont(const LOGFONTW* plf)
{
return S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::SetTextColor(COLORREF color)
{
return S_OK;
}
#pragma endregion
#pragma region IOleWindow
IFACEMETHODIMP MarkdownPreviewHandler::GetWindow(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = m_hwndParent;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP MarkdownPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP MarkdownPreviewHandler::SetSite(IUnknown* punkSite)
{
if (m_punkSite)
{
m_punkSite->Release();
m_punkSite = NULL;
}
return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK;
}
IFACEMETHODIMP MarkdownPreviewHandler::GetSite(REFIID riid, void** ppv)
{
*ppv = NULL;
return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,69 @@
#pragma once
#include "pch.h"
#include <filesystem>
#include <ShlObj.h>
#include <string>
class MarkdownPreviewHandler :
public IInitializeWithFile,
public IPreviewHandler,
public IPreviewHandlerVisuals,
public IOleWindow,
public IObjectWithSite
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithFile
IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode);
// IPreviewHandler
IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc);
IFACEMETHODIMP SetFocus();
IFACEMETHODIMP QueryFocus(HWND* phwnd);
IFACEMETHODIMP TranslateAccelerator(MSG* pmsg);
IFACEMETHODIMP SetRect(const RECT* prc);
IFACEMETHODIMP DoPreview();
IFACEMETHODIMP Unload();
// IPreviewHandlerVisuals
IFACEMETHODIMP SetBackgroundColor(COLORREF color);
IFACEMETHODIMP SetFont(const LOGFONTW* plf);
IFACEMETHODIMP SetTextColor(COLORREF color);
// IOleWindow
IFACEMETHODIMP GetWindow(HWND* phwnd);
IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* punkSite);
IFACEMETHODIMP GetSite(REFIID riid, void** ppv);
MarkdownPreviewHandler();
protected:
~MarkdownPreviewHandler();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
std::wstring m_filePath;
// Parent window that hosts the previewer window.
// Note: do NOT DestroyWindow this.
HWND m_hwndParent;
// Bounding rect of the parent window.
RECT m_rcParent;
// Site pointer from host, used to get IPreviewHandlerFrame.
IUnknown* m_punkSite;
HANDLE m_process;
HANDLE m_resizeEvent;
};

View File

@@ -0,0 +1,128 @@
<?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.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h markdownpreviewhandler.base.rc markdownpreviewhandler.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ed9a1ac6-aeb0-4569-a6e9-e1696182b545}</ProjectGuid>
<RootNamespace>MarkdownPreviewHandlerCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</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>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</OutDir>
<CopyCppRuntimeToOutputDir>true</CopyCppRuntimeToOutputDir>
</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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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="MarkdownPreviewHandler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="MarkdownPreviewHandler.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/markdownpreviewhandler.rc" />
<None Include="markdownpreviewhandler.base.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>
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,65 @@
<?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="MarkdownPreviewHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header 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="MarkdownPreviewHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def">
<Filter>Source Files</Filter>
</None>
<None Include="packages.config" />
<None Include="markdownpreviewhandler.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="Resources.resx">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/markdownpreviewhandler.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,113 @@
<?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">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized CLR Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the CLR Framework object]
</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 CLR 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.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: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" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" 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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<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>
</data>
</root>

View File

@@ -0,0 +1,74 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "ClassFactory.h"
HINSTANCE g_hInst = NULL;
long g_cDllRef = 0;
// {60789D87-9C3C-44AF-B18C-3DE2C2820ED3}
static const GUID CLSID_MarkdownPreviewHandler = { 0x60789d87, 0x9c3c, 0x44af, { 0xb1, 0x8c, 0x3d, 0xe2, 0xc2, 0x82, 0xe, 0xd3 } };
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// FUNCTION: DllGetClassObject
//
// PURPOSE: Create the class factory and query to the specific interface.
//
// PARAMETERS:
// * rclsid - The CLSID that will associate the correct data and code.
// * riid - A reference to the identifier of the interface that the caller
// is to use to communicate with the class object.
// * ppv - The address of a pointer variable that receives the interface
// pointer requested in riid. Upon successful return, *ppv contains the
// requested interface pointer. If an error occurs, the interface pointer
// is NULL.
//
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
if (IsEqualCLSID(CLSID_MarkdownPreviewHandler, rclsid))
{
hr = E_OUTOFMEMORY;
ClassFactory* pClassFactory = new ClassFactory();
if (pClassFactory)
{
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
}
return hr;
}
//
// FUNCTION: DllCanUnloadNow
//
// PURPOSE: Check if we can unload the component from the memory.
//
// NOTE: The component can be unloaded from the memory when its reference
// count is zero (i.e. nobody is still using the component).
//
STDAPI DllCanUnloadNow(void)
{
return g_cDllRef > 0 ? S_FALSE : S_OK;
}

View File

@@ -0,0 +1,46 @@
#include <windows.h>
#include "resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" 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 powerpreview.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys Markdown Preview Handler"
#define INTERNAL_NAME "PowerToys.MarkdownPreviewHandler"
#define ORIGINAL_FILENAME "PowerToys.MarkdownPreviewHandlerCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by markdownpreviewhandler.base.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -1,72 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.PreviewHandler.Monaco
{
using System;
using System.Runtime.InteropServices;
using Common;
/// <summary>
/// Implementation of preview handler for files with source code.
/// </summary>
[Guid("afbd5a44-2520-4ae0-9224-6cfce8fe4400")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class MonacoPreviewHandler : FileBasedPreviewHandler, IDisposable
{
private MonacoPreviewHandlerControl _monacoPreviewHandlerControl;
private bool _disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="MonacoPreviewHandler"/> class.
/// </summary>
public MonacoPreviewHandler()
{
this.Initialize();
}
/// <inheritdoc />
[STAThread]
public override void DoPreview()
{
_monacoPreviewHandlerControl.DoPreview(FilePath);
}
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
_monacoPreviewHandlerControl = new MonacoPreviewHandlerControl();
return _monacoPreviewHandlerControl;
}
/// <summary>
/// Disposes objects
/// </summary>
/// <param name="disposing">Is Disposing</param>
[STAThread]
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_monacoPreviewHandlerControl.Dispose();
}
_disposedValue = true;
this.Unload();
}
}
/// <inheritdoc />
[STAThread]
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,5 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyTitle>PowerToys.MonacoPreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys MonacoPreviewHandler</AssemblyDescription>
<Description>PowerToys MonacoPreviewHandler</Description>
@@ -9,14 +11,22 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Monaco</RootNamespace>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<EnableComHosting>true</EnableComHosting>
<AssemblyName>PowerToys.MonacoPreviewHandler</AssemblyName>
</PropertyGroup>
@@ -35,6 +45,7 @@
<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>

View File

@@ -107,13 +107,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMonacoPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility. Show an error message instead.
InvokeOnControlThread(() =>
{
_infoBarAdded = true;
AddTextBoxControl(Properties.Resources.GpoDisabledErrorText);
Resize += FormResized;
base.DoPreview(dataSource);
});
_infoBarAdded = true;
AddTextBoxControl(Properties.Resources.GpoDisabledErrorText);
Resize += FormResized;
base.DoPreview(dataSource);
return;
}
@@ -132,7 +129,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
// Checks if dataSource is a string
if (!(dataSource is string filePath))
{
throw new ArgumentException($"{nameof(dataSource)} for {nameof(MonacoPreviewHandler)} must be a string but was a '{typeof(T)}'");
throw new ArgumentException($"{nameof(dataSource)} for {nameof(MonacoPreviewHandlerControl)} must be a string but was a '{typeof(T)}'");
}
// Check if the file is too big.
@@ -145,103 +142,94 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
try
{
InvokeOnControlThread(() =>
Logger.LogInfo("Create WebView2 environment");
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\MonacoPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(async () =>
{
Logger.LogInfo("Create WebView2 environment");
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\MonacoPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
_loadingBar.Value = 60;
this.Update();
try
{
_loadingBar.Value = 60;
this.Update();
InvokeOnControlThread(async () =>
if (CoreWebView2Environment.GetAvailableBrowserVersionString() == null)
{
try
{
if (CoreWebView2Environment.GetAvailableBrowserVersionString() == null)
{
throw new WebView2RuntimeNotFoundException();
}
throw new WebView2RuntimeNotFoundException();
}
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
_loadingBar.Value = 70;
this.Update();
_loadingBar.Value = 70;
this.Update();
// Initialize WebView
try
{
await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
// Initialize WebView
try
{
await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
// Wait until html is loaded
initializeIndexFileAndSelectedFileTask.Wait();
// Wait until html is loaded
initializeIndexFileAndSelectedFileTask.Wait();
_webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
_webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
Logger.LogInfo("Navigates to string of HTML file");
Logger.LogInfo("Navigates to string of HTML file");
_webView.NavigateToString(_html);
_webView.NavigationCompleted += WebView2Init;
_webView.Height = this.Height;
_webView.Width = this.Width;
Controls.Add(_webView);
_webView.SendToBack();
_loadingBar.Value = 100;
this.Update();
}
catch (NullReferenceException e)
{
Logger.LogError("NullReferenceException catched. Skipping exception.", e);
}
}
catch (WebView2RuntimeNotFoundException e)
{
Logger.LogWarning("WebView2 was not found:");
Logger.LogWarning(e.Message);
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
_webView.NavigateToString(_html);
_webView.NavigationCompleted += WebView2Init;
_webView.Height = this.Height;
_webView.Width = this.Width;
Controls.Add(_webView);
_webView.SendToBack();
_loadingBar.Value = 100;
this.Update();
}
catch (NullReferenceException e)
{
Logger.LogError("NullReferenceException catched. Skipping exception.", e);
}
}
catch (WebView2RuntimeNotFoundException e)
{
Logger.LogWarning("WebView2 was not found:");
Logger.LogWarning(e.Message);
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
// WebView2 not installed message
Label errorMessage = new Label();
errorMessage.Text = Resources.WebView2_Not_Installed_Message;
errorMessage.Width = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Width + 10;
errorMessage.Height = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height;
Controls.Add(errorMessage);
// WebView2 not installed message
Label errorMessage = new Label();
errorMessage.Text = Resources.WebView2_Not_Installed_Message;
errorMessage.Width = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Width + 10;
errorMessage.Height = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height;
Controls.Add(errorMessage);
// Download Link
Label downloadLink = new LinkLabel();
downloadLink.Text = Resources.Download_WebView2;
downloadLink.Click += DownloadLink_Click;
downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10;
downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10;
downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height;
Controls.Add(downloadLink);
}
});
});
// Download Link
Label downloadLink = new LinkLabel();
downloadLink.Text = Resources.Download_WebView2;
downloadLink.Click += DownloadLink_Click;
downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10;
downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10;
downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height;
Controls.Add(downloadLink);
}
});
}
catch (Exception e)
{
InvokeOnControlThread(() =>
{
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label text = new Label();
text.Text = Resources.Exception_Occurred;
text.Text += e.Message;
text.Text += "\n" + e.Source;
text.Text += "\n" + e.StackTrace;
text.Width = 500;
text.Height = 10000;
Controls.Add(text);
Logger.LogError(e.Message);
});
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label text = new Label();
text.Text = Resources.Exception_Occurred;
text.Text += e.Message;
text.Text += "\n" + e.Source;
text.Text += "\n" + e.StackTrace;
text.Width = 500;
text.Height = 10000;
Controls.Add(text);
Logger.LogError(e.Message);
}
this.Resize += FormResize;
@@ -249,18 +237,16 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
else
{
Logger.LogInfo("File is too big to display. Showing error message");
InvokeOnControlThread(() =>
{
Controls.Remove(_loading);
_loadingBar.Dispose();
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label errorMessage = new Label();
errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture);
errorMessage.Width = 500;
errorMessage.Height = 50;
Controls.Add(errorMessage);
});
Controls.Remove(_loading);
_loadingBar.Dispose();
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label errorMessage = new Label();
errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture);
errorMessage.Width = 500;
errorMessage.Height = 50;
Controls.Add(errorMessage);
}
}
@@ -350,46 +336,41 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
private void SetBackground()
{
Logger.LogTrace();
InvokeOnControlThread(() =>
{
this.BackColor = Settings.BackgroundColor;
});
this.BackColor = Settings.BackgroundColor;
}
private void InitializeLoadingScreen()
{
Logger.LogTrace();
InvokeOnControlThread(() =>
{
_loadingBackground = new Label();
_loadingBackground.BackColor = Settings.BackgroundColor;
_loadingBackground.Width = this.Width;
_loadingBackground.Height = this.Height;
Controls.Add(_loadingBackground);
_loadingBackground.BringToFront();
_loadingBackground = new Label();
_loadingBackground.BackColor = Settings.BackgroundColor;
_loadingBackground.Width = this.Width;
_loadingBackground.Height = this.Height;
Controls.Add(_loadingBackground);
_loadingBackground.BringToFront();
_loadingBar = new ProgressBar();
_loadingBar.Width = this.Width - 10;
_loadingBar.Location = new Point(5, this.Height / 2);
_loadingBar.Maximum = 100;
_loadingBar.Value = 10;
Controls.Add(_loadingBar);
_loadingBar = new ProgressBar();
_loadingBar.Width = this.Width - 10;
_loadingBar.Location = new Point(5, this.Height / 2);
_loadingBar.Maximum = 100;
_loadingBar.Value = 10;
Controls.Add(_loadingBar);
_loading = new Label();
_loading.Text = Resources.Loading_Screen_Message;
_loading.Width = this.Width;
_loading.Height = 45;
_loading.Location = new Point(0, _loadingBar.Location.Y - _loading.Height);
_loading.TextAlign = ContentAlignment.TopCenter;
_loading.Font = new Font("MS Sans Serif", 16, FontStyle.Bold);
_loading.ForeColor = Settings.TextColor;
Controls.Add(_loading);
_loading = new Label();
_loading.Text = Resources.Loading_Screen_Message;
_loading.Width = this.Width;
_loading.Height = 45;
_loading.Location = new Point(0, _loadingBar.Location.Y - _loading.Height);
_loading.TextAlign = ContentAlignment.TopCenter;
_loading.Font = new Font("MS Sans Serif", 16, FontStyle.Bold);
_loading.ForeColor = Settings.TextColor;
Controls.Add(_loading);
_loading.BringToFront();
_loadingBar.BringToFront();
_loading.BringToFront();
_loadingBar.BringToFront();
this.Update();
this.Update();
});
Logger.LogInfo("Loading screen initialized");
}

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.PreviewHandler.Monaco
{
using System.Globalization;
using System.Windows.Threading;
using Common.UI;
using interop;
internal static class Program
{
private static CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static MonacoPreviewHandlerControl _previewHandlerControl;
/// <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)
{
string filePath = args[0];
int hwnd = Convert.ToInt32(args[1], 16);
Rectangle s = default(Rectangle);
int left = Convert.ToInt32(args[2], 10);
int right = Convert.ToInt32(args[3], 10);
int top = Convert.ToInt32(args[4], 10);
int bottom = Convert.ToInt32(args[5], 10);
_previewHandlerControl = new MonacoPreviewHandlerControl();
_previewHandlerControl.SetWindow((IntPtr)hwnd, s);
_previewHandlerControl.DoPreview(filePath);
NativeEventWaiter.WaitForEventLoop(
Constants.DevFilesPreviewResizeEvent(),
() =>
{
Rectangle s = default(Rectangle);
_previewHandlerControl.SetRect(s);
},
Dispatcher.CurrentDispatcher,
_tokenSource.Token);
}
else
{
MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
Application.Run();
}
}
}

View File

@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\modules\FileExplorerPreview</PublishDir>
<RuntimeIdentifier>win10-$(Platform)</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "MonacoPreviewHandler.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;
MonacoPreviewHandler* pExt = new (std::nothrow) MonacoPreviewHandler();
if (pExt)
{
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement(&g_cDllRef);
}
else
{
InterlockedDecrement(&g_cDllRef);
}
return S_OK;
}

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,262 @@
#include "pch.h"
#include "MonacoPreviewHandler.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>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
MonacoPreviewHandler::MonacoPreviewHandler() :
m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL)
{
m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::DEV_FILES_PREVIEW_RESIZE_EVENT);
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::monacoPrevLogPath);
Logger::init(LogSettings::monacoPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
MonacoPreviewHandler::~MonacoPreviewHandler()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP MonacoPreviewHandler::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(MonacoPreviewHandler, IPreviewHandler),
QITABENT(MonacoPreviewHandler, IInitializeWithFile),
QITABENT(MonacoPreviewHandler, IPreviewHandlerVisuals),
QITABENT(MonacoPreviewHandler, IOleWindow),
QITABENT(MonacoPreviewHandler, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
MonacoPreviewHandler::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
MonacoPreviewHandler::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithFile
IFACEMETHODIMP MonacoPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode)
{
m_filePath = pszFilePath;
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandler
IFACEMETHODIMP MonacoPreviewHandler::SetWindow(HWND hwnd, const RECT* prc)
{
if (hwnd && prc)
{
m_hwndParent = hwnd;
m_rcParent = *prc;
}
return S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::SetFocus()
{
return S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::QueryFocus(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = ::GetFocus();
if (*phwnd)
{
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
IFACEMETHODIMP MonacoPreviewHandler::TranslateAccelerator(MSG* pmsg)
{
HRESULT hr = S_FALSE;
IPreviewHandlerFrame* pFrame = NULL;
if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame)))
{
hr = pFrame->TranslateAccelerator(pmsg);
pFrame->Release();
}
return hr;
}
IFACEMETHODIMP MonacoPreviewHandler::SetRect(const RECT* prc)
{
HRESULT hr = E_INVALIDARG;
if (prc != NULL)
{
if (!m_resizeEvent)
{
Logger::error(L"Failed to create resize event for MonacoPreviewHandler");
}
else
{
if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom)
{
if (!SetEvent(m_resizeEvent))
{
Logger::error(L"Failed to signal resize event for MonacoPreviewHandler");
}
}
}
m_rcParent = *prc;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP MonacoPreviewHandler::DoPreview()
{
try
{
Logger::info(L"Starting MonacoPreviewHandler.exe");
STARTUPINFO info = { sizeof(info) };
std::wstring cmdLine{ L"\"" + m_filePath + L"\"" };
cmdLine += L" ";
std::wostringstream ss;
ss << std::hex << m_hwndParent;
cmdLine += ss.str();
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.left);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.right);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.top);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.bottom);
std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.MonacoPreviewHandler.exe";
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = appPath.c_str();
sei.lpParameters = cmdLine.c_str();
sei.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&sei);
m_process = sei.hProcess;
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start MonacoPreviewHandler.exe. Error: {}", errorMessage);
}
return S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::Unload()
{
Logger::info(L"Unload and terminate .exe");
m_hwndParent = NULL;
TerminateProcess(m_process, 0);
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandlerVisuals
IFACEMETHODIMP MonacoPreviewHandler::SetBackgroundColor(COLORREF color)
{
return S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::SetFont(const LOGFONTW* plf)
{
return S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::SetTextColor(COLORREF color)
{
return S_OK;
}
#pragma endregion
#pragma region IOleWindow
IFACEMETHODIMP MonacoPreviewHandler::GetWindow(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = m_hwndParent;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP MonacoPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP MonacoPreviewHandler::SetSite(IUnknown* punkSite)
{
if (m_punkSite)
{
m_punkSite->Release();
m_punkSite = NULL;
}
return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK;
}
IFACEMETHODIMP MonacoPreviewHandler::GetSite(REFIID riid, void** ppv)
{
*ppv = NULL;
return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,70 @@
#pragma once
#include "pch.h"
#include <filesystem>
#include <ShlObj.h>
#include <string>
class MonacoPreviewHandler :
public IInitializeWithFile,
public IPreviewHandler,
public IPreviewHandlerVisuals,
public IOleWindow,
public IObjectWithSite
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithFile
IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode);
// IPreviewHandler
IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc);
IFACEMETHODIMP SetFocus();
IFACEMETHODIMP QueryFocus(HWND* phwnd);
IFACEMETHODIMP TranslateAccelerator(MSG* pmsg);
IFACEMETHODIMP SetRect(const RECT* prc);
IFACEMETHODIMP DoPreview();
IFACEMETHODIMP Unload();
// IPreviewHandlerVisuals
IFACEMETHODIMP SetBackgroundColor(COLORREF color);
IFACEMETHODIMP SetFont(const LOGFONTW* plf);
IFACEMETHODIMP SetTextColor(COLORREF color);
// IOleWindow
IFACEMETHODIMP GetWindow(HWND* phwnd);
IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* punkSite);
IFACEMETHODIMP GetSite(REFIID riid, void** ppv);
MonacoPreviewHandler();
protected:
~MonacoPreviewHandler();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
std::wstring m_filePath;
// Parent window that hosts the previewer window.
// Note: do NOT DestroyWindow this.
HWND m_hwndParent;
// Bounding rect of the parent window.
RECT m_rcParent;
// Site pointer from host, used to get IPreviewHandlerFrame.
IUnknown* m_punkSite;
HANDLE m_process;
HANDLE m_resizeEvent;
};

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.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{b3e869c4-8210-4ebd-a621-ff4c4afcbfa9}</ProjectGuid>
<RootNamespace>MonacoPreviewHandlerCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</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>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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="MonacoPreviewHandler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="MonacoPreviewHandler.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="MonacoPreviewHandlerCpp.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>
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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="MonacoPreviewHandler.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="MonacoPreviewHandler.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="MonacoPreviewHandlerCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

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;
// {D8034CFA-F34B-41FE-AD45-62FCBB52A6DA}
static const GUID CLSID_MonacoPreviewHandler = { 0xd8034cfa, 0xf34b, 0x41fe, { 0xad, 0x45, 0x62, 0xfc, 0xbb, 0x52, 0xa6, 0xda } };
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// FUNCTION: DllGetClassObject
//
// PURPOSE: Create the class factory and query to the specific interface.
//
// PARAMETERS:
// * rclsid - The CLSID that will associate the correct data and code.
// * riid - A reference to the identifier of the interface that the caller
// is to use to communicate with the class object.
// * ppv - The address of a pointer variable that receives the interface
// pointer requested in riid. Upon successful return, *ppv contains the
// requested interface pointer. If an error occurs, the interface pointer
// is NULL.
//
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
if (IsEqualCLSID(CLSID_MonacoPreviewHandler, rclsid))
{
hr = E_OUTOFMEMORY;
ClassFactory* pClassFactory = new ClassFactory();
if (pClassFactory)
{
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
}
return hr;
}
//
// FUNCTION: DllCanUnloadNow
//
// PURPOSE: Check if we can unload the component from the memory.
//
// NOTE: The component can be unloaded from the memory when its reference
// count is zero (i.e. nobody is still using the component).
//
STDAPI DllCanUnloadNow(void)
{
return g_cDllRef > 0 ? S_FALSE : S_OK;
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" 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 Dev Files Preview Handler Module"
#define INTERNAL_NAME "PowerToys.MonacoPreviewHandlerCpp"
#define ORIGINAL_FILENAME "PowerToys.MonacoPreviewHandlerCpp.dll"
// Non-localizable
//////////////////////////////

View File

@@ -1,71 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Common;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.PreviewHandler.Pdf
{
/// <summary>
/// Implementation of preview handler for pdf files.
/// </summary>
[Guid("07665729-6243-4746-95b7-79579308d1b2")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class PdfPreviewHandler : StreamBasedPreviewHandler, IDisposable
{
private PdfPreviewHandlerControl _pdfPreviewHandlerControl;
private bool _disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="PdfPreviewHandler"/> class.
/// </summary>
public PdfPreviewHandler()
{
Initialize();
}
/// <inheritdoc />
public override void DoPreview()
{
_pdfPreviewHandlerControl.DoPreview(Stream);
}
/// <inheritdoc />
protected override IPreviewHandlerControl CreatePreviewHandlerControl()
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.Events.PdfFileHandlerLoaded());
_pdfPreviewHandlerControl = new PdfPreviewHandlerControl();
return _pdfPreviewHandlerControl;
}
/// <summary>
/// Disposes objects
/// </summary>
/// <param name="disposing">Is Disposing</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_pdfPreviewHandlerControl.Dispose();
}
_disposedValue = true;
}
}
/// <inheritdoc />
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,5 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyTitle>PowerToys.PdfPreviewHandler</AssemblyTitle>
<AssemblyDescription>PowerToys PdfPreviewHandler</AssemblyDescription>
@@ -10,13 +12,21 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<SelfContained>true</SelfContained>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{69E1EE8D-143A-4060-9129-4658ACF14AAF}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Pdf</RootNamespace>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<EnableComHosting>true</EnableComHosting>
<AssemblyName>PowerToys.PdfPreviewHandler</AssemblyName>
</PropertyGroup>
@@ -40,6 +50,7 @@
<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>
@@ -48,7 +59,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.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>

View File

@@ -55,12 +55,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf
if (global::PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPdfPreviewEnabledValue() == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
// GPO is disabling this utility. Show an error message instead.
InvokeOnControlThread(() =>
{
_infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText);
Controls.Add(_infoBar);
base.DoPreview(dataSource);
});
_infoBar = GetTextBoxControl(Resources.GpoDisabledErrorText);
Controls.Add(_infoBar);
base.DoPreview(dataSource);
return;
}
@@ -69,7 +66,12 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf
try
{
using (var dataStream = new ReadonlyStream(dataSource as IStream))
if (!(dataSource is string filePath))
{
throw new ArgumentException($"{nameof(dataSource)} for {nameof(PdfPreviewHandlerControl)} must be a string but was a '{typeof(T)}'");
}
using (var dataStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var memStream = new MemoryStream();
dataStream.CopyTo(memStream);
@@ -82,77 +84,71 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf
if (pdf.PageCount > 0)
{
InvokeOnControlThread(() =>
_flowLayoutPanel = new FlowLayoutPanel
{
_flowLayoutPanel = new FlowLayoutPanel
{
AutoScroll = true,
AutoSize = true,
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
};
_flowLayoutPanel.Resize += FlowLayoutPanel_Resize;
AutoScroll = true,
AutoSize = true,
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
};
_flowLayoutPanel.Resize += FlowLayoutPanel_Resize;
// Only show first 10 pages.
for (uint i = 0; i < pdf.PageCount && i < 10; i++)
// Only show first 10 pages.
for (uint i = 0; i < pdf.PageCount && i < 10; i++)
{
using (var page = pdf.GetPage(i))
{
using (var page = pdf.GetPage(i))
var image = PageToImage(page);
var picturePanel = new Panel()
{
var image = PageToImage(page);
var picturePanel = new Panel()
{
Name = "picturePanel",
Margin = new Padding(6, 6, 6, 0),
Size = CalculateSize(image),
BorderStyle = BorderStyle.FixedSingle,
};
var picture = new PictureBox
{
Dock = DockStyle.Fill,
Image = image,
SizeMode = PictureBoxSizeMode.Zoom,
};
picturePanel.Controls.Add(picture);
_flowLayoutPanel.Controls.Add(picturePanel);
}
}
if (pdf.PageCount > 10)
{
var messageBox = new RichTextBox
{
Name = "messageBox",
Text = Resources.PdfMorePagesMessage,
BackColor = Color.LightYellow,
Dock = DockStyle.Fill,
Multiline = true,
ReadOnly = true,
ScrollBars = RichTextBoxScrollBars.None,
BorderStyle = BorderStyle.None,
Name = "picturePanel",
Margin = new Padding(6, 6, 6, 0),
Size = CalculateSize(image),
BorderStyle = BorderStyle.FixedSingle,
};
messageBox.ContentsResized += RTBContentsResized;
_flowLayoutPanel.Controls.Add(messageBox);
var picture = new PictureBox
{
Dock = DockStyle.Fill,
Image = image,
SizeMode = PictureBoxSizeMode.Zoom,
};
picturePanel.Controls.Add(picture);
_flowLayoutPanel.Controls.Add(picturePanel);
}
}
Controls.Add(_flowLayoutPanel);
});
if (pdf.PageCount > 10)
{
var messageBox = new RichTextBox
{
Name = "messageBox",
Text = Resources.PdfMorePagesMessage,
BackColor = Color.LightYellow,
Dock = DockStyle.Fill,
Multiline = true,
ReadOnly = true,
ScrollBars = RichTextBoxScrollBars.None,
BorderStyle = BorderStyle.None,
};
messageBox.ContentsResized += RTBContentsResized;
_flowLayoutPanel.Controls.Add(messageBox);
}
Controls.Add(_flowLayoutPanel);
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Unable to update the password. The value provided as the current password is incorrect.", StringComparison.Ordinal))
{
InvokeOnControlThread(() =>
{
Controls.Clear();
_infoBar = GetTextBoxControl(Resources.PdfPasswordProtectedError);
Controls.Add(_infoBar);
});
Controls.Clear();
_infoBar = GetTextBoxControl(Resources.PdfPasswordProtectedError);
Controls.Add(_infoBar);
}
else
{
@@ -171,12 +167,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf
{
PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewError { Message = ex.Message });
InvokeOnControlThread(() =>
{
Controls.Clear();
_infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError);
Controls.Add(_infoBar);
});
Controls.Clear();
_infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError);
Controls.Add(_infoBar);
}
finally
{

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.PreviewHandler.Pdf
{
using System.Globalization;
using System.Windows.Threading;
using Common.UI;
using interop;
internal static class Program
{
private static CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static PdfPreviewHandlerControl _previewHandlerControl;
/// <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)
{
string filePath = args[0];
int hwnd = Convert.ToInt32(args[1], 16);
Rectangle s = default(Rectangle);
int left = Convert.ToInt32(args[2], 10);
int right = Convert.ToInt32(args[3], 10);
int top = Convert.ToInt32(args[4], 10);
int bottom = Convert.ToInt32(args[5], 10);
_previewHandlerControl = new PdfPreviewHandlerControl();
_previewHandlerControl.SetWindow((IntPtr)hwnd, s);
_previewHandlerControl.DoPreview(filePath);
NativeEventWaiter.WaitForEventLoop(
Constants.PdfPreviewResizeEvent(),
() =>
{
Rectangle s = default(Rectangle);
_previewHandlerControl.SetRect(s);
},
Dispatcher.CurrentDispatcher,
_tokenSource.Token);
}
else
{
MessageBox.Show("Wrong number of args: " + args.Length.ToString(CultureInfo.InvariantCulture));
}
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
Application.Run();
}
}
}

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "ClassFactory.h"
#include "PdfPreviewHandler.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;
PdfPreviewHandler* pExt = new (std::nothrow) PdfPreviewHandler();
if (pExt)
{
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
return hr;
}
IFACEMETHODIMP ClassFactory::LockServer(BOOL fLock)
{
if (fLock)
{
InterlockedIncrement(&g_cDllRef);
}
else
{
InterlockedDecrement(&g_cDllRef);
}
return S_OK;
}

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,261 @@
#include "pch.h"
#include "PdfPreviewHandler.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>
extern HINSTANCE g_hInst;
extern long g_cDllRef;
PdfPreviewHandler::PdfPreviewHandler() :
m_cRef(1), m_hwndParent(NULL), m_rcParent(), m_punkSite(NULL), m_process(NULL)
{
m_resizeEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::PDF_PREVIEW_RESIZE_EVENT);
std::filesystem::path logFilePath(PTSettingsHelper::get_local_low_folder_location());
logFilePath.append(LogSettings::pdfPrevLogPath);
Logger::init(LogSettings::pdfPrevLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
InterlockedIncrement(&g_cDllRef);
}
PdfPreviewHandler::~PdfPreviewHandler()
{
InterlockedDecrement(&g_cDllRef);
}
#pragma region IUnknown
IFACEMETHODIMP PdfPreviewHandler::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(PdfPreviewHandler, IPreviewHandler),
QITABENT(PdfPreviewHandler, IInitializeWithFile),
QITABENT(PdfPreviewHandler, IPreviewHandlerVisuals),
QITABENT(PdfPreviewHandler, IOleWindow),
QITABENT(PdfPreviewHandler, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG)
PdfPreviewHandler::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG)
PdfPreviewHandler::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IInitializationWithFile
IFACEMETHODIMP PdfPreviewHandler::Initialize(LPCWSTR pszFilePath, DWORD grfMode)
{
m_filePath = pszFilePath;
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandler
IFACEMETHODIMP PdfPreviewHandler::SetWindow(HWND hwnd, const RECT* prc)
{
if (hwnd && prc)
{
m_hwndParent = hwnd;
m_rcParent = *prc;
}
return S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::SetFocus()
{
return S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::QueryFocus(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = ::GetFocus();
if (*phwnd)
{
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
IFACEMETHODIMP PdfPreviewHandler::TranslateAccelerator(MSG* pmsg)
{
HRESULT hr = S_FALSE;
IPreviewHandlerFrame* pFrame = NULL;
if (m_punkSite && SUCCEEDED(m_punkSite->QueryInterface(&pFrame)))
{
hr = pFrame->TranslateAccelerator(pmsg);
pFrame->Release();
}
return hr;
}
IFACEMETHODIMP PdfPreviewHandler::SetRect(const RECT* prc)
{
HRESULT hr = E_INVALIDARG;
if (prc != NULL)
{
if (!m_resizeEvent)
{
Logger::error(L"Failed to create resize event for PdfPreviewHandler");
}
else
{
if (m_rcParent.right != prc->right || m_rcParent.left != prc->left || m_rcParent.top != prc->top || m_rcParent.bottom != prc->bottom)
{
if (!SetEvent(m_resizeEvent))
{
Logger::error(L"Failed to signal resize event for PdfPreviewHandler");
}
}
}
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP PdfPreviewHandler::DoPreview()
{
try
{
Logger::info(L"Starting PdfPreviewHandler.exe");
STARTUPINFO info = { sizeof(info) };
std::wstring cmdLine{ L"\"" + m_filePath + L"\"" };
cmdLine += L" ";
std::wostringstream ss;
ss << std::hex << m_hwndParent;
cmdLine += ss.str();
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.left);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.right);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.top);
cmdLine += L" ";
cmdLine += std::to_wstring(m_rcParent.bottom);
std::wstring appPath = get_module_folderpath(g_hInst) + L"\\PowerToys.PdfPreviewHandler.exe";
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = appPath.c_str();
sei.lpParameters = cmdLine.c_str();
sei.nShow = SW_SHOWDEFAULT;
ShellExecuteEx(&sei);
m_process = sei.hProcess;
}
catch (std::exception& e)
{
std::wstring errorMessage = std::wstring{ winrt::to_hstring(e.what()) };
Logger::error(L"Failed to start PdfPreviewHandler.exe. Error: {}", errorMessage);
}
return S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::Unload()
{
Logger::info(L"Unload and terminate .exe");
m_hwndParent = NULL;
TerminateProcess(m_process, 0);
return S_OK;
}
#pragma endregion
#pragma region IPreviewHandlerVisuals
IFACEMETHODIMP PdfPreviewHandler::SetBackgroundColor(COLORREF color)
{
return S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::SetFont(const LOGFONTW* plf)
{
return S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::SetTextColor(COLORREF color)
{
return S_OK;
}
#pragma endregion
#pragma region IOleWindow
IFACEMETHODIMP PdfPreviewHandler::GetWindow(HWND* phwnd)
{
HRESULT hr = E_INVALIDARG;
if (phwnd)
{
*phwnd = m_hwndParent;
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP PdfPreviewHandler::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP PdfPreviewHandler::SetSite(IUnknown* punkSite)
{
if (m_punkSite)
{
m_punkSite->Release();
m_punkSite = NULL;
}
return punkSite ? punkSite->QueryInterface(&m_punkSite) : S_OK;
}
IFACEMETHODIMP PdfPreviewHandler::GetSite(REFIID riid, void** ppv)
{
*ppv = NULL;
return m_punkSite ? m_punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
#pragma endregion
#pragma region Helper Functions
#pragma endregion

View File

@@ -0,0 +1,69 @@
#pragma once
#include "pch.h"
#include <filesystem>
#include <ShlObj.h>
#include <string>
class PdfPreviewHandler :
public IInitializeWithFile,
public IPreviewHandler,
public IPreviewHandlerVisuals,
public IOleWindow,
public IObjectWithSite
{
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv);
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
// IInitializeWithFile
IFACEMETHODIMP Initialize(LPCWSTR pszFilePath, DWORD grfMode);
// IPreviewHandler
IFACEMETHODIMP SetWindow(HWND hwnd, const RECT* prc);
IFACEMETHODIMP SetFocus();
IFACEMETHODIMP QueryFocus(HWND* phwnd);
IFACEMETHODIMP TranslateAccelerator(MSG* pmsg);
IFACEMETHODIMP SetRect(const RECT* prc);
IFACEMETHODIMP DoPreview();
IFACEMETHODIMP Unload();
// IPreviewHandlerVisuals
IFACEMETHODIMP SetBackgroundColor(COLORREF color);
IFACEMETHODIMP SetFont(const LOGFONTW* plf);
IFACEMETHODIMP SetTextColor(COLORREF color);
// IOleWindow
IFACEMETHODIMP GetWindow(HWND* phwnd);
IFACEMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* punkSite);
IFACEMETHODIMP GetSite(REFIID riid, void** ppv);
PdfPreviewHandler();
protected:
~PdfPreviewHandler();
private:
// Reference count of component.
long m_cRef;
// Provided during initialization.
std::wstring m_filePath;
// Parent window that hosts the previewer window.
// Note: do NOT DestroyWindow this.
HWND m_hwndParent;
// Bounding rect of the parent window.
RECT m_rcParent;
// Site pointer from host, used to get IPreviewHandlerFrame.
IUnknown* m_punkSite;
HANDLE m_process;
HANDLE m_resizeEvent;
};

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.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{54f7c616-fd41-4e62-bff9-015686914f4d}</ProjectGuid>
<RootNamespace>PdfPreviewHandlerCpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</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>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\</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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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>
<PrecompiledHeader>Use</PrecompiledHeader>
<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="pch.h" />
<ClInclude Include="PdfPreviewHandler.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ClassFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PdfPreviewHandler.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="GlobalExportFunctions.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PdfPreviewHandlerCpp.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>
</ItemGroup>
<Import Project="..\..\..\..\deps\spdlog.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\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="PdfPreviewHandler.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="PdfPreviewHandler.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="PdfPreviewHandlerCpp.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

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;
// {A5A41CC7-02CB-41D4-8C9B-9087040D6098}
static const GUID CLSID_PdfPreviewHandler = { 0xa5a41cc7, 0x2cb, 0x41d4, { 0x8c, 0x9b, 0x90, 0x87, 0x4, 0xd, 0x60, 0x98 } };
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// FUNCTION: DllGetClassObject
//
// PURPOSE: Create the class factory and query to the specific interface.
//
// PARAMETERS:
// * rclsid - The CLSID that will associate the correct data and code.
// * riid - A reference to the identifier of the interface that the caller
// is to use to communicate with the class object.
// * ppv - The address of a pointer variable that receives the interface
// pointer requested in riid. Upon successful return, *ppv contains the
// requested interface pointer. If an error occurs, the interface pointer
// is NULL.
//
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
if (IsEqualCLSID(CLSID_PdfPreviewHandler, rclsid))
{
hr = E_OUTOFMEMORY;
ClassFactory* pClassFactory = new ClassFactory();
if (pClassFactory)
{
hr = pClassFactory->QueryInterface(riid, ppv);
pClassFactory->Release();
}
}
return hr;
}
//
// FUNCTION: DllCanUnloadNow
//
// PURPOSE: Check if we can unload the component from the memory.
//
// NOTE: The component can be unloaded from the memory when its reference
// count is zero (i.e. nobody is still using the component).
//
STDAPI DllCanUnloadNow(void)
{
return g_cDllRef > 0 ? S_FALSE : S_OK;
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220929.3" 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

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