Add Windows 11 context menu entry "Edit with Notepad++"

This commit implements the Windows 11 context menu using the correct way of having a DLL file and a sparse package.

Fix #13320, close #13330
This commit is contained in:
Bjarke Istrup Pedersen 2023-03-11 11:17:27 +01:00 committed by Don Ho
parent 7561864299
commit ce4d374a47
26 changed files with 880 additions and 0 deletions

View File

@ -7,6 +7,11 @@
name="Notepad++"
type="win32"
/>
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=Notepad++, O=Notepad++, L=Saint Cloud, S=Ile-de-France, C=FR"
packageName="NotepadPlusPlus"
applicationId="NotepadPlusPlus"
/>
<description>Notepad++</description>
<dependency>
<dependentAssembly>

View File

@ -0,0 +1,2 @@
packages
Packaging/NppModernShell.msix

View File

@ -0,0 +1,35 @@
#include "pch.h"
#include "CommandHandlerFactory.h"
#include "EditWithNppExplorerCommandHandler.h"
using namespace NppModernShell::CommandHandlers;
using namespace NppModernShell::Factories;
IFACEMETHODIMP CommandHandlerFactory::CreateInstance(_In_opt_ IUnknown* pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void** ppvObject) noexcept
{
UNREFERENCED_PARAMETER(pUnkOuter);
try
{
return winrt::make<EditWithNppExplorerCommandHandler>()->QueryInterface(riid, ppvObject);
}
catch (...)
{
return winrt::to_hresult();
}
}
IFACEMETHODIMP CommandHandlerFactory::LockServer(_In_ BOOL fLock) noexcept
{
if (fLock)
{
++winrt::get_module_lock();
}
else
{
--winrt::get_module_lock();
}
return S_OK;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "pch.h"
namespace NppModernShell::Factories
{
class __declspec(uuid("4EACAA14-3B43-4595-A44C-FBA8F0848620")) CommandHandlerFactory : public winrt::implements<NppModernShell::Factories::CommandHandlerFactory, IClassFactory>
{
public:
IFACEMETHODIMP CreateInstance(_In_opt_ IUnknown* pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void** ppvObject) noexcept override;
IFACEMETHODIMP LockServer(_In_ BOOL fLock) noexcept override;
};
}

View File

@ -0,0 +1,78 @@
#include "pch.h"
#include "EditWithNppExplorerCommandHandler.h"
#include "Helpers.h"
using namespace NppModernShell::CommandHandlers;
using namespace NppModernShell::Helpers;
const wstring EditWithNppExplorerCommandHandler::GetNppExecutableFullPath()
{
const wstring path = GetInstallationPath();
const wstring fileName = L"\\notepad++.exe";
return L"\"" + path + fileName + L"\"";
}
const wstring EditWithNppExplorerCommandHandler::Title()
{
return L"Edit with Notepad++";
}
const wstring EditWithNppExplorerCommandHandler::Icon()
{
const wstring fileName = GetNppExecutableFullPath();
return fileName;
}
const wstring EditWithNppExplorerCommandHandler::GetCommandLine()
{
const wstring fileName = GetNppExecutableFullPath();
const wstring parameters = L"\"%1\"";
return fileName + L" " + parameters;
}
IFACEMETHODIMP EditWithNppExplorerCommandHandler::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept try
{
if (!selection)
{
return S_OK;
}
DWORD count;
RETURN_IF_FAILED(selection->GetCount(&count));
IShellItem* psi;
LPWSTR itemName;
for (DWORD i = 0; i < count; ++i)
{
selection->GetItemAt(i, &psi);
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &itemName));
std::wstring cmdline = this->GetCommandLine();
cmdline = cmdline.replace(cmdline.find(L"%1"), 2, itemName);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
wchar_t* command = (LPWSTR)cmdline.c_str();
if (!CreateProcess(nullptr, command, nullptr, nullptr, false, CREATE_NEW_PROCESS_GROUP, nullptr, nullptr, &si, &pi))
{
return S_OK;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return S_OK;
}
CATCH_RETURN();

View File

@ -0,0 +1,20 @@
#pragma once
#include "pch.h"
#include "ExplorerCommandBase.h"
namespace NppModernShell::CommandHandlers
{
class EditWithNppExplorerCommandHandler : public ExplorerCommandBase
{
public:
const wstring Title() override;
const wstring Icon() override;
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept override;
private:
const wstring GetNppExecutableFullPath();
const wstring GetCommandLine();
};
}

View File

@ -0,0 +1,68 @@
#include "pch.h"
#include "ExplorerCommandBase.h"
using namespace NppModernShell::CommandHandlers;
const EXPCMDFLAGS ExplorerCommandBase::Flags()
{
return ECF_DEFAULT;
}
const EXPCMDSTATE ExplorerCommandBase::State(_In_opt_ IShellItemArray* selection)
{
UNREFERENCED_PARAMETER(selection);
return ECS_ENABLED;
}
IFACEMETHODIMP ExplorerCommandBase::GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
{
UNREFERENCED_PARAMETER(items);
*name = nullptr;
auto str = wil::make_cotaskmem_string_nothrow(Title().c_str());
RETURN_IF_NULL_ALLOC(str);
*name = str.release();
return S_OK;
}
IFACEMETHODIMP ExplorerCommandBase::GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
{
*icon = nullptr;
auto str = wil::make_cotaskmem_string_nothrow(Icon().c_str());
RETURN_IF_NULL_ALLOC(str);
*icon = str.release();
return S_OK;
}
IFACEMETHODIMP ExplorerCommandBase::GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip)
{
*infoTip = nullptr;
return E_NOTIMPL;
}
IFACEMETHODIMP ExplorerCommandBase::GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState)
{
UNREFERENCED_PARAMETER(okToBeSlow);
*cmdState = State(selection);
return S_OK;
}
IFACEMETHODIMP ExplorerCommandBase::GetFlags(_Out_ EXPCMDFLAGS* flags)
{
*flags = Flags();
return S_OK;
}
IFACEMETHODIMP ExplorerCommandBase::GetCanonicalName(_Out_ GUID* guidCommandName)
{
*guidCommandName = GUID_NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP ExplorerCommandBase::EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands)
{
*enumCommands = nullptr;
return E_NOTIMPL;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "pch.h"
namespace NppModernShell::CommandHandlers
{
class ExplorerCommandBase : public winrt::implements<ExplorerCommandBase, IExplorerCommand>
{
public:
virtual const wstring Title() = 0;
virtual const wstring Icon() = 0;
virtual const EXPCMDFLAGS Flags();
virtual const EXPCMDSTATE State(_In_opt_ IShellItemArray* selection);
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name);
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon);
IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip);
IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState);
IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags);
IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName);
IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands);
virtual IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept = 0;
};
}

View File

@ -0,0 +1,27 @@
#include "pch.h"
#include "Helpers.h"
using namespace NppModernShell::Helpers;
const HMODULE GetThisModule()
{
HMODULE hm = NULL;
BOOL result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)&GetInstallationPath, &hm);
if (result == FALSE)
{
throw "Failed to locate current module, unable to proceed";
}
return hm;
}
const wstring NppModernShell::Helpers::GetInstallationPath()
{
HMODULE thisModule = GetThisModule();
wchar_t path[FILENAME_MAX] = { 0 };
GetModuleFileName(thisModule, path, FILENAME_MAX);
return std::filesystem::path(path).parent_path().wstring();
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "pch.h"
#include <filesystem>
namespace NppModernShell::Helpers
{
const wstring GetInstallationPath();
}

View File

@ -0,0 +1,72 @@
#include "pch.h"
#include "Installer.h"
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Management::Deployment;
using namespace NppModernShell::Helpers;
using namespace NppModernShell::Installer;
const wstring SparsePackageName = L"NotepadPlusPlus";
STDAPI NppModernShell::Installer::RegisterSparsePackage()
{
PackageManager packageManager;
AddPackageOptions options;
const wstring externalLocation = GetInstallationPath();
const wstring sparsePkgPath = externalLocation + L"\\NppModernShell.msix";
Uri externalUri(externalLocation);
Uri packageUri(sparsePkgPath);
options.ExternalLocationUri(externalUri);
auto deploymentOperation = packageManager.AddPackageByUriAsync(packageUri, options);
auto deployResult = deploymentOperation.get();
if (!SUCCEEDED(deployResult.ExtendedErrorCode()))
{
return deployResult.ExtendedErrorCode();
}
return S_OK;
}
STDAPI NppModernShell::Installer::UnregisterSparsePackage()
{
PackageManager packageManager;
IIterable<Package> packages;
try
{
packages = packageManager.FindPackagesForUser(L"");
}
catch (winrt::hresult_error const& ex)
{
return ex.code();
}
for (const Package& package : packages)
{
if (package.Id().Name() != SparsePackageName)
{
continue;
}
winrt::hstring fullName = package.Id().FullName();
auto deploymentOperation = packageManager.RemovePackageAsync(fullName, RemovalOptions::None);
auto deployResult = deploymentOperation.get();
if (!SUCCEEDED(deployResult.ExtendedErrorCode()))
{
return deployResult.ExtendedErrorCode();
}
break;
}
return S_OK;
}

View File

@ -0,0 +1,10 @@
#pragma once
#include "pch.h"
#include "Helpers.h"
namespace NppModernShell::Installer
{
STDAPI RegisterSparsePackage();
STDAPI UnregisterSparsePackage();
}

View File

@ -0,0 +1,39 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NppModernShell", "NppModernShell.vcxproj", "{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packaging", "Packaging", "{B9E2DBFF-8940-482F-9D07-BA9C4EAE7620}"
ProjectSection(SolutionItems) = preProject
Packaging\AppxManifest.xml = Packaging\AppxManifest.xml
Packaging\Square150x150Logo.png = Packaging\Square150x150Logo.png
Packaging\Square44x44Logo.png = Packaging\Square44x44Logo.png
Packaging\StoreLogo.png = Packaging\StoreLogo.png
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Debug|ARM64.Build.0 = Debug|ARM64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Debug|x64.ActiveCfg = Debug|x64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Debug|x64.Build.0 = Debug|x64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Release|ARM64.ActiveCfg = Release|ARM64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Release|ARM64.Build.0 = Release|ARM64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Release|x64.ActiveCfg = Release|x64
{E7539F55-2932-47D0-82B8-46ED5AFCA1C0}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAA0AA08-253D-435A-929E-9214D1E358CB}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{e7539f55-2932-47d0-82b8-46ed5afca1c0}</ProjectGuid>
<RootNamespace>NppModernShell</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" 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" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;NPPMODERNSHELL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>source.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;NPPMODERNSHELL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>source.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;NPPMODERNSHELL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>source.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;NPPMODERNSHELL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>source.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="EditWithNppExplorerCommandHandler.h" />
<ClInclude Include="ExplorerCommandBase.h" />
<ClInclude Include="CommandHandlerFactory.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="Helpers.h" />
<ClInclude Include="Installer.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CommandHandlerFactory.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="EditWithNppExplorerCommandHandler.cpp" />
<ClCompile Include="ExplorerCommandBase.cpp" />
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="Installer.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="source.def" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\Microsoft.Windows.ImplementationLibrary.1.0.230202.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.230202.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.ImplementationLibrary.1.0.230202.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.ImplementationLibrary.1.0.230202.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="CommandHandlers">
<UniqueIdentifier>{ff31b0d8-bf03-4a28-945d-cfae6eb365fa}</UniqueIdentifier>
</Filter>
<Filter Include="Factories">
<UniqueIdentifier>{3ce00d49-8c93-43bf-8d92-00c9b7087776}</UniqueIdentifier>
</Filter>
<Filter Include="Installer">
<UniqueIdentifier>{ca898efe-24d8-4317-99c9-769e8e80c3aa}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="EditWithNppExplorerCommandHandler.h">
<Filter>CommandHandlers</Filter>
</ClInclude>
<ClInclude Include="ExplorerCommandBase.h">
<Filter>CommandHandlers</Filter>
</ClInclude>
<ClInclude Include="Installer.h">
<Filter>Installer</Filter>
</ClInclude>
<ClInclude Include="CommandHandlerFactory.h">
<Filter>Factories</Filter>
</ClInclude>
<ClInclude Include="pch.h" />
<ClInclude Include="Helpers.h" />
<ClInclude Include="framework.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="EditWithNppExplorerCommandHandler.cpp">
<Filter>CommandHandlers</Filter>
</ClCompile>
<ClCompile Include="ExplorerCommandBase.cpp">
<Filter>CommandHandlers</Filter>
</ClCompile>
<ClCompile Include="Installer.cpp">
<Filter>Installer</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="CommandHandlerFactory.cpp">
<Filter>Factories</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="source.def" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop4 desktop5 uap10 com">
<Identity Name="NotepadPlusPlus" ProcessorArchitecture="neutral" Publisher="CN=&quot;Notepad++&quot;, O=&quot;Notepad++&quot;, L=Saint Cloud, S=Ile-de-France, C=FR" Version="1.0.0.0" />
<Properties>
<DisplayName>Notepad++</DisplayName>
<PublisherDisplayName>Notepad++</PublisherDisplayName>
<Logo>StoreLogo.png</Logo>
<uap10:AllowExternalContent>true</uap10:AllowExternalContent>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.22000.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources"/>
</Capabilities>
<Applications>
<Application Id="NotepadPlusPlus" Executable="notepad++.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<uap:VisualElements AppListEntry="none" DisplayName="Notepad++" Description="Notepad++" BackgroundColor="transparent" Square150x150Logo="Square150x150Logo.png" Square44x44Logo="Square44x44Logo.png">
</uap:VisualElements>
<Extensions>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type="*">
<desktop5:Verb Id="EditWithNotepadPlusPlus" Clsid="4EACAA14-3B43-4595-A44C-FBA8F0848620" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="Notepad++ Shell Extension">
<com:Class Id="4EACAA14-3B43-4595-A44C-FBA8F0848620" Path="NppModernShell.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
</Extensions>
</Application>
</Applications>
</Package>

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,68 @@
# Windows 11 Modern UI Context Menu integration
The purpose of this project is to allow Notepad++ to integrate into the new Windows 11 right-click context menu.
Doing this requires two new things.
* A dll library with some COM objects that the shell can communicate with.
* A Sparse Package containing the metadata for the COM server.
To build this, the following steps needs to be taken:
1. Build a Release dll file (NppModernShell.dll)
2. Generate a Sparse Package (NppModernShell.msix)
3. Sign both of these with signtool.exe
4. Make sure they are included in the installer, so they are deployed next to the notepad++.exe program.
5. The installer should, upon installation, install the package.
6. The installer should, upon uninstallation, uninstall the package.
## Prerequisites
To be able to build this project, the following is needed:
* [Visual Studio 2022](https://visualstudio.microsoft.com/vs)
* [Windows 11 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk)
## Build a Release dll file (NppModernShell.dll)
Just open the NppModernShell.sln Visual Studio solution, select Release as the build type, and do a Rebuild of the solution.
## Generate a Sparse Package (NppModernShell.msix)
To generate a Sparse Package, you need to have the makeappx.exe tool in your PATH, the easiest way to do this is to run the `Developer Command Prompt for VS 2022` command prompt, since it sets up the path.
Once inside the NppModernShell folder, run the following command to generate the Sparse Package:
```
makeappx pack /d .\Packaging /p .\NppModernShell.msix /nv
```
This takes the content of the Packaging directory, and packages them up into the msix file.
## Sign both of these with signtool.exe
Now we have both the `NppModernShell.dll` and `NppModernShell.msix` files, we need to sign them with a valid certificate.
To do this, once again run the `Developer Command Prompt for VS 2022` command prompt and change to the NppModernShell folder.
The following command expects the following:
* The pfx certificate is called MyCert.pfx
* The password for the pfx certificate is: `Test1234`
Make the needed changes to match the real certificate.
```
SignTool.exe sign /fd SHA256 /tr http://timestamp.digicert.com /td sha256 /a /f .\MyCert.pfx /p Test1234 /d "Notepad++" /du https://notepad-plus-plus.org/ NppModernShell.msix
SignTool.exe sign /fd SHA256 /tr http://timestamp.digicert.com /td sha256 /a /f .\MyCert.pfx /p Test1234 /d "Notepad++" /du https://notepad-plus-plus.org/ x64\Release\NppModernShell.dll
```
Now both files has been signed, and can be used.
## Make sure they are included in the installer, so they are deployed next to the notepad++.exe program.
The installer needs to deploy the two files into the same directory as notepad++.exe .
They need to be there, since the DLL is looking for notepad++.exe in the same directory as it is located itself.
## The installer should, upon installation, install the package.
When the installer is running, after all the files has been copied into the program files directory, the follow command should be be run to run the register function to register the package:
```
rundll32.exe .\NppModernShell.dll,RegisterSparsePackage
```
Remember to wait for the rundll32 process to exit before continuing.
## The installer should, upon uninstallation, uninstall the package.
When the uninstaller is running, it should run this command to unregister the package:
```
rundll32.exe .\NppModernShell.dll,UnregisterSparsePackage
```
Here we need to wait for rundll32 to finish, since if it isn't finished, the dll file will be locked by explorer.

View File

@ -0,0 +1,62 @@
#include "pch.h"
#include "CommandHandlerFactory.h"
using namespace NppModernShell::Factories;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(hModule);
UNREFERENCED_PARAMETER(lpReserved);
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
__control_entrypoint(DllExport)
STDAPI DllCanUnloadNow(void)
{
if (winrt::get_module_lock())
{
return S_FALSE;
}
winrt::clear_factory_cache();
return S_OK;
}
_Check_return_
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
{
if (!ppv)
{
return E_POINTER;
}
if (riid != IID_IClassFactory && riid != IID_IUnknown)
{
return E_NOINTERFACE;
}
if (rclsid != __uuidof(CommandHandlerFactory))
{
return E_INVALIDARG;
}
try
{
return winrt::make<CommandHandlerFactory>()->QueryInterface(riid, ppv);
}
catch (...)
{
return winrt::to_hresult();
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#include <shlwapi.h>
#include <shobjidl_core.h>
// WinRT Header Files
#include <winrt/base.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Management.Deployment.h>
// Windows Implementation Library Header Files
#include "wil\winrt.h"

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.230202.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
// add headers that you want to pre-compile here
#include "framework.h"
using namespace std;
#endif //PCH_H

View File

@ -0,0 +1,6 @@
LIBRARY
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
RegisterSparsePackage
UnregisterSparsePackage