Skip to content

Commit 52e7e70

Browse files
committed
Init
1 parent 6b5bf5b commit 52e7e70

File tree

16 files changed

+384
-143
lines changed

16 files changed

+384
-143
lines changed

src/Files.App.CsWin32/ManualGuid.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory
4141

4242
[GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")]
4343
public static partial Guid* IID_IQueryInfo { get; }
44+
45+
[GuidRVAGen.Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B")]
46+
public static partial Guid* IID_IShellItemImageFactory { get; }
4447
}
4548

4649
public static unsafe partial class CLSID

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,8 @@ QITIPF_FLAGS
225225
GetKeyboardState
226226
MapVirtualKey
227227
GetKeyboardLayout
228+
HKEY_CLASSES_ROOT
229+
HKEY_CURRENT_USER
230+
RegOpenKeyEx
231+
RegEnumKeyEx
232+
RegGetValue

src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Files.App.Storage.Storables
99
{
10-
public partial class HomeFolder : IHomeFolder
10+
public unsafe partial class HomeFolder : IHomeFolder
1111
{
1212
public string Id => "Home"; // Will be "files://Home" in the future.
1313

@@ -48,38 +48,36 @@ public IAsyncEnumerable<IStorableChild> GetQuickAccessFolderAsync(CancellationTo
4848
/// <inheritdoc/>
4949
public IAsyncEnumerable<IStorableChild> GetLogicalDrivesAsync(CancellationToken cancellationToken = default)
5050
{
51-
return GetLogicalDrives().ToAsyncEnumerable();
51+
var availableDrives = PInvoke.GetLogicalDrives();
52+
if (availableDrives is 0)
53+
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();
5254

53-
IEnumerable<IStorableChild> GetLogicalDrives()
54-
{
55-
var availableDrives = PInvoke.GetLogicalDrives();
56-
if (availableDrives is 0)
57-
yield break;
58-
59-
int count = BitOperations.PopCount(availableDrives);
60-
var driveLetters = new char[count];
55+
int count = BitOperations.PopCount(availableDrives);
56+
var driveLetters = new char[count];
6157

62-
count = 0;
63-
char driveLetter = 'A';
64-
while (availableDrives is not 0)
65-
{
66-
if ((availableDrives & 1) is not 0)
67-
driveLetters[count++] = driveLetter;
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;
6864

69-
availableDrives >>= 1;
70-
driveLetter++;
71-
}
65+
availableDrives >>= 1;
66+
driveLetter++;
67+
}
7268

73-
foreach (char letter in driveLetters)
74-
{
75-
cancellationToken.ThrowIfCancellationRequested();
69+
List<IStorableChild> items = [];
70+
foreach (char letter in driveLetters)
71+
{
72+
cancellationToken.ThrowIfCancellationRequested();
7673

77-
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
78-
throw new InvalidOperationException();
74+
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
75+
throw new InvalidOperationException();
7976

80-
yield return new WindowsFolder(driveRoot.ThisPtr);
81-
}
77+
items.Add(new WindowsFolder(driveRoot.ThisPtr));
8278
}
79+
80+
return items.ToAsyncEnumerable();
8381
}
8482

8583
/// <inheritdoc/>
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using Windows.Win32;
54
using Windows.Win32.UI.Shell;
65

76
namespace Files.App.Storage
87
{
9-
public interface IWindowsStorable : IDisposable
8+
public unsafe interface IWindowsStorable : IDisposable
109
{
11-
ComPtr<IShellItem> ThisPtr { get; }
10+
IShellItem* ThisPtr { get; }
1211
}
1312
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage
5+
{
6+
public partial class ShellNewItem
7+
{
8+
}
9+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Win32;
5+
using Windows.Win32;
6+
using Windows.Win32.Foundation;
7+
using Windows.Win32.System.Registry;
8+
9+
namespace Files.App.Storage
10+
{
11+
public static partial class ShellNewManager
12+
{
13+
public const string CachedShellNewItemsRegistryPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Discardable\\PostSetup\\ShellNew";
14+
15+
public static IEnumerable<ShellNewItem> GetItems()
16+
{
17+
var cachedItems = GetItemsFromCache();
18+
if (cachedItems.Any())
19+
{
20+
foreach (var item in cachedItems)
21+
yield return item;
22+
23+
yield break;
24+
}
25+
26+
foreach (var item in GetItemsFromAssoc())
27+
yield return item;
28+
}
29+
30+
private static IEnumerable<ShellNewItem> GetItemsFromCache()
31+
{
32+
bool fRes = WindowsStorableHelpers.GetRegistryValue(HKEY.HKEY_CURRENT_USER, CachedShellNewItemsRegistryPath, "Classes", REG_ROUTINE_FLAGS.RRF_RT_REG_MULTI_SZ, out string? cachedExtensions);
33+
if (!fRes)
34+
{
35+
yield break;
36+
}
37+
38+
// TODO: Access each cached extension and get the ShellNewItem properties
39+
}
40+
41+
private static IEnumerable<ShellNewItem> GetItemsFromAssoc()
42+
{
43+
var newMenuItems = new List<ShellNewItem>();
44+
string[] extensionsExcluded = [".contact", ".library-ms", ".url", ".lnk"];
45+
46+
// Get a handle of HKEY_CLASSES_ROOT
47+
bool fRes = WindowsStorableHelpers.OpenRegistryKey(HKEY.HKEY_CLASSES_ROOT, string.Empty, REG_SAM_FLAGS.KEY_READ, out HKEY hKey);
48+
if (!fRes)
49+
{
50+
if (!hKey.IsNull) PInvoke.RegCloseKey(hKey);
51+
yield break;
52+
}
53+
54+
foreach (string keyName in WindowsStorableHelpers.EnumerateRegistryKeyNames(hKey).Where(x => x.StartsWith('.') &&
55+
!extensionsExcluded.Contains(x, StringComparer.OrdinalIgnoreCase)))
56+
{
57+
fRes = WindowsStorableHelpers.OpenRegistryKey(HKEY.HKEY_CLASSES_ROOT, keyName, REG_SAM_FLAGS.KEY_READ, out HKEY hSubKey);
58+
if (!fRes)
59+
{
60+
if (!hKey.IsNull) PInvoke.RegCloseKey(hKey);
61+
if (!hSubKey.IsNull) PInvoke.RegCloseKey(hSubKey);
62+
yield break;
63+
}
64+
65+
// TODO: Get an entry
66+
yield return new();
67+
}
68+
69+
// TODO: Add .txt if necessary
70+
71+
if (!hKey.IsNull) PInvoke.RegCloseKey(hKey);
72+
73+
}
74+
}
75+
}

src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public unsafe WindowsBulkOperations(HWND ownerHWnd = default, FILEOPERATION_FLAG
101101
public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? copyName)
102102
{
103103
fixed (char* pszCopyName = copyName)
104-
return _pFileOperation.Get()->CopyItem(targetItem.ThisPtr.Get(), destinationFolder.ThisPtr.Get(), pszCopyName, _pProgressSink.Get());
104+
return _pFileOperation.Get()->CopyItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszCopyName, _pProgressSink.Get());
105105
}
106106

107107
/// <summary>
@@ -111,7 +111,7 @@ public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFold
111111
/// <returns>If this method succeeds, it returns <see cref="HRESULT.S_OK"/>. Otherwise, it returns an <see cref="HRESULT"/> error code.</returns>
112112
public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem)
113113
{
114-
return _pFileOperation.Get()->DeleteItem(targetItem.ThisPtr.Get(), _pProgressSink.Get());
114+
return _pFileOperation.Get()->DeleteItem(targetItem.ThisPtr, _pProgressSink.Get());
115115
}
116116

117117
/// <summary>
@@ -124,7 +124,7 @@ public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem)
124124
public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? newName)
125125
{
126126
fixed (char* pszNewName = newName)
127-
return _pFileOperation.Get()->MoveItem(targetItem.ThisPtr.Get(), destinationFolder.ThisPtr.Get(), pszNewName, null);
127+
return _pFileOperation.Get()->MoveItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszNewName, null);
128128
}
129129

130130
/// <summary>
@@ -138,7 +138,7 @@ public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFold
138138
public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE_FLAGS_AND_ATTRIBUTES fileAttributes, string name, string? templateName)
139139
{
140140
fixed (char* pszName = name, pszTemplateName = templateName)
141-
return _pFileOperation.Get()->NewItem(destinationFolder.ThisPtr.Get(), (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink.Get());
141+
return _pFileOperation.Get()->NewItem(destinationFolder.ThisPtr, (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink.Get());
142142
}
143143

144144
/// <summary>
@@ -150,7 +150,7 @@ public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE
150150
public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string newName)
151151
{
152152
fixed (char* pszNewName = newName)
153-
return _pFileOperation.Get()->RenameItem(targetItem.ThisPtr.Get(), pszNewName, _pProgressSink.Get());
153+
return _pFileOperation.Get()->RenameItem(targetItem.ThisPtr, pszNewName, _pProgressSink.Get());
154154
}
155155

156156
/// <summary>

src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
namespace Files.App.Storage
99
{
1010
[DebuggerDisplay("{" + nameof(ToString) + "()}")]
11-
public sealed class WindowsFile : WindowsStorable, IChildFile
11+
public unsafe sealed class WindowsFile : WindowsStorable, IChildFile
1212
{
13-
public WindowsFile(ComPtr<IShellItem> nativeObject)
13+
public WindowsFile(IShellItem* ptr)
1414
{
15-
ThisPtr = nativeObject;
15+
ThisPtr = ptr;
1616
}
1717

1818
public Task<Stream> OpenStreamAsync(FileAccess accessMode, CancellationToken cancellationToken = default)
Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using System.Runtime.CompilerServices;
54
using Windows.Win32;
65
using Windows.Win32.Foundation;
76
using Windows.Win32.System.SystemServices;
@@ -10,29 +9,22 @@
109
namespace Files.App.Storage
1110
{
1211
[DebuggerDisplay("{" + nameof(ToString) + "()}")]
13-
public sealed class WindowsFolder : WindowsStorable, IChildFolder
12+
public unsafe sealed class WindowsFolder : WindowsStorable, IChildFolder
1413
{
15-
public WindowsFolder(ComPtr<IShellItem> nativeObject)
14+
public WindowsFolder(IShellItem* ptr)
1615
{
17-
ThisPtr = nativeObject;
18-
}
19-
20-
public unsafe WindowsFolder(IShellItem* nativeObject)
21-
{
22-
ComPtr<IShellItem> ptr = default;
23-
ptr.Attach(nativeObject);
2416
ThisPtr = ptr;
2517
}
2618

27-
public unsafe WindowsFolder(Guid folderId)
19+
public WindowsFolder(Guid folderId)
2820
{
29-
ComPtr<IShellItem> pItem = default;
21+
IShellItem* pItem = default;
3022

31-
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pItem.GetAddressOf());
23+
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)&pItem);
3224
if (hr.Failed)
3325
{
3426
fixed (char* pszShellPath = $"Shell:::{folderId:B}")
35-
hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)pItem.GetAddressOf());
27+
hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)&pItem);
3628

3729
// Invalid FOLDERID; this should never happen.
3830
hr.ThrowOnFailure();
@@ -43,47 +35,35 @@ public unsafe WindowsFolder(Guid folderId)
4335

4436
public IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type = StorableType.All, CancellationToken cancellationToken = default)
4537
{
46-
return GetItems().ToAsyncEnumerable();
38+
ComPtr<IEnumShellItems> pEnumShellItems = default;
39+
HRESULT hr = ThisPtr->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf());
40+
if (hr.ThrowIfFailedOnDebug().Failed)
41+
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();
4742

48-
unsafe IEnumerable<IStorableChild> GetItems()
43+
List<IStorableChild> items = [];
44+
IShellItem* pShellItem = default;
45+
while (pEnumShellItems.Get()->Next(1, &pShellItem).Succeeded && pShellItem is not null)
4946
{
50-
ComPtr<IEnumShellItems> pEnumShellItems = default;
51-
GetEnumerator();
47+
cancellationToken.ThrowIfCancellationRequested();
5248

53-
ComPtr<IShellItem> pShellItem = default;
54-
while (GetNext() && !pShellItem.IsNull)
55-
{
56-
cancellationToken.ThrowIfCancellationRequested();
57-
var isFolder = pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER);
49+
var isFolder = pShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded &&
50+
returnedAttributes == SFGAO_FLAGS.SFGAO_FOLDER;
5851

59-
if (type is StorableType.File && !isFolder)
60-
{
61-
yield return new WindowsFile(pShellItem);
62-
}
63-
else if (type is StorableType.Folder && isFolder)
64-
{
65-
yield return new WindowsFolder(pShellItem);
66-
}
67-
else
68-
{
69-
continue;
70-
}
52+
if (type is StorableType.File && !isFolder)
53+
{
54+
items.Add(new WindowsFile(pShellItem));
7155
}
72-
73-
yield break;
74-
75-
unsafe void GetEnumerator()
56+
else if (type is StorableType.Folder && isFolder)
7657
{
77-
HRESULT hr = ThisPtr.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf());
78-
hr.ThrowIfFailedOnDebug();
58+
items.Add(new WindowsFolder(pShellItem));
7959
}
80-
81-
unsafe bool GetNext()
60+
else
8261
{
83-
HRESULT hr = pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf());
84-
return hr.ThrowIfFailedOnDebug() == HRESULT.S_OK;
62+
continue;
8563
}
8664
}
65+
66+
return items.ToAsyncEnumerable();
8767
}
8868
}
8969
}

0 commit comments

Comments
 (0)