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