Explorer plugin native context menu: free all allocated unmanaged memory

This commit is contained in:
Yusyuriv 2024-06-07 21:29:04 +06:00
parent ce5a38bc49
commit ee64c259fc
No known key found for this signature in database
GPG key ID: A91C52E6F73148E0

View file

@ -156,92 +156,142 @@ public static class ShellContextMenuDisplayHelper
public static void ExecuteContextMenuItem(string fileName, uint menuItemId) public static void ExecuteContextMenuItem(string fileName, uint menuItemId)
{ {
var malloc = GetMalloc(); IMalloc malloc = null;
var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _); IntPtr originalPidl = IntPtr.Zero;
if (hr != 0) throw new Exception("SHParseDisplayName failed"); IntPtr pShellFolder = IntPtr.Zero;
IntPtr pContextMenu = IntPtr.Zero;
IntPtr hMenu = IntPtr.Zero;
IContextMenu contextMenu = null;
IShellFolder shellFolder = null;
var originalPidl = pidl; try
var guid = typeof(IShellFolder).GUID;
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed");
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
var hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
var directory = Path.GetDirectoryName(fileName);
var invokeCommandInfo = new CMINVOKECOMMANDINFO
{ {
cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO)), malloc = GetMalloc();
fMask = (uint)ContextMenuInvokeCommandFlags.Unicode, var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);
hwnd = IntPtr.Zero, if (hr != 0) throw new Exception("SHParseDisplayName failed");
lpVerb = (IntPtr)(menuItemId - ContextMenuStartId),
lpParameters = null,
lpDirectory = directory ?? "",
nShow = 1,
hIcon = IntPtr.Zero,
};
hr = contextMenu.InvokeCommand(ref invokeCommandInfo); originalPidl = pidl;
if (hr != 0)
{ var guid = typeof(IShellFolder).GUID;
throw new Exception($"InvokeCommand failed with code {hr:X}"); hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed");
shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
var directory = Path.GetDirectoryName(fileName);
var invokeCommandInfo = new CMINVOKECOMMANDINFO
{
cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO)),
fMask = (uint)ContextMenuInvokeCommandFlags.Unicode,
hwnd = IntPtr.Zero,
lpVerb = (IntPtr)(menuItemId - ContextMenuStartId),
lpParameters = null,
lpDirectory = directory ?? "",
nShow = 1,
hIcon = IntPtr.Zero,
};
hr = contextMenu.InvokeCommand(ref invokeCommandInfo);
if (hr != 0)
{
throw new Exception($"InvokeCommand failed with code {hr:X}");
}
} }
finally
{
if (hMenu != IntPtr.Zero)
DestroyMenu(hMenu);
DestroyMenu(hMenu); if (contextMenu != null)
Marshal.ReleaseComObject(contextMenu); Marshal.ReleaseComObject(contextMenu);
Marshal.ReleaseComObject(shellFolder);
if (originalPidl != IntPtr.Zero) if (pContextMenu != IntPtr.Zero)
malloc.Free(originalPidl); Marshal.Release(pContextMenu);
Marshal.ReleaseComObject(malloc); if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder);
if (pShellFolder != IntPtr.Zero)
Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero)
malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc);
}
} }
public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath) public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath)
{ {
var malloc = GetMalloc(); IMalloc malloc = null;
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _); IntPtr originalPidl = IntPtr.Zero;
if (hr != 0) throw new Exception("SHParseDisplayName failed"); IntPtr pShellFolder = IntPtr.Zero;
IShellFolder shellFolder = null;
IntPtr pContextMenu = IntPtr.Zero;
IContextMenu contextMenu = null;
IntPtr hMenu = IntPtr.Zero;
var originalPidl = pidl; try
{
malloc = GetMalloc();
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _);
if (hr != 0) throw new Exception("SHParseDisplayName failed");
var guid = typeof(IShellFolder).GUID; originalPidl = pidl;
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed");
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder)); var guid = typeof(IShellFolder).GUID;
hr = shellFolder.GetUIObjectOf( hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu if (hr != 0) throw new Exception("SHBindToParent failed");
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu)); shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
var hMenu = CreatePopupMenu(); contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
var menuItems = new List<ContextMenuItem>(); hMenu = CreatePopupMenu();
ProcessMenuWithIcons(hMenu, contextMenu, menuItems); contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
DestroyMenu(hMenu); var menuItems = new List<ContextMenuItem>();
Marshal.ReleaseComObject(contextMenu); ProcessMenuWithIcons(hMenu, contextMenu, menuItems);
Marshal.ReleaseComObject(shellFolder);
if (originalPidl != IntPtr.Zero) return menuItems;
malloc.Free(originalPidl); }
finally
{
if (hMenu != IntPtr.Zero)
DestroyMenu(hMenu);
Marshal.ReleaseComObject(malloc); if (contextMenu != null)
Marshal.ReleaseComObject(contextMenu);
return menuItems; if (pContextMenu != IntPtr.Zero)
Marshal.Release(pContextMenu);
if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder);
if (pShellFolder != IntPtr.Zero)
Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero)
malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc);
}
} }