diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 736bfb17f4d8..225866176239 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -632,6 +632,7 @@ inetcpl Infobar INFOEXAMPLE Infotip +INFOW initialfile INITDIALOG INITGUID @@ -1725,6 +1726,7 @@ WNDCLASSEX WNDCLASSEXW WNDCLASSW wnode +WNet WORKSPACESEDITOR WORKSPACESLAUNCHER WORKSPACESSNAPSHOTTOOL @@ -1869,6 +1871,8 @@ contentdialog Convs coppied copyable +COPYASUNCCONTEXTMENU +COPYASUNCEXT coreclr Corpor covrun diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 3833f59652a5..18060454d214 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -125,6 +125,10 @@ "FileLocksmithContextMenuPackage.msix", "FileLocksmithCLI.exe", + "WinUI3Apps\\PowerToys.CopyAsUNCExt.dll", + "WinUI3Apps\\PowerToys.CopyAsUNCContextMenu.dll", + "CopyAsUNCContextMenuPackage.msix", + "WinUI3Apps\\Peek.Common.dll", "WinUI3Apps\\Peek.FilePreviewer.dll", "WinUI3Apps\\Powertoys.Peek.UI.dll", diff --git a/PowerToys.slnx b/PowerToys.slnx index 44b02db3fc77..fe1b75588f59 100644 --- a/PowerToys.slnx +++ b/PowerToys.slnx @@ -9,11 +9,11 @@ - + - + @@ -376,6 +376,11 @@ + + + + + @@ -427,7 +432,7 @@ - + @@ -438,7 +443,7 @@ - + @@ -464,13 +469,13 @@ - - - + - + + + @@ -713,13 +718,6 @@ - @@ -1069,6 +1067,7 @@ + diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp index 39786a16d886..9f7ca328cc22 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp @@ -1429,7 +1429,7 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall) try { // Packages to unregister - const std::vector packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}}; + const std::vector packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}, {L"CopyAsUNCContextMenu"}}; for (auto const &package : packagesToRemoveDisplayName) { diff --git a/installer/PowerToysSetupVNext/CopyAsUNC.wxs b/installer/PowerToysSetupVNext/CopyAsUNC.wxs new file mode 100644 index 000000000000..2bb044f2f2b5 --- /dev/null +++ b/installer/PowerToysSetupVNext/CopyAsUNC.wxs @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj b/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj index a7a9744e878b..4e6094bfed67 100644 --- a/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj +++ b/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj @@ -34,6 +34,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil call move /Y ..\..\..\BaseApplications.wxs.bk ..\..\..\BaseApplications.wxs call move /Y ..\..\..\CmdPal.wxs.bk ..\..\..\CmdPal.wxs call move /Y ..\..\..\ColorPicker.wxs.bk ..\..\..\ColorPicker.wxs + call move /Y ..\..\..\CopyAsUNC.wxs.bk ..\..\..\CopyAsUNC.wxs call move /Y ..\..\..\Core.wxs.bk ..\..\..\Core.wxs call move /Y ..\..\..\DscResources.wxs.bk ..\..\..\DscResources.wxs call move /Y ..\..\..\EnvironmentVariables.wxs.bk ..\..\..\EnvironmentVariables.wxs @@ -114,6 +115,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil + diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs index 584d61c4497f..4683355a60af 100644 --- a/installer/PowerToysSetupVNext/Product.wxs +++ b/installer/PowerToysSetupVNext/Product.wxs @@ -45,6 +45,7 @@ + diff --git a/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 index 6724d9517088..f2617204137b 100644 --- a/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 +++ b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 @@ -160,6 +160,10 @@ Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerCustomLangu Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs +#CopyAsUNC +Generate-FileList -fileDepsJson "" -fileListName CopyAsUNCAssetsFiles -wxsFilePath $PSScriptRoot\CopyAsUNC.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\CopyAsUNC" +Generate-FileComponents -fileListName "CopyAsUNCAssetsFiles" -wxsFilePath $PSScriptRoot\CopyAsUNC.wxs + #FileLocksmith Generate-FileList -fileDepsJson "" -fileListName FileLocksmithAssetsFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\FileLocksmith" Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 1b035a9a7ebf..492c1a32c00b 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -296,4 +296,8 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredNewPlusHideBuiltInNewContextMenuValue()); } + GpoRuleConfigured GPOWrapper::GetConfiguredCopyAsUNCEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredCopyAsUNCEnabledValue()); + } } diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index e5d8fa9884c3..543ecfb72e0a 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -79,6 +79,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredRunAtStartupValue(); static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue(); static GpoRuleConfigured GetConfiguredNewPlusHideBuiltInNewContextMenuValue(); + static GpoRuleConfigured GetConfiguredCopyAsUNCEnabledValue(); }; } diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index 6f60761b6670..1598e1f833ec 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -83,6 +83,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredRunAtStartupValue(); static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue(); static GpoRuleConfigured GetConfiguredNewPlusHideBuiltInNewContextMenuValue(); + static GpoRuleConfigured GetConfiguredCopyAsUNCEnabledValue(); } } } diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index 4a5a48eb7a34..e11bc8c2ebb9 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -71,6 +71,7 @@ namespace powertoys_gpo const std::wstring POLICY_CONFIGURE_ENABLED_QOI_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerQOIThumbnails"; const std::wstring POLICY_CONFIGURE_ENABLED_NEWPLUS = L"ConfigureEnabledUtilityNewPlus"; const std::wstring POLICY_CONFIGURE_ENABLED_WORKSPACES = L"ConfigureEnabledUtilityWorkspaces"; + const std::wstring POLICY_CONFIGURE_ENABLED_COPY_AS_UNC = L"ConfigureEnabledUtilityCopyAsUNC"; // The registry value names for PowerToys installer and update policies. const std::wstring POLICY_DISABLE_PER_USER_INSTALLATION = L"PerUserInstallationDisabled"; @@ -506,6 +507,11 @@ namespace powertoys_gpo { return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_NEWPLUS); } + + inline gpo_rule_configured_t getConfiguredCopyAsUNCEnabledValue() + { + return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_COPY_AS_UNC); + } #pragma endregion UtilityEnabledStatePolicies // Individual module setting policies diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/AppxManifest.xml b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/AppxManifest.xml new file mode 100644 index 000000000000..31d8dc616f23 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/AppxManifest.xml @@ -0,0 +1,56 @@ + + + + + PowerToys Copy as UNC Context Menu + Microsoft + Assets\CopyAsUNC\storelogo.png + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/CopyAsUNC.ico b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/CopyAsUNC.ico new file mode 100644 index 000000000000..5c773ac3b353 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/CopyAsUNC.ico differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/LargeTile.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/LargeTile.png new file mode 100644 index 000000000000..fee780ef62b1 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/LargeTile.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SmallTile.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SmallTile.png new file mode 100644 index 000000000000..7022ed52100b Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SmallTile.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SplashScreen.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SplashScreen.png new file mode 100644 index 000000000000..8710be6ecaab Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/SplashScreen.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square150x150Logo.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square150x150Logo.png new file mode 100644 index 000000000000..c68c09d31c56 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square150x150Logo.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square44x44Logo.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square44x44Logo.png new file mode 100644 index 000000000000..c68c09d31c56 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Square44x44Logo.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Wide310x150Logo.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Wide310x150Logo.png new file mode 100644 index 000000000000..c68c09d31c56 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/Wide310x150Logo.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/storelogo.png b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/storelogo.png new file mode 100644 index 000000000000..a5078f7f7944 Binary files /dev/null and b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Assets/CopyAsUNC/storelogo.png differ diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.base.rc b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.base.rc new file mode 100644 index 000000000000..e152a05eff1c --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.base.rc @@ -0,0 +1,45 @@ +#include +#include "Generated Files/resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FILE_VERSION + PRODUCTVERSION PRODUCT_VERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + 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 + END +END diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.vcxproj b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.vcxproj new file mode 100644 index 000000000000..94ed3e3a42e9 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/CopyAsUNCContextMenu.vcxproj @@ -0,0 +1,162 @@ + + + + + + + + + 17.0 + Win32Proj + {caa90eba-0776-4e3a-9d71-7e2c95a3c221} + CopyAsUNCContextMenu + + + PowerToys.CopyAsUNCContextMenu + + $(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\ + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps\ + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;COPYASUNCCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..;../../..; + + + Windows + true + false + runtimeobject.lib;Mpr.lib;%(AdditionalDependencies) + Source.def + + + del $(OutDir)\CopyAsUNCContextMenuPackage.msix /q +MakeAppx.exe pack /d . /p $(OutDir)CopyAsUNCContextMenuPackage.msix /nv + + + if not exist "$(OutDir)Assets\CopyAsUNC\" mkdir "$(OutDir)Assets\CopyAsUNC\" +xcopy /Y "$(ProjectDir)Assets\CopyAsUNC\CopyAsUNC.ico" "$(OutDir)Assets\CopyAsUNC\" + + + + + Level3 + true + true + true + WIN32;NDEBUG;COPYASUNCCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..;../../..; + + + Windows + true + true + true + false + runtimeobject.lib;Mpr.lib;%(AdditionalDependencies) + Source.def + + + del $(OutDir)\CopyAsUNCContextMenuPackage.msix /q +MakeAppx.exe pack /d . /p $(OutDir)CopyAsUNCContextMenuPackage.msix /nv + + + if not exist "$(OutDir)Assets\CopyAsUNC\" mkdir "$(OutDir)Assets\CopyAsUNC\" +xcopy /Y "$(ProjectDir)Assets\CopyAsUNC\CopyAsUNC.ico" "$(OutDir)Assets\CopyAsUNC\" + + + + + + + + + + + Create + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + + + {ddb485b8-e771-4b9e-a9b5-bff587daf5ab} + + + + + + + + + + + + + + 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}. + + + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Resources.resx b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Resources.resx new file mode 100644 index 000000000000..eef647a09ec8 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Resources.resx @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Copy as UNC path + This text will be shown when the user opens the context menu (right clicks) a file or folder on a mapped network drive. + + + Copy as UNC + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Source.def b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Source.def new file mode 100644 index 000000000000..05e3f386fcc5 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/Source.def @@ -0,0 +1,5 @@ +LIBRARY +EXPORTS +DllCanUnloadNow PRIVATE +DllGetClassObject PRIVATE +DllGetActivationFactory PRIVATE diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/dllmain.cpp b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/dllmain.cpp new file mode 100644 index 000000000000..a40d1b3c7492 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/dllmain.cpp @@ -0,0 +1,210 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +#include +#include + +#include "CopyAsUNCLib/Settings.h" + +#include +#include +#include +#include +#include +#include + +#include "Generated Files/resource.h" + +#pragma comment(lib, "Mpr.lib") + +using namespace Microsoft::WRL; + +HINSTANCE g_hInst = 0; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hInst = hModule; + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +class __declspec(uuid("89A22F51-9ED6-48FE-81FE-5DFD36F8CD32")) CopyAsUNCContextMenuCommand final : public RuntimeClass, IExplorerCommand, IObjectWithSite> +{ +public: + virtual const wchar_t* Title() { return L"Copy as UNC path"; } + virtual const EXPCMDFLAGS Flags() { return ECF_DEFAULT; } + virtual const EXPCMDSTATE State(_In_opt_ IShellItemArray*) { return ECS_ENABLED; } + + // IExplorerCommand + IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* name) + { + return SHStrDup(L"Copy as UNC path", name); + } + + IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon) + { + std::wstring iconResourcePath = get_module_folderpath(g_hInst); + iconResourcePath += L"\\Assets\\CopyAsUNC\\"; + iconResourcePath += L"CopyAsUNC.ico"; + return SHStrDup(iconResourcePath.c_str(), icon); + } + + IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip) + { + *infoTip = nullptr; + return E_NOTIMPL; + } + + IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName) + { + *guidCommandName = __uuidof(this); + return S_OK; + } + + IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL /*okToBeSlow*/, _Out_ EXPCMDSTATE* cmdState) + { + *cmdState = ECS_HIDDEN; + + if (!CopyAsUNCSettingsInstance().GetEnabled()) + return S_OK; + + if (CopyAsUNCSettingsInstance().GetShowInExtendedContextMenu()) + return S_OK; + + // Only show for items on mapped network drives + if (selection) + { + IShellItem* item = nullptr; + if (SUCCEEDED(selection->GetItemAt(0, &item))) + { + LPWSTR filePath = nullptr; + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &filePath))) + { + // Check first 3 chars for drive root (e.g. "Z:\") + std::wstring root(filePath, min((size_t)3, wcslen(filePath))); + if (GetDriveTypeW(root.c_str()) == DRIVE_REMOTE) + { + *cmdState = ECS_ENABLED; + } + CoTaskMemFree(filePath); + } + item->Release(); + } + } + + return S_OK; + } + + IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept + { + if (!selection) + return S_OK; + + IShellItem* item = nullptr; + if (FAILED(selection->GetItemAt(0, &item))) + return S_OK; + + LPWSTR filePath = nullptr; + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &filePath))) + { + std::wstring uncPath; + + // If already a UNC path, use it directly + if (wcslen(filePath) >= 2 && filePath[0] == L'\\' && filePath[1] == L'\\') + { + uncPath = filePath; + } + else + { + // Resolve mapped drive letter to UNC via WNetGetUniversalName + DWORD bufSize = MAX_PATH * 2; + std::vector buf(bufSize); + DWORD result = WNetGetUniversalNameW(filePath, UNIVERSAL_NAME_INFO_LEVEL, buf.data(), &bufSize); + + if (result == ERROR_MORE_DATA) + { + buf.resize(bufSize); + result = WNetGetUniversalNameW(filePath, UNIVERSAL_NAME_INFO_LEVEL, buf.data(), &bufSize); + } + + if (result == NO_ERROR) + { + auto info = reinterpret_cast(buf.data()); + uncPath = info->lpUniversalName; + } + } + + if (!uncPath.empty()) + { + if (OpenClipboard(nullptr)) + { + EmptyClipboard(); + size_t byteLen = (uncPath.size() + 1) * sizeof(wchar_t); + HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, byteLen); + if (hMem) + { + void* locked = GlobalLock(hMem); + memcpy(locked, uncPath.c_str(), byteLen); + GlobalUnlock(hMem); + SetClipboardData(CF_UNICODETEXT, hMem); + } + CloseClipboard(); + } + } + + CoTaskMemFree(filePath); + } + + item->Release(); + return S_OK; + } + + IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags) + { + *flags = Flags(); + return S_OK; + } + + IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands) + { + *enumCommands = nullptr; + return E_NOTIMPL; + } + + // IObjectWithSite + IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept + { + m_site = site; + return S_OK; + } + IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept { return m_site.CopyTo(riid, site); } + +protected: + ComPtr m_site; +}; + +CoCreatableClass(CopyAsUNCContextMenuCommand) + CoCreatableClassWrlCreatorMapInclude(CopyAsUNCContextMenuCommand) + + STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory) +{ + return Module::GetModule().GetActivationFactory(activatableClassId, factory); +} + +STDAPI DllCanUnloadNow() +{ + return Module::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE; +} + +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _COM_Outptr_ void** instance) +{ + return Module::GetModule().GetClassObject(rclsid, riid, instance); +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/framework.h b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/framework.h new file mode 100644 index 000000000000..5a3602bb1ef5 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/packages.config b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/packages.config new file mode 100644 index 000000000000..d63e76b1f5ad --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/packages.config @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.cpp b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.cpp new file mode 100644 index 000000000000..64b7eef6d6b9 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.h b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.h new file mode 100644 index 000000000000..b6161e34cc0d --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/pch.h @@ -0,0 +1,8 @@ +#ifndef PCH_H +#define PCH_H + +#include + +#include "framework.h" + +#endif //PCH_H diff --git a/src/modules/CopyAsUNC/CopyAsUNCContextMenu/resource.base.h b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/resource.base.h new file mode 100644 index 000000000000..9ee60540dfc9 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCContextMenu/resource.base.h @@ -0,0 +1,12 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Copy as UNC Context Menu" +#define INTERNAL_NAME "PowerToys.CopyAsUNCContextMenu.dll" +#define ORIGINAL_FILENAME "PowerToys.CopyAsUNCContextMenu.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.base.rc b/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.base.rc new file mode 100644 index 000000000000..17500550d6b4 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.base.rc @@ -0,0 +1,40 @@ +#include +#include "Generated Files/resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FILE_VERSION + PRODUCTVERSION PRODUCT_VERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + 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 + END +END diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.vcxproj b/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.vcxproj new file mode 100644 index 000000000000..0e651a54053e --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/CopyAsUNCExt.vcxproj @@ -0,0 +1,114 @@ + + + + + + + + + 16.0 + Win32Proj + {7d462a53-6ec3-44eb-ae6f-b86ac892c9e3} + CopyAsUNCExt + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps\ + PowerToys.CopyAsUNCExt + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;COPYASUNCEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..;../../;../../../;%(AdditionalIncludeDirectories) + + + Windows + true + false + dll.def + + + + + Level3 + true + true + true + WIN32;NDEBUG;COPYASUNCEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..;../../;../../../;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + dll.def + + + + + + + + + + + + + + + + + + + + Create + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + {ddb485b8-e771-4b9e-a9b5-bff587daf5ab} + + + + + + + + + + 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}. + + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/PowerToysModule.cpp b/src/modules/CopyAsUNC/CopyAsUNCExt/PowerToysModule.cpp new file mode 100644 index 000000000000..7b9e4fe7a103 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/PowerToysModule.cpp @@ -0,0 +1,118 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "CopyAsUNCLib/Constants.h" +#include "CopyAsUNCLib/Settings.h" + +#include "dllmain.h" +#include "Generated Files/resource.h" + +class CopyAsUNCModule : public PowertoyModuleIface +{ +public: + CopyAsUNCModule() + { + LoggerHelpers::init_logger(constants::nonlocalizable::PowerToyName, L"ModuleInterface", "CopyAsUNC"); + init_settings(); + } + + virtual const wchar_t* get_name() override + { + static WCHAR buffer[128]; + LoadStringW(globals::instance, IDS_COPY_AS_UNC_POWERTOYNAME, buffer, ARRAYSIZE(buffer)); + return buffer; + } + + virtual const wchar_t* get_key() override + { + return constants::nonlocalizable::PowerToyKey; + } + + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredCopyAsUNCEnabledValue(); + } + + virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.add_bool_toggle(L"bool_show_extended_menu", + L"", + CopyAsUNCSettingsInstance().GetShowInExtendedContextMenu()); + return settings.serialize_to_buffer(buffer, buffer_size); + } + + virtual void set_config(PCWSTR config) override + { + try + { + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + auto extendedMenu = values.get_bool_value(L"bool_show_extended_menu"); + if (extendedMenu.has_value()) + { + CopyAsUNCSettingsInstance().SetExtendedContextMenuOnly(extendedMenu.value()); + CopyAsUNCSettingsInstance().Save(); + } + } + catch (std::exception& e) + { + Logger::error("Configuration parsing failed: {}", std::string{ e.what() }); + } + } + + virtual void enable() override + { + Logger::info(L"Copy as UNC enabled"); + + if (package::IsWin11OrGreater()) + { + std::wstring path = get_module_folderpath(globals::instance); + std::wstring packageUri = path + L"\\CopyAsUNCContextMenuPackage.msix"; + if (!package::IsPackageRegisteredWithPowerToysVersion(constants::nonlocalizable::ContextMenuPackageName)) + { + package::RegisterSparsePackage(path, packageUri); + } + } + + m_enabled = true; + } + + virtual void disable() override + { + Logger::info(L"Copy as UNC disabled"); + m_enabled = false; + } + + virtual bool is_enabled() override + { + return m_enabled; + } + + virtual void destroy() override + { + delete this; + } + +private: + bool m_enabled = false; + + void init_settings() + { + m_enabled = CopyAsUNCSettingsInstance().GetEnabled(); + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new CopyAsUNCModule(); +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/Resources.resx b/src/modules/CopyAsUNC/CopyAsUNCExt/Resources.resx new file mode 100644 index 000000000000..0f86156cf733 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/Resources.resx @@ -0,0 +1,10 @@ + + + text/microsoft-resx + 2.0 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Copy as UNC + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/dll.def b/src/modules/CopyAsUNC/CopyAsUNCExt/dll.def new file mode 100644 index 000000000000..982a7f5abec3 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/dll.def @@ -0,0 +1,3 @@ +LIBRARY +EXPORTS +powertoy_create diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.cpp b/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.cpp new file mode 100644 index 000000000000..0ceff34bfee8 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.cpp @@ -0,0 +1,25 @@ +#include "pch.h" + +#pragma comment(lib, "shlwapi") + +#include "dllmain.h" + +namespace globals +{ + HMODULE instance; +} + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + globals::instance = hModule; + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.h b/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.h new file mode 100644 index 000000000000..5d0e5528efe2 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/dllmain.h @@ -0,0 +1,8 @@ +#pragma once + +#include "pch.h" + +namespace globals +{ + extern HMODULE instance; +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/packages.config b/src/modules/CopyAsUNC/CopyAsUNCExt/packages.config new file mode 100644 index 000000000000..97349a856f8f --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/pch.cpp b/src/modules/CopyAsUNC/CopyAsUNCExt/pch.cpp new file mode 100644 index 000000000000..1a6a81e23883 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/pch.cpp @@ -0,0 +1,3 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/pch.h b/src/modules/CopyAsUNC/CopyAsUNCExt/pch.h new file mode 100644 index 000000000000..bdfb1dd12f29 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/pch.h @@ -0,0 +1,11 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#include +#include +#include diff --git a/src/modules/CopyAsUNC/CopyAsUNCExt/resource.base.h b/src/modules/CopyAsUNC/CopyAsUNCExt/resource.base.h new file mode 100644 index 000000000000..f7869739b333 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCExt/resource.base.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. + +////////////////////////////// +// Non-localizable + +#define IDS_COPY_AS_UNC_POWERTOYNAME 101 + +#define FILE_DESCRIPTION "PowerToys Copy as UNC Module Interface" +#define INTERNAL_NAME "PowerToys.CopyAsUNCExt.dll" +#define ORIGINAL_FILENAME "PowerToys.CopyAsUNCExt.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/Constants.h b/src/modules/CopyAsUNC/CopyAsUNCLib/Constants.h new file mode 100644 index 000000000000..0b321561670e --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/Constants.h @@ -0,0 +1,22 @@ +#pragma once + +#include "pch.h" + +// Non-localizable constants +namespace constants::nonlocalizable +{ + // String key used by PowerToys runner + constexpr WCHAR PowerToyKey[] = L"Copy as UNC"; + + // Nonlocalized name of this PowerToy, for logs, etc. + constexpr WCHAR PowerToyName[] = L"CopyAsUNC"; + + // JSON key used to store extended menu enabled + constexpr WCHAR JsonKeyShowInExtendedContextMenu[] = L"showInExtendedContextMenu"; + + // Path of the JSON file used to store settings + constexpr WCHAR DataFilePath[] = L"\\copy-as-unc-settings.json"; + + // Name of the tier 1 context menu package + constexpr WCHAR ContextMenuPackageName[] = L"CopyAsUNCContextMenu"; +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/CopyAsUNCLib.vcxproj b/src/modules/CopyAsUNC/CopyAsUNCLib/CopyAsUNCLib.vcxproj new file mode 100644 index 000000000000..de7b0ca2cbf7 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/CopyAsUNCLib.vcxproj @@ -0,0 +1,86 @@ + + + + + + 17.0 + Win32Proj + {ddb485b8-e771-4b9e-a9b5-bff587daf5ab} + CopyAsUNCLib + 10.0 + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + Unicode + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + ../../..; + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + ../../..; + + + true + true + true + + + + + + + + + + + + Create + + + + + + + + + + + + + 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}. + + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.cpp b/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.cpp new file mode 100644 index 000000000000..10d74476e0f2 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.cpp @@ -0,0 +1,112 @@ +#include "pch.h" +#include "Settings.h" +#include "Constants.h" + +#include +#include +#include + +static bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime) +{ + WIN32_FILE_ATTRIBUTE_DATA attr{}; + if (GetFileAttributesExW(filePath.c_str(), GetFileExInfoStandard, &attr)) + { + *lpFileTime = attr.ftLastWriteTime; + return true; + } + return false; +} + +CopyAsUNCSettings::CopyAsUNCSettings() +{ + generalJsonFilePath = PTSettingsHelper::get_powertoys_general_save_file_location(); + std::wstring savePath = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyKey); + std::error_code ec; + + jsonFilePath = savePath + constants::nonlocalizable::DataFilePath; + RefreshEnabledState(); + Load(); +} + +void CopyAsUNCSettings::Save() +{ + json::JsonObject jsonData; + + jsonData.SetNamedValue(constants::nonlocalizable::JsonKeyShowInExtendedContextMenu, json::value(settings.showInExtendedContextMenu)); + + json::to_file(jsonFilePath, jsonData); + GetSystemTimeAsFileTime(&lastLoadedTime); +} + +void CopyAsUNCSettings::Load() +{ + if (!std::filesystem::exists(jsonFilePath)) + { + Save(); + } + else + { + ParseJson(); + } +} + +void CopyAsUNCSettings::RefreshEnabledState() +{ + FILETIME lastModifiedTime{}; + if (!(LastModifiedTime(generalJsonFilePath, &lastModifiedTime) && + CompareFileTime(&lastModifiedTime, &lastLoadedGeneralSettingsTime) == 1)) + return; + + lastLoadedGeneralSettingsTime = lastModifiedTime; + + auto json = json::from_file(generalJsonFilePath); + if (!json) + return; + + const json::JsonObject& jsonSettings = json.value(); + try + { + json::JsonObject modulesEnabledState; + json::get(jsonSettings, L"enabled", modulesEnabledState, json::JsonObject{}); + json::get(modulesEnabledState, L"Copy as UNC", settings.enabled, true); + } + catch (const winrt::hresult_error&) + { + } +} + +void CopyAsUNCSettings::Reload() +{ + FILETIME lastModifiedTime{}; + if (LastModifiedTime(jsonFilePath, &lastModifiedTime) && + CompareFileTime(&lastModifiedTime, &lastLoadedTime) == 1) + { + Load(); + } +} + +void CopyAsUNCSettings::ParseJson() +{ + auto json = json::from_file(jsonFilePath); + if (json) + { + const json::JsonObject& jsonSettings = json.value(); + try + { + if (json::has(jsonSettings, constants::nonlocalizable::JsonKeyShowInExtendedContextMenu, json::JsonValueType::Boolean)) + { + settings.showInExtendedContextMenu = jsonSettings.GetNamedBoolean(constants::nonlocalizable::JsonKeyShowInExtendedContextMenu); + } + } + catch (const winrt::hresult_error&) + { + } + } + GetSystemTimeAsFileTime(&lastLoadedTime); +} + +CopyAsUNCSettings& CopyAsUNCSettingsInstance() +{ + static CopyAsUNCSettings instance; + return instance; +} diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.h b/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.h new file mode 100644 index 000000000000..757cbcb08f42 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/Settings.h @@ -0,0 +1,52 @@ +#pragma once + +#include "pch.h" + +class CopyAsUNCSettings +{ +public: + CopyAsUNCSettings(); + + inline bool GetEnabled() + { + // TODO: Add GPO entry to src/common/utils/gpo.h and uncomment: + // auto gpoSetting = powertoys_gpo::getConfiguredCopyAsUNCEnabledValue(); + // if (gpoSetting == powertoys_gpo::gpo_rule_configured_enabled) return true; + // if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled) return false; + Reload(); + RefreshEnabledState(); + return settings.enabled; + } + + inline bool GetShowInExtendedContextMenu() const + { + return settings.showInExtendedContextMenu; + } + + inline void SetExtendedContextMenuOnly(bool extendedOnly) + { + settings.showInExtendedContextMenu = extendedOnly; + } + + void Save(); + void Load(); + +private: + struct Settings + { + bool enabled{ true }; + bool showInExtendedContextMenu{ false }; + }; + + void RefreshEnabledState(); + void Reload(); + void ParseJson(); + + Settings settings; + std::wstring generalJsonFilePath; + std::wstring jsonFilePath; + FILETIME lastLoadedTime{}; + FILETIME lastLoadedGeneralSettingsTime{}; +}; + +CopyAsUNCSettings& CopyAsUNCSettingsInstance(); diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/framework.h b/src/modules/CopyAsUNC/CopyAsUNCLib/framework.h new file mode 100644 index 000000000000..54b83e94fd33 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/packages.config b/src/modules/CopyAsUNC/CopyAsUNCLib/packages.config new file mode 100644 index 000000000000..97349a856f8f --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/pch.cpp b/src/modules/CopyAsUNC/CopyAsUNCLib/pch.cpp new file mode 100644 index 000000000000..64b7eef6d6b9 --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/CopyAsUNC/CopyAsUNCLib/pch.h b/src/modules/CopyAsUNC/CopyAsUNCLib/pch.h new file mode 100644 index 000000000000..e012418d365d --- /dev/null +++ b/src/modules/CopyAsUNC/CopyAsUNCLib/pch.h @@ -0,0 +1,9 @@ +#ifndef PCH_H +#define PCH_H + +#include +#include + +#include "framework.h" + +#endif //PCH_H diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 626bddc47f51..5cd0e400a759 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -286,6 +286,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"PowerToys.CmdPalModuleInterface.dll", L"PowerToys.ZoomItModuleInterface.dll", L"PowerToys.LightSwitchModuleInterface.dll", + L"WinUI3Apps/PowerToys.CopyAsUNCExt.dll", // L"PowerToys.PowerDisplayModuleInterface.dll", // TEMPORARILY_DISABLED: PowerDisplay }; diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 022ad9d76ce9..bba43b791ac2 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -807,6 +807,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) return "ZoomIt"; case ESettingsWindowNames::PowerDisplay: return "PowerDisplay"; + case ESettingsWindowNames::CopyAsUNC: + return "CopyAsUNC"; default: { Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast(value)); @@ -950,6 +952,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value) { return ESettingsWindowNames::PowerDisplay; } + else if (value == "CopyAsUNC") + { + return ESettingsWindowNames::CopyAsUNC; + } else { Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value)); diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h index 4da4d70a7a36..21fbeb50bb18 100644 --- a/src/runner/settings_window.h +++ b/src/runner/settings_window.h @@ -37,6 +37,7 @@ enum class ESettingsWindowNames CmdPal, ZoomIt, PowerDisplay, + CopyAsUNC, }; std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index f56176a1f0ce..9ae28a077e9e 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -366,6 +366,22 @@ public bool Hosts } } + private bool copyAsUNC = true; + + [JsonPropertyName("Copy as UNC")] + public bool CopyAsUNC + { + get => copyAsUNC; + set + { + if (copyAsUNC != value) + { + LogTelemetryEvent(value); + copyAsUNC = value; + } + } + } + private bool fileLocksmith = true; [JsonPropertyName("File Locksmith")] diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml index 895dbd07825e..fe93366a53ce 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml @@ -279,6 +279,14 @@ + + + + + diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index f2efe864b4ce..3d374f035ff0 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -3762,6 +3762,14 @@ Activate by holding the key for the character you want to add an accent to, then Press duration before showing taskbar icon shortcuts (ms) ms = milliseconds + + Shell integration + This refers to directly integrating in with the Windows shell + + + Copy as UNC + Copy as UNC is the name of the utility; do not loc + A Windows shell extension to find out which processes are using the selected files and directories. {Locked="Windows"} diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs index 75c22dd855b5..8b37c1c65e84 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs @@ -225,6 +225,8 @@ public PowerPreviewViewModel(ISettingsRepository moduleSet _stlThumbnailColor = Settings.Properties.StlThumbnailColor.Value; + _isCopyAsUNCEnabled = GeneralSettingsConfig.Enabled.CopyAsUNC; + _qoiThumbnailEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredQoiThumbnailsEnabledValue(); if (_qoiThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _qoiThumbnailEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -1102,6 +1104,25 @@ public bool IsElevated } } + private bool _isCopyAsUNCEnabled; + + public bool IsCopyAsUNCEnabled + { + get => _isCopyAsUNCEnabled; + set + { + if (_isCopyAsUNCEnabled != value) + { + _isCopyAsUNCEnabled = value; + GeneralSettingsConfig.Enabled.CopyAsUNC = value; + OnPropertyChanged(nameof(IsCopyAsUNCEnabled)); + + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(outgoing.ToString()); + } + } + } + private void RaisePropertyChanged([CallerMemberName] string propertyName = null) { // Notify UI of property change