Skip to content

Commit d2e7094

Browse files
committed
Add NanaZip.Extras and NanaZip.LegacyShellExtension projects.
1 parent d0f3f44 commit d2e7094

18 files changed

Lines changed: 1739 additions & 3 deletions
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* PROJECT: NanaZip
3+
* FILE: CNanaZipCopyHook.cpp
4+
* PURPOSE: Copy hook implementation
5+
*
6+
* LICENSE: The MIT License
7+
*
8+
* DEVELOPER: dinhngtu (contact@tudinh.xyz)
9+
*/
10+
11+
#define WIN32_LEAN_AND_MEAN
12+
13+
#include <windows.h>
14+
#include <exception>
15+
#include <filesystem>
16+
#include <Unknwn.h>
17+
#include <olectl.h>
18+
#include <shellapi.h>
19+
#include <strsafe.h>
20+
21+
#include "CNanaZipCopyHook.hpp"
22+
23+
static constexpr PCWSTR DRAGNOTIFIER_PREFIX = L"{7F4FD2EA-8CC8-43C4-8440-CD76805B4E95}";
24+
static constexpr ULONG_PTR DRAGNOTIFIER_COPY = 0x7F4FD2EA;
25+
26+
IFACEMETHODIMP_(UINT)
27+
CNanaZipCopyHook::CopyCallback(
28+
_In_opt_ HWND hwnd,
29+
UINT wFunc,
30+
UINT wFlags,
31+
_In_ PCWSTR pszSrcFile,
32+
DWORD dwSrcAttribs,
33+
_In_opt_ PCWSTR pszDestFile,
34+
DWORD dwDestAttribs) {
35+
36+
UNREFERENCED_PARAMETER(wFlags);
37+
UNREFERENCED_PARAMETER(dwSrcAttribs);
38+
UNREFERENCED_PARAMETER(dwDestAttribs);
39+
40+
try {
41+
if (wFunc != FO_COPY && wFunc == FO_MOVE)
42+
return IDYES;
43+
44+
std::filesystem::path srcpath(pszSrcFile);
45+
std::filesystem::path dstpath(pszDestFile);
46+
if (srcpath.stem() != DRAGNOTIFIER_PREFIX)
47+
return IDYES;
48+
49+
auto hwndString = srcpath.extension().wstring();
50+
if (!hwndString.length())
51+
return IDYES;
52+
53+
auto hwndDest = reinterpret_cast<HWND>(std::stoull(hwndString.substr(1)));
54+
auto dest = dstpath.parent_path();
55+
DragNotifierCopyData data{};
56+
if (FAILED(StringCchCopyW(&data.filename[0], ARRAYSIZE(data.filename), dest.c_str())))
57+
return IDYES;
58+
59+
COPYDATASTRUCT cds{DRAGNOTIFIER_COPY, sizeof(DragNotifierCopyData), &data};
60+
DWORD_PTR res;
61+
// we can't use PostMessage since the copydata message needs to stay alive during the send
62+
SendMessageTimeoutW(
63+
hwndDest,
64+
WM_COPYDATA,
65+
reinterpret_cast<WPARAM>(hwnd),
66+
reinterpret_cast<LPARAM>(&cds),
67+
SMTO_ABORTIFHUNG,
68+
100,
69+
&res);
70+
// but it doesn't matter if the caller returns or not
71+
return IDNO;
72+
} catch (const std::exception &e) {
73+
OutputDebugStringA(e.what());
74+
return IDYES;
75+
}
76+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#define WIN32_LEAN_AND_MEAN
4+
5+
#include <windows.h>
6+
#include <winrt/windows.foundation.h>
7+
#include <ShlObj.h>
8+
9+
struct CNanaZipCopyHook : winrt::implements<CNanaZipCopyHook, ICopyHookW> {
10+
STDMETHOD_(UINT, CopyCallback)(
11+
_In_opt_ HWND hwnd,
12+
UINT wFunc,
13+
UINT wFlags,
14+
_In_ PCWSTR pszSrcFile,
15+
DWORD dwSrcAttribs,
16+
_In_opt_ PCWSTR pszDestFile,
17+
DWORD dwDestAttribs);
18+
};
19+
20+
struct DragNotifierCopyData {
21+
wchar_t filename[MAX_PATH];
22+
};
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* PROJECT: NanaZip
3+
* FILE: NanaZip.Extras.ShellExtension.cpp
4+
* PURPOSE: Entry point and self registration code
5+
*
6+
* LICENSE: The MIT License
7+
*
8+
* DEVELOPER: dinhngtu (contact@tudinh.xyz)
9+
*/
10+
11+
#define WIN32_LEAN_AND_MEAN
12+
13+
#include <string>
14+
#include <windows.h>
15+
16+
#include <wil/registry.h>
17+
#include <wil/win32_helpers.h>
18+
#include <wil/stl.h>
19+
20+
#include "CNanaZipCopyHook.hpp"
21+
22+
using namespace std::string_literals;
23+
24+
static HINSTANCE g_hInstance = NULL;
25+
26+
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
27+
UNREFERENCED_PARAMETER(lpReserved);
28+
29+
switch (ul_reason_for_call) {
30+
case DLL_PROCESS_ATTACH:
31+
g_hInstance = hModule;
32+
break;
33+
case DLL_THREAD_ATTACH:
34+
case DLL_THREAD_DETACH:
35+
case DLL_PROCESS_DETACH:
36+
break;
37+
}
38+
return TRUE;
39+
}
40+
41+
struct DECLSPEC_UUID("{542CE69A-6EA7-4D77-9B8F-8F56CEA2BF16}") CNanaZipLegacyContextMenuFactory
42+
: winrt::implements<CNanaZipLegacyContextMenuFactory, IClassFactory> {
43+
IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) WIN_NOEXCEPT {
44+
if (pUnkOuter)
45+
return CLASS_E_NOAGGREGATION;
46+
47+
try {
48+
return winrt::make<CNanaZipCopyHook>()->QueryInterface(riid, ppvObject);
49+
} catch (...) {
50+
return winrt::to_hresult();
51+
}
52+
}
53+
54+
IFACEMETHODIMP LockServer(BOOL fLock) WIN_NOEXCEPT {
55+
if (fLock)
56+
++winrt::get_module_lock();
57+
else
58+
--winrt::get_module_lock();
59+
return S_OK;
60+
}
61+
};
62+
63+
_Use_decl_annotations_ STDAPI DllCanUnloadNow() {
64+
if (winrt::get_module_lock())
65+
return S_FALSE;
66+
winrt::clear_factory_cache();
67+
return S_OK;
68+
}
69+
70+
_Use_decl_annotations_ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID *ppv) {
71+
try {
72+
*ppv = NULL;
73+
if (rclsid == __uuidof(CNanaZipLegacyContextMenuFactory))
74+
return winrt::make<CNanaZipLegacyContextMenuFactory>()->QueryInterface(riid, ppv);
75+
return E_INVALIDARG;
76+
} catch (...) {
77+
return winrt::to_hresult();
78+
}
79+
}
80+
81+
_Use_decl_annotations_ STDAPI DllRegisterServer() {
82+
try {
83+
wchar_t clsid[wil::guid_string_buffer_length];
84+
85+
if (!StringFromGUID2(__uuidof(CNanaZipLegacyContextMenuFactory), clsid, ARRAYSIZE(clsid)))
86+
winrt::throw_hresult(E_OUTOFMEMORY);
87+
88+
auto clsidKeyName = L"Software\\Classes\\CLSID\\"s + clsid;
89+
auto clsidKey =
90+
wil::reg::create_unique_key(HKEY_CURRENT_USER, clsidKeyName.c_str(), wil::reg::key_access::readwrite);
91+
92+
auto serverKey =
93+
wil::reg::create_unique_key(clsidKey.get(), L"InprocServer32", wil::reg::key_access::readwrite);
94+
auto dllPath = wil::GetModuleFileNameW<std::wstring>(g_hInstance);
95+
wil::reg::set_value_string(serverKey.get(), NULL, dllPath.c_str());
96+
wil::reg::set_value_string(serverKey.get(), L"ThreadingModel", L"Apartment");
97+
98+
auto handlerKeyName = L"Software\\Classes\\Directory\\shellex\\CopyHookHandlers\\"s + clsid;
99+
auto handlerKey =
100+
wil::reg::create_unique_key(HKEY_CURRENT_USER, handlerKeyName.c_str(), wil::reg::key_access::readwrite);
101+
wil::reg::set_value_string(handlerKey.get(), NULL, clsid);
102+
103+
return S_OK;
104+
} catch (...) {
105+
return winrt::to_hresult();
106+
}
107+
}
108+
109+
_Use_decl_annotations_ STDAPI DllUnregisterServer() {
110+
try {
111+
wchar_t clsid[wil::guid_string_buffer_length];
112+
113+
if (!StringFromGUID2(__uuidof(CNanaZipLegacyContextMenuFactory), clsid, ARRAYSIZE(clsid)))
114+
winrt::throw_hresult(E_OUTOFMEMORY);
115+
116+
auto handlerKeyName = L"Software\\Classes\\Directory\\shellex\\CopyHookHandlers\\"s + clsid;
117+
RegDeleteKeyExW(HKEY_CURRENT_USER, handlerKeyName.c_str(), 0, 0);
118+
119+
auto serverKeyName = L"Software\\Classes\\CLSID\\"s + clsid + L"\\InprocServer32";
120+
RegDeleteKeyExW(HKEY_CURRENT_USER, serverKeyName.c_str(), 0, 0);
121+
122+
auto clsidKeyName = L"Software\\Classes\\CLSID\\"s + clsid;
123+
RegDeleteKeyExW(HKEY_CURRENT_USER, clsidKeyName.c_str(), 0, 0);
124+
125+
return S_OK;
126+
} catch (...) {
127+
return winrt::to_hresult();
128+
}
129+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LIBRARY
2+
3+
EXPORTS
4+
5+
DllCanUnloadNow PRIVATE
6+
DllGetClassObject PRIVATE
7+
DllRegisterServer PRIVATE
8+
DllUnregisterServer PRIVATE
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
3+
<dependency>
4+
<dependentAssembly>
5+
<assemblyIdentity
6+
type="win32"
7+
name="Microsoft.Windows.Common-Controls"
8+
version="6.0.0.0"
9+
processorArchitecture="*"
10+
publicKeyToken="6595b64144ccf1df"
11+
language="*"/>
12+
</dependentAssembly>
13+
</dependency>
14+
</assembly>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup Label="Globals">
4+
<ProjectGuid>{C1CBC99A-1225-446F-975D-4158D57BF586}</ProjectGuid>
5+
<RootNamespace>NanaZip</RootNamespace>
6+
<MileProjectType>DynamicLibrary</MileProjectType>
7+
<MileProjectManifestFile>NanaZip.Extras.ShellExtension.manifest</MileProjectManifestFile>
8+
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
9+
<YY_Thunks_File>YY_Thunks_for_Vista.obj</YY_Thunks_File>
10+
<MileUniCrtDisableRuntimeDebuggingFeature>true</MileUniCrtDisableRuntimeDebuggingFeature>
11+
<MileProjectEnableCppWinRTSupport>true</MileProjectEnableCppWinRTSupport>
12+
<MileProjectUseProjectProperties>true</MileProjectUseProjectProperties>
13+
<MileProjectCompanyName>M2-Team</MileProjectCompanyName>
14+
<MileProjectFileDescription>NanaZip Extras Shell Extension</MileProjectFileDescription>
15+
<MileProjectInternalName>NanaZip</MileProjectInternalName>
16+
<MileProjectLegalCopyright>© M2-Team and Contributors. All rights reserved.</MileProjectLegalCopyright>
17+
<MileProjectOriginalFilename>NanaZip.Extras.ShellExtension.dll</MileProjectOriginalFilename>
18+
<MileProjectProductName>NanaZip</MileProjectProductName>
19+
<MileProjectVersion>5.1.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2021-08-31'))).TotalDays).0</MileProjectVersion>
20+
<MileProjectVersionTag>Preview 0</MileProjectVersionTag>
21+
</PropertyGroup>
22+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Platform.x86.props" />
23+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Platform.x64.props" />
24+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Platform.ARM64.props" />
25+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Cpp.Default.props" />
26+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Cpp.props" />
27+
<Import Project="..\NanaZip.Frieren\NanaZip.Frieren.props" />
28+
<ItemDefinitionGroup>
29+
<ClCompile>
30+
<AdditionalOptions>%(AdditionalOptions) /Wv:18</AdditionalOptions>
31+
<PreprocessorDefinitions>WINRT_NO_SOURCE_LOCATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
32+
</ClCompile>
33+
<Link>
34+
<LargeAddressAware>true</LargeAddressAware>
35+
<ModuleDefinitionFile>NanaZip.Extras.ShellExtension.def</ModuleDefinitionFile>
36+
</Link>
37+
</ItemDefinitionGroup>
38+
<ItemDefinitionGroup>
39+
<ClCompile>
40+
<RuntimeLibrary Condition="'$(Configuration)' == 'Debug'">MultiThreadedDebug</RuntimeLibrary>
41+
<RuntimeLibrary Condition="'$(Configuration)' == 'Release'">MultiThreaded</RuntimeLibrary>
42+
</ClCompile>
43+
</ItemDefinitionGroup>
44+
<ItemGroup>
45+
<ClCompile Include="CNanaZipCopyHook.cpp" />
46+
</ItemGroup>
47+
<ItemGroup>
48+
<ClCompile Include="NanaZip.Extras.ShellExtension.cpp" />
49+
</ItemGroup>
50+
<ItemGroup>
51+
<ClInclude Include="CNanaZipCopyHook.hpp" />
52+
</ItemGroup>
53+
<ItemGroup>
54+
<PackageReference Include="Microsoft.Windows.ImplementationLibrary">
55+
<Version>1.0.240803.1</Version>
56+
</PackageReference>
57+
<PackageReference Include="Mile.Windows.Helpers">
58+
<Version>1.0.671</Version>
59+
</PackageReference>
60+
<PackageReference Include="Mile.Windows.UniCrt">
61+
<Version>1.1.278</Version>
62+
</PackageReference>
63+
</ItemGroup>
64+
<ItemGroup>
65+
<None Include="NanaZip.Extras.ShellExtension.def" />
66+
</ItemGroup>
67+
<Import Sdk="Mile.Project.Configurations" Version="1.0.1426" Project="Mile.Project.Cpp.targets" />
68+
</Project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup>
4+
<Filter Include="Source Files">
5+
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6+
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7+
</Filter>
8+
<Filter Include="Header Files">
9+
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10+
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
11+
</Filter>
12+
<Filter Include="Resource Files">
13+
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14+
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15+
</Filter>
16+
</ItemGroup>
17+
<ItemGroup>
18+
<ClCompile Include="CNanaZipCopyHook.cpp">
19+
<Filter>Source Files</Filter>
20+
</ClCompile>
21+
<ClCompile Include="NanaZip.Extras.ShellExtension.cpp">
22+
<Filter>Source Files</Filter>
23+
</ClCompile>
24+
</ItemGroup>
25+
<ItemGroup>
26+
<ClInclude Include="CNanaZipCopyHook.hpp">
27+
<Filter>Header Files</Filter>
28+
</ClInclude>
29+
</ItemGroup>
30+
<ItemGroup>
31+
<None Include="NanaZip.Extras.ShellExtension.def">
32+
<Filter>Source Files</Filter>
33+
</None>
34+
</ItemGroup>
35+
</Project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project Sdk="WixToolset.Sdk/5.0.2">
2+
<ItemGroup>
3+
<ProjectReference Include="..\NanaZip.Extras.ShellExtension.vcxproj" />
4+
</ItemGroup>
5+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!--
2+
This file contains the declaration of all the localizable strings.
3+
-->
4+
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5+
6+
<String Id="DowngradeError" Value="A newer version of [ProductName] is already installed." />
7+
<String Id="WrongArchError" Value="You must use the [ProductName] installer with the correct CPU architecture." />
8+
9+
<String Id="ShellExtensionFeatureTitle" Value="Shell Extension" />
10+
11+
</WixLocalization>

0 commit comments

Comments
 (0)