Skip to content

Commit c1f76c3

Browse files
Init
Co-Authored-By: Dongle <[email protected]>
1 parent ae7432d commit c1f76c3

23 files changed

+1401
-391
lines changed

Diff for: src/Files.App.CsWin32/Extras.cs

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nInd
3737
? (nint)_SetWindowLong(hWnd, (int)nIndex, (int)dwNewLong)
3838
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
3939
}
40+
41+
[DllImport("User32", EntryPoint = "SHUpdateRecycleBinIcon")]
42+
public static extern void SHUpdateRecycleBinIcon();
4043
}
4144

4245
namespace Extras
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4+
using System.Diagnostics;
45
using System.Runtime.InteropServices;
5-
using Windows.Win32.Foundation;
66

7-
namespace Windows.Win32
7+
namespace Windows.Win32.Foundation
88
{
9-
public static class HRESULTExtensions
9+
[DebuggerDisplay("{" + nameof(Value) + ",h}")]
10+
public readonly partial struct HRESULT
1011
{
1112
/// <summary>
1213
/// Throws an exception if the <see cref="HRESULT"/> indicates a failure in debug mode. Otherwise, it returns the original <see cref="HRESULT"/>.
1314
/// </summary>
14-
/// <param name="hr">Represents the result of an operation, indicating success or failure.</param>
1515
/// <returns>Returns the original <see cref="HRESULT"/> value regardless of the operation's success.</returns>
16-
public static HRESULT ThrowIfFailedOnDebug(this HRESULT hr)
16+
public readonly HRESULT ThrowIfFailedOnDebug()
1717
{
1818
#if DEBUG
19-
if (hr.Failed)
20-
Marshal.ThrowExceptionForHR(hr.Value);
19+
if (Failed) Marshal.ThrowExceptionForHR(Value);
2120
#endif
2221

23-
return hr;
22+
return this;
2423
}
2524
}
2625
}

Diff for: src/Files.App.CsWin32/NativeMethods.txt

+30
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,33 @@ FILE_FILE_COMPRESSION
172172
WM_WINDOWPOSCHANGING
173173
WINDOWPOS
174174
UnregisterClass
175+
E_POINTER
176+
E_NOINTERFACE
177+
E_FAIL
178+
IShellFolder
179+
FILE_FLAGS_AND_ATTRIBUTES
180+
GetLogicalDrives
181+
IShellItemImageFactory
182+
GdipCreateBitmapFromHBITMAP
183+
GdipGetImageEncodersSize
184+
GdipGetImageEncoders
185+
ImageFormatPNG
186+
ImageFormatJPEG
187+
IStream
188+
CreateStreamOnHGlobal
189+
STATFLAG
190+
STREAM_SEEK
191+
GdipSaveImageToStream
192+
GdipGetImageRawFormat
193+
_TRANSFER_SOURCE_FLAGS
194+
E_NOTIMPL
195+
DeleteObject
196+
GdipDisposeImage
197+
DeleteObject
198+
OleInitialize
199+
OleUninitialize
200+
IShellIconOverlayManager
201+
SHGetFileInfo
202+
GetFileAttributes
203+
SetFileAttributes
204+
INVALID_FILE_ATTRIBUTES

Diff for: src/Files.App.Storage/Files.App.Storage.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
<Configurations>Debug;Release</Configurations>
1010
<Platforms>x86;x64;arm64</Platforms>
1111
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
12+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1213
</PropertyGroup>
1314

1415
<ItemGroup>
1516
<PackageReference Include="FluentFTP" />
1617
</ItemGroup>
1718

1819
<ItemGroup>
20+
<ProjectReference Include="..\Files.App.CsWin32\Files.App.CsWin32.csproj" />
1921
<ProjectReference Include="..\Files.Core.Storage\Files.Core.Storage.csproj" />
2022
<ProjectReference Include="..\Files.Shared\Files.Shared.csproj" />
2123
</ItemGroup>
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using System.Numerics;
5+
using System.Runtime.CompilerServices;
6+
using Windows.Win32;
7+
8+
namespace Files.App.Storage.Storables
9+
{
10+
public partial class HomeFolder : IHomeFolder
11+
{
12+
public string Id => "Home"; // Will be "files://Home" in the future.
13+
14+
public string Name => "Home";
15+
16+
/// <inheritdoc/>
17+
public async IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type = StorableType.Folder, [EnumeratorCancellation] CancellationToken cancellationToken = default)
18+
{
19+
await foreach (var folder in GetQuickAccessFolderAsync(cancellationToken))
20+
{
21+
cancellationToken.ThrowIfCancellationRequested();
22+
23+
yield return folder;
24+
}
25+
26+
await foreach (var drive in GetLogicalDrivesAsync(cancellationToken))
27+
{
28+
cancellationToken.ThrowIfCancellationRequested();
29+
30+
yield return drive;
31+
}
32+
33+
await foreach (var location in GetNetworkLocationsAsync(cancellationToken))
34+
{
35+
cancellationToken.ThrowIfCancellationRequested();
36+
37+
yield return location;
38+
}
39+
}
40+
41+
/// <inheritdoc/>
42+
public IAsyncEnumerable<IStorableChild> GetQuickAccessFolderAsync(CancellationToken cancellationToken = default)
43+
{
44+
IFolder folder = new WindowsFolder(new Guid("3936e9e4-d92c-4eee-a85a-bc16d5ea0819"));
45+
return folder.GetItemsAsync(StorableType.Folder, cancellationToken);
46+
}
47+
48+
/// <inheritdoc/>
49+
public async IAsyncEnumerable<IStorableChild> GetLogicalDrivesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
50+
{
51+
var availableDrives = PInvoke.GetLogicalDrives();
52+
if (availableDrives is 0)
53+
yield break;
54+
55+
int count = BitOperations.PopCount(availableDrives);
56+
var driveLetters = new char[count];
57+
58+
count = 0;
59+
char driveLetter = 'A';
60+
while (availableDrives is not 0)
61+
{
62+
if ((availableDrives & 1) is not 0)
63+
driveLetters[count++] = driveLetter;
64+
65+
availableDrives >>= 1;
66+
driveLetter++;
67+
}
68+
69+
foreach (int letter in driveLetters)
70+
{
71+
cancellationToken.ThrowIfCancellationRequested();
72+
73+
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
74+
throw new InvalidOperationException();
75+
76+
yield return new WindowsFolder(driveRoot.ThisPtr);
77+
await Task.Yield();
78+
}
79+
80+
}
81+
82+
/// <inheritdoc/>
83+
public IAsyncEnumerable<IStorableChild> GetNetworkLocationsAsync(CancellationToken cancellationToken = default)
84+
{
85+
Guid FOLDERID_NetHood = new("{C5ABBF53-E17F-4121-8900-86626FC2C973}");
86+
IFolder folder = new WindowsFolder(FOLDERID_NetHood);
87+
return folder.GetItemsAsync(StorableType.Folder, cancellationToken);
88+
}
89+
90+
/// <inheritdoc/>
91+
public IAsyncEnumerable<IStorableChild> GetRecentFilesAsync(CancellationToken cancellationToken = default)
92+
{
93+
Guid FOLDERID_NetHood = new("{AE50C081-EBD2-438A-8655-8A092E34987A}");
94+
IFolder folder = new WindowsFolder(FOLDERID_NetHood);
95+
return folder.GetItemsAsync(StorableType.Folder, cancellationToken);
96+
}
97+
}
98+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage.Storables
5+
{
6+
public partial interface IHomeFolder : IFolder
7+
{
8+
/// <summary>
9+
/// Gets quick access folders.
10+
/// </summary>
11+
/// <param name="cancellationToken">The cancellation token.</param>
12+
/// <returns>A list of the collection.</returns>
13+
public IAsyncEnumerable<IStorableChild> GetQuickAccessFolderAsync(CancellationToken cancellationToken = default);
14+
15+
/// <summary>
16+
/// Gets available logical drives.
17+
/// </summary>
18+
/// <param name="cancellationToken">The cancellation token.</param>
19+
/// <returns>A list of the collection.</returns>
20+
public IAsyncEnumerable<IStorableChild> GetLogicalDrivesAsync(CancellationToken cancellationToken = default);
21+
22+
/// <summary>
23+
/// Gets network locations(shortcuts).
24+
/// </summary>
25+
/// <param name="cancellationToken">The cancellation token.</param>
26+
/// <returns>A list of the collection.</returns>
27+
public IAsyncEnumerable<IStorableChild> GetNetworkLocationsAsync(CancellationToken cancellationToken = default);
28+
29+
/// <summary>
30+
/// Gets recent files.
31+
/// </summary>
32+
/// <param name="cancellationToken">The cancellation token.</param>
33+
/// <returns>A list of the collection.</returns>
34+
public IAsyncEnumerable<IStorableChild> GetRecentFilesAsync(CancellationToken cancellationToken = default);
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Windows.Win32;
5+
using Windows.Win32.UI.Shell;
6+
7+
namespace Files.App.Storage.Storables
8+
{
9+
public interface IWindowsStorable
10+
{
11+
ComPtr<IShellItem> ThisPtr { get; }
12+
}
13+
}
+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Windows.Win32;
5+
6+
namespace Files.App.Storage
7+
{
8+
/// <summary>
9+
/// Represents an asynchronous operation on STA.
10+
/// </summary>
11+
public partial class STATask
12+
{
13+
public static Task Run(Action action)
14+
{
15+
var tcs = new TaskCompletionSource();
16+
17+
Thread thread =
18+
new(() =>
19+
{
20+
PInvoke.OleInitialize();
21+
22+
try
23+
{
24+
action();
25+
tcs.SetResult();
26+
}
27+
catch (Exception ex)
28+
{
29+
tcs.SetException(ex);
30+
}
31+
finally
32+
{
33+
PInvoke.OleUninitialize();
34+
}
35+
})
36+
{
37+
IsBackground = true,
38+
Priority = ThreadPriority.Normal
39+
};
40+
41+
thread.SetApartmentState(ApartmentState.STA);
42+
thread.Start();
43+
44+
return tcs.Task;
45+
}
46+
47+
public static Task<T> Run<T>(Func<T> func)
48+
{
49+
var tcs = new TaskCompletionSource<T>();
50+
51+
Thread thread =
52+
new(() =>
53+
{
54+
PInvoke.OleInitialize();
55+
56+
try
57+
{
58+
tcs.SetResult(func());
59+
}
60+
catch (Exception ex)
61+
{
62+
tcs.SetException(ex);
63+
}
64+
finally
65+
{
66+
PInvoke.OleUninitialize();
67+
}
68+
})
69+
{
70+
IsBackground = true,
71+
Priority = ThreadPriority.Normal
72+
};
73+
74+
thread.SetApartmentState(ApartmentState.STA);
75+
thread.Start();
76+
77+
return tcs.Task;
78+
}
79+
80+
public static Task Run(Func<Task> func)
81+
{
82+
var tcs = new TaskCompletionSource();
83+
84+
Thread thread =
85+
new(async () =>
86+
{
87+
PInvoke.OleInitialize();
88+
89+
try
90+
{
91+
await func();
92+
tcs.SetResult();
93+
}
94+
catch (Exception ex)
95+
{
96+
tcs.SetException(ex);
97+
}
98+
finally
99+
{
100+
PInvoke.OleUninitialize();
101+
}
102+
})
103+
{
104+
IsBackground = true,
105+
Priority = ThreadPriority.Normal
106+
};
107+
108+
thread.SetApartmentState(ApartmentState.STA);
109+
thread.Start();
110+
111+
return tcs.Task;
112+
}
113+
114+
public static Task<T?> Run<T>(Func<Task<T>> func)
115+
{
116+
var tcs = new TaskCompletionSource<T?>();
117+
118+
Thread thread =
119+
new(async () =>
120+
{
121+
PInvoke.OleInitialize();
122+
123+
try
124+
{
125+
tcs.SetResult(await func());
126+
}
127+
catch (Exception ex)
128+
{
129+
tcs.SetException(ex);
130+
}
131+
finally
132+
{
133+
PInvoke.OleUninitialize();
134+
}
135+
})
136+
{
137+
IsBackground = true,
138+
Priority = ThreadPriority.Normal
139+
};
140+
141+
thread.SetApartmentState(ApartmentState.STA);
142+
thread.Start();
143+
144+
return tcs.Task;
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)