Skip to content

Commit fe0938a

Browse files
committed
Auto-close WebDav file explorer windows
1 parent 4489f07 commit fe0938a

File tree

3 files changed

+105
-6
lines changed

3 files changed

+105
-6
lines changed

SecureFolderFS.Core.WebDav/SecureFolderFS.Core.WebDav.csproj

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@
77
<Platforms>AnyCPU;ARM64;x64;x86</Platforms>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<COMReference Include="Shell32">
12+
<VersionMinor>0</VersionMinor>
13+
<VersionMajor>1</VersionMajor>
14+
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
15+
<Lcid>0</Lcid>
16+
<WrapperTool>tlbimp</WrapperTool>
17+
<Isolated>false</Isolated>
18+
<EmbedInteropTypes>true</EmbedInteropTypes>
19+
</COMReference>
20+
<COMReference Include="SHDocVw">
21+
<WrapperTool>tlbimp</WrapperTool>
22+
<VersionMinor>1</VersionMinor>
23+
<VersionMajor>1</VersionMajor>
24+
<Guid>eab22ac0-30c1-11cf-a7eb-0000c05bae0b</Guid>
25+
<Lcid>0</Lcid>
26+
<Isolated>false</Isolated>
27+
<EmbedInteropTypes>true</EmbedInteropTypes>
28+
</COMReference>
29+
</ItemGroup>
30+
1031
<ItemGroup>
1132
<ProjectReference Include="..\lib\nwebdav\NWebDav.Server.HttpListener\NWebDav.Server.HttpListener.csproj" />
1233
<ProjectReference Include="..\lib\nwebdav\NWebDav.Server\NWebDav.Server.csproj" />
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Runtime.InteropServices;
1+
using System;
2+
using System.Runtime.InteropServices;
23
using System.Text;
34

45
namespace SecureFolderFS.Core.WebDav.UnsafeNative
@@ -8,28 +9,35 @@ internal static class UnsafeNativeApis
89
public const int CONNECT_TEMPORARY = 4;
910
public const int RESOURCETYPE_DISK = 1;
1011

11-
[DllImport("Mpr.dll")]
12+
[DllImport("Mpr.dll", CharSet = CharSet.Unicode)]
1213
public static extern int WNetAddConnection2(
1314
[In] NETRESOURCE lpNetResource,
1415
[In] string lpPassword,
1516
[In] string lpUserName,
1617
[In] uint dwFlags);
1718

18-
[DllImport("Mpr.dll")]
19+
[DllImport("Mpr.dll", CharSet = CharSet.Unicode)]
1920
public static extern int WNetCancelConnection2(
2021
[In] string lpName,
2122
[In] uint dwFlags,
2223
[In] bool fForce);
2324

24-
[DllImport("Mpr.dll")]
25+
[DllImport("Mpr.dll", CharSet = CharSet.Unicode)]
2526
public static extern int WNetGetConnection(
2627
[In] string lpLocalName,
2728
[Out] StringBuilder lpRemoteName,
2829
[In, Out] ref int lpnLength);
30+
31+
[DllImport("user32.dll")]
32+
public static extern int SendMessageA(
33+
[In] IntPtr hWnd,
34+
[In] uint wMsg,
35+
[In] IntPtr wParam,
36+
[In] IntPtr lParam);
2937
}
3038

3139
[StructLayout(LayoutKind.Sequential)]
32-
public class NETRESOURCE
40+
internal class NETRESOURCE
3341
{
3442
public uint dwScope;
3543
public uint dwType;
@@ -40,4 +48,9 @@ public class NETRESOURCE
4048
public string lpComment = null!;
4149
public string lpProvider = null!;
4250
}
51+
52+
internal static class WindowMessages
53+
{
54+
public const uint WM_CLOSE = 0x0010;
55+
}
4356
}

SecureFolderFS.Core.WebDav/WebDavFileSystem.cs

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using SecureFolderFS.Core.FileSystem;
22
using SecureFolderFS.Core.FileSystem.Enums;
3+
using SecureFolderFS.Core.FileSystem.Helpers;
4+
using SecureFolderFS.Core.WebDav.UnsafeNative;
35
using SecureFolderFS.Sdk.Storage;
6+
using System;
7+
using System.IO;
48
using System.Threading.Tasks;
59

610
namespace SecureFolderFS.Core.WebDav
@@ -28,7 +32,16 @@ public WebDavFileSystem(IFolder rootFolder, WebDavWrapper webDavWrapper)
2832
public async Task<bool> CloseAsync(FileSystemCloseMethod closeMethod)
2933
{
3034
if (IsOperational)
31-
IsOperational = !await Task.Run(() => _webDavWrapper.CloseFileSystem(closeMethod));
35+
{
36+
var closeResult = !await Task.Run(() => _webDavWrapper.CloseFileSystem(closeMethod));
37+
IsOperational = !closeResult;
38+
39+
if (closeResult && OperatingSystem.IsWindows()) // Closed successfully
40+
{
41+
// Close all file explorer windows
42+
await CloseExplorerShellAsync(RootFolder.Id);
43+
}
44+
}
3245

3346
return !IsOperational;
3447
}
@@ -38,5 +51,57 @@ public async ValueTask DisposeAsync()
3851
{
3952
_ = await CloseAsync(FileSystemCloseMethod.CloseForcefully);
4053
}
54+
55+
private static async Task CloseExplorerShellAsync(string path)
56+
{
57+
var formattedPath = PathHelpers.EnsureNoTrailingPathSeparator(path);
58+
var shellWindows = new SHDocVw.ShellWindows();
59+
60+
foreach (SHDocVw.InternetExplorer ie in shellWindows)
61+
{
62+
var formattedName = Path.GetFileNameWithoutExtension(ie.FullName);
63+
if (!formattedName.Equals("explorer", StringComparison.OrdinalIgnoreCase))
64+
continue;
65+
66+
var url = ie.LocationURL.Replace('/', Path.DirectorySeparatorChar);
67+
var formattedUrl = Uri.UnescapeDataString(url);
68+
if (!formattedUrl.Contains(formattedPath))
69+
continue;
70+
71+
var windowClosed = false;
72+
try
73+
{
74+
// Hook up closing event
75+
ie.WindowClosing += Window_Closing;
76+
77+
// Try quit first
78+
ie.Quit();
79+
80+
// Wait a short delay
81+
await Task.Delay(100);
82+
83+
if (!windowClosed)
84+
{
85+
// Retry with WM_CLOSE
86+
_ = UnsafeNativeApis.SendMessageA(ie.HWND, WindowMessages.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
87+
}
88+
}
89+
catch (Exception ex)
90+
{
91+
// May sometimes throw when trying to access invalid window handle
92+
_ = ex;
93+
}
94+
finally
95+
{
96+
// Unhook to avoid leaking memory
97+
ie.WindowClosing -= Window_Closing;
98+
}
99+
100+
void Window_Closing(bool IsChildWindow, ref bool Cancel)
101+
{
102+
windowClosed = true;
103+
}
104+
}
105+
}
41106
}
42107
}

0 commit comments

Comments
 (0)