Skip to content

Commit 29f3b23

Browse files
committed
Confirmed working.
1 parent c78e87d commit 29f3b23

File tree

3 files changed

+108
-43
lines changed

3 files changed

+108
-43
lines changed

src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -204,72 +204,69 @@ public static HRESULT TryUnpinFolderFromQuickAccess(this IWindowsFolder @this)
204204
return HRESULT.S_OK;
205205
}
206206

207-
public static IEnumerable<WindowsContextMenuItem> GetShellNewItems(this IWindowsFolder @this)
207+
public static IEnumerable<WindowsContextMenuItem>? GetShellNewMenuItems(this IWindowsFolder @this)
208208
{
209209
HRESULT hr = default;
210210

211211
IContextMenu* pNewMenu = default;
212212
using ComPtr<IShellExtInit> pShellExtInit = default;
213213
using ComPtr<IContextMenu2> pContextMenu2 = default;
214+
using ComHeapPtr<ITEMIDLIST> pShellFolderPidl = default;
214215

215216
hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&pNewMenu);
216-
if (hr.ThrowIfFailedOnDebug().Failed)
217-
return [];
217+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
218218

219219
hr = pNewMenu->QueryInterface(IID.IID_IContextMenu2, (void**)pContextMenu2.GetAddressOf());
220-
if (hr.ThrowIfFailedOnDebug().Failed)
221-
return [];
220+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
222221

223222
hr = pNewMenu->QueryInterface(IID.IID_IShellExtInit, (void**)pShellExtInit.GetAddressOf());
224-
if (hr.ThrowIfFailedOnDebug().Failed)
225-
return [];
223+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
226224

227225
@this.ShellNewMenu = pNewMenu;
228226

229-
ITEMIDLIST* pFolderPidl = default;
230-
hr = PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, &pFolderPidl);
231-
if (hr.ThrowIfFailedOnDebug().Failed)
232-
return [];
227+
hr = PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, pShellFolderPidl.GetAddressOf());
228+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
233229

234-
hr = pShellExtInit.Get()->Initialize(pFolderPidl, null, default);
235-
if (hr.ThrowIfFailedOnDebug().Failed)
236-
return [];
230+
hr = pShellExtInit.Get()->Initialize(pShellFolderPidl.Get(), null, default);
231+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
237232

238233
// Inserts "New (&W)"
239234
HMENU hMenu = PInvoke.CreatePopupMenu();
240235
hr = pNewMenu->QueryContextMenu(hMenu, 0, 1, 256, 0);
241-
if (hr.ThrowIfFailedOnDebug().Failed)
242-
return [];
236+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
243237

244-
// Invokes CNewMenu::_InitMenuPopup(), which populates the hSubMenu
238+
// Populates the hSubMenu
245239
HMENU hSubMenu = PInvoke.GetSubMenu(hMenu, 0);
246240
hr = pContextMenu2.Get()->HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0);
247-
if (hr.ThrowIfFailedOnDebug().Failed)
248-
return [];
241+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
249242

250243
uint dwCount = unchecked((uint)PInvoke.GetMenuItemCount(hSubMenu));
251-
if (dwCount is unchecked((uint)-1))
252-
return [];
244+
if (dwCount is unchecked((uint)-1)) return null;
253245

254-
// Enumerates and populates the list
255-
List<WindowsContextMenuItem> shellNewItems = [];
246+
// Enumerates the menu items
247+
List<WindowsContextMenuItem> items = [];
256248
for (uint dwIndex = 0; dwIndex < dwCount; dwIndex++)
257249
{
258250
MENUITEMINFOW mii = default;
259251
mii.cbSize = (uint)sizeof(MENUITEMINFOW);
260-
mii.fMask = MENU_ITEM_MASK.MIIM_STRING | MENU_ITEM_MASK.MIIM_ID | MENU_ITEM_MASK.MIIM_STATE | MENU_ITEM_MASK.MIIM_TYPE;
252+
mii.fMask = MENU_ITEM_MASK.MIIM_STRING | MENU_ITEM_MASK.MIIM_ID | MENU_ITEM_MASK.MIIM_STATE | MENU_ITEM_MASK.MIIM_FTYPE;
261253
mii.dwTypeData = (char*)NativeMemory.Alloc(256U);
262254
mii.cch = 256;
263255

264256
if (PInvoke.GetMenuItemInfo(hSubMenu, dwIndex, true, &mii))
265257
{
266-
shellNewItems.Add(new(mii.wID, new(mii.dwTypeData), null, (WindowsContextMenuType)mii.fType, (WindowsContextMenuState)mii.fState));
258+
byte[]? rawImageData = null;
259+
GpBitmap* gpBitmap = ConvertHBITMAPToGpBitmap(mii.hbmpItem);
260+
if (gpBitmap is not null)
261+
rawImageData = ConvertGpBitmapToByteArray(gpBitmap);
262+
263+
items.Add(new(mii.wID, new(mii.dwTypeData), rawImageData, (WindowsContextMenuType)mii.fType, (WindowsContextMenuState)mii.fState));
267264
}
268265

269266
NativeMemory.Free(mii.dwTypeData);
270267
}
271268

272-
return shellNewItems;
269+
return items;
273270
}
274271

275272
public static bool InvokeShellNewItem(this IWindowsFolder @this, WindowsContextMenuItem item)
@@ -299,7 +296,7 @@ public static bool InvokeShellNewItem(this IWindowsFolder @this, WindowsContextM
299296
return false;
300297
}
301298

302-
public static IEnumerable<WindowsContextMenuItem> GetOpenWithMenuItems(this IWindowsFile @this)
299+
public static IEnumerable<WindowsContextMenuItem>? GetOpenWithMenuItems(this IWindowsFile @this)
303300
{
304301
HRESULT hr = default;
305302

@@ -312,37 +309,46 @@ public static IEnumerable<WindowsContextMenuItem> GetOpenWithMenuItems(this IWin
312309
using ComHeapPtr<ITEMIDLIST> pThisAbsolutePidl = default;
313310
ComHeapPtr<ITEMIDLIST> pThisRelativePidl = default;
314311

315-
PInvoke.CoCreateInstance(CLSID.CLSID_OpenWithMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)pOpenWithContextMenu.GetAddressOf());
312+
hr = PInvoke.CoCreateInstance(CLSID.CLSID_OpenWithMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)pOpenWithContextMenu.GetAddressOf());
313+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
314+
316315
hr = pOpenWithContextMenu.Get()->QueryInterface(IID.IID_IShellExtInit, (void**)pShellExtInit.GetAddressOf());
316+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
317+
317318
hr = pOpenWithContextMenu.Get()->QueryInterface(IID.IID_IContextMenu2, (void**)pOpenWithContextMenu2.GetAddressOf());
319+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
318320

319321
// Get the absolute PIDL of the parent folder
320322
@this.ThisPtr->GetParent(pParentFolderShellItem.GetAddressOf());
321-
PInvoke.SHGetIDListFromObject((IUnknown*)pParentFolderShellItem.Get(), pParentAbsolutePidl.GetAddressOf());
323+
hr = PInvoke.SHGetIDListFromObject((IUnknown*)pParentFolderShellItem.Get(), pParentAbsolutePidl.GetAddressOf());
324+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
322325

323326
// Get the relative PIDL of the current item
324-
PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, pThisAbsolutePidl.GetAddressOf());
327+
hr = PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, pThisAbsolutePidl.GetAddressOf());
328+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
325329
pThisRelativePidl.Attach(PInvoke.ILFindLastID(pThisAbsolutePidl.Get()));
326330

327331
hr = PInvoke.SHCreateDataObject(pParentAbsolutePidl.Get(), 1U, pThisRelativePidl.GetAddressOf(), null, IID.IID_IDataObject, (void**)pDataObject.GetAddressOf());
332+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
328333

329-
// The 2nd parameter must not be null, others aren't used.
330334
hr = pShellExtInit.Get()->Initialize(null, pDataObject.Get(), HKEY.Null);
335+
if (hr.ThrowIfFailedOnDebug().Failed) return [];
331336

332-
// Inserts "New (&W)"
337+
// Inserts "Open With(&H)" or "Open With(&H)..."
333338
HMENU hMenu = PInvoke.CreatePopupMenu();
334339
hr = pOpenWithContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 256, 0);
340+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
335341

336-
// Invokes CNewMenu::_InitMenuPopup(), which populates the hSubMenu
342+
// Populates the hSubMenu
337343
HMENU hSubMenu = PInvoke.GetSubMenu(hMenu, 0);
338344
hr = pOpenWithContextMenu2.Get()->HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0);
345+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
339346

340347
uint dwCount = unchecked((uint)PInvoke.GetMenuItemCount(hSubMenu));
341-
if (dwCount is unchecked((uint)-1)) return [];
348+
if (dwCount is unchecked((uint)-1)) return null;
342349

350+
// Enumerates the menu items
343351
List<WindowsContextMenuItem> items = [];
344-
345-
// Enumerates and populates the list
346352
for (uint dwIndex = 0U; dwIndex < dwCount; dwIndex++)
347353
{
348354
MENUITEMINFOW mii = default;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
// System
5+
global using global::System;
6+
global using global::System.Collections;
7+
global using global::System.Collections.Generic;
8+
global using global::System.Collections.ObjectModel;
9+
global using global::System.Linq;
10+
global using global::System.Threading;
11+
global using global::System.Threading.Tasks;
12+
global using global::System.ComponentModel;
13+
global using global::System.Diagnostics;
14+
global using global::System.Text.Json;
15+
global using global::System.Text.Json.Serialization;
16+
global using SystemIO = global::System.IO;
17+
18+
// Microsoft.VisualStudio.TestTools
19+
20+
global using global::Microsoft.VisualStudio.TestTools.UnitTesting;
21+
global using global::Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
22+
23+
24+
// Files.Core.Storage
25+
26+
global using global::Files.Core.Storage;
27+
global using global::Files.Core.Storage.Contracts;
28+
global using global::Files.Core.Storage.Storables;
29+
global using global::Files.Core.Storage.Enums;
30+
global using global::Files.Core.Storage.EventArguments;
31+
global using global::Files.Core.Storage.Extensions;
32+
global using global::OwlCore.Storage;
33+
34+
// Files.App.Storage
35+
36+
global using global::Files.App.Storage;
37+
global using global::Files.App.Storage.Storables;
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using Files.App.Storage;
5-
using Microsoft.UI.Xaml.Controls;
6-
using Microsoft.VisualStudio.TestTools.UnitTesting;
7-
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
8-
using System.Linq;
9-
104
namespace Files.App.UnitTests
115
{
126
[TestClass]
137
public class WindowsStorageTests
148
{
9+
[TestMethod]
10+
public void Test_WindowsStorageTests_GetShellNewItems()
11+
{
12+
var folder = WindowsStorable.TryParse("C:\\Windows") as WindowsFolder;
13+
14+
Assert.IsNotNull(folder);
15+
16+
var items = folder.GetShellNewMenuItems();
17+
18+
Assert.IsNotNull(items);
19+
20+
foreach (var item in items)
21+
{
22+
Assert.IsNotNull(item.Name);
23+
24+
if (item.State is not WindowsContextMenuState.Disabled && item.Type is WindowsContextMenuType.Bitmap)
25+
Assert.IsNotNull(item.Icon);
26+
}
27+
}
28+
1529
[TestMethod]
1630
public void Test_WindowsStorageTests_GetOpenWithMenuItems()
1731
{
@@ -21,7 +35,15 @@ public void Test_WindowsStorageTests_GetOpenWithMenuItems()
2135

2236
var items = file.GetOpenWithMenuItems();
2337

24-
Assert.AreNotEqual(0, items.Count());
38+
Assert.IsNotNull(items);
39+
40+
foreach (var item in items)
41+
{
42+
Assert.IsNotNull(item.Name);
43+
44+
if (item.State is not WindowsContextMenuState.Disabled && item.Type is WindowsContextMenuType.Bitmap)
45+
Assert.IsNotNull(item.Icon);
46+
}
2547
}
2648
}
2749
}

0 commit comments

Comments
 (0)