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,25 +156,35 @@ 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;
IntPtr originalPidl = IntPtr.Zero;
IntPtr pShellFolder = IntPtr.Zero;
IntPtr pContextMenu = IntPtr.Zero;
IntPtr hMenu = IntPtr.Zero;
IContextMenu contextMenu = null;
IShellFolder shellFolder = null;
try
{
malloc = GetMalloc();
var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _); var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);
if (hr != 0) throw new Exception("SHParseDisplayName failed"); if (hr != 0) throw new Exception("SHParseDisplayName failed");
var originalPidl = pidl; originalPidl = pidl;
var guid = typeof(IShellFolder).GUID; var guid = typeof(IShellFolder).GUID;
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl); hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed"); if (hr != 0) throw new Exception("SHBindToParent failed");
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder)); shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf( hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
); );
if (hr != 0) throw new Exception("GetUIObjectOf failed"); if (hr != 0) throw new Exception("GetUIObjectOf failed");
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu)); contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
var hMenu = CreatePopupMenu(); hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal); contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
var directory = Path.GetDirectoryName(fileName); var directory = Path.GetDirectoryName(fileName);
@ -195,53 +205,93 @@ public static class ShellContextMenuDisplayHelper
{ {
throw new Exception($"InvokeCommand failed with code {hr:X}"); 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);
if (pContextMenu != IntPtr.Zero)
Marshal.Release(pContextMenu);
if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder); Marshal.ReleaseComObject(shellFolder);
if (originalPidl != IntPtr.Zero) if (pShellFolder != IntPtr.Zero)
malloc.Free(originalPidl); Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero)
malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc); Marshal.ReleaseComObject(malloc);
} }
}
public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath) public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath)
{ {
var malloc = GetMalloc(); IMalloc malloc = null;
IntPtr originalPidl = IntPtr.Zero;
IntPtr pShellFolder = IntPtr.Zero;
IShellFolder shellFolder = null;
IntPtr pContextMenu = IntPtr.Zero;
IContextMenu contextMenu = null;
IntPtr hMenu = IntPtr.Zero;
try
{
malloc = GetMalloc();
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _); var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _);
if (hr != 0) throw new Exception("SHParseDisplayName failed"); if (hr != 0) throw new Exception("SHParseDisplayName failed");
var originalPidl = pidl; originalPidl = pidl;
var guid = typeof(IShellFolder).GUID; var guid = typeof(IShellFolder).GUID;
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl); hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed"); if (hr != 0) throw new Exception("SHBindToParent failed");
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder)); shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf( hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
); );
if (hr != 0) throw new Exception("GetUIObjectOf failed"); if (hr != 0) throw new Exception("GetUIObjectOf failed");
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu)); contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
var hMenu = CreatePopupMenu(); hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal); contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
var menuItems = new List<ContextMenuItem>(); var menuItems = new List<ContextMenuItem>();
ProcessMenuWithIcons(hMenu, contextMenu, menuItems); ProcessMenuWithIcons(hMenu, contextMenu, menuItems);
return menuItems;
}
finally
{
if (hMenu != IntPtr.Zero)
DestroyMenu(hMenu); DestroyMenu(hMenu);
if (contextMenu != null)
Marshal.ReleaseComObject(contextMenu); Marshal.ReleaseComObject(contextMenu);
if (pContextMenu != IntPtr.Zero)
Marshal.Release(pContextMenu);
if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder); Marshal.ReleaseComObject(shellFolder);
if (pShellFolder != IntPtr.Zero)
Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero) if (originalPidl != IntPtr.Zero)
malloc.Free(originalPidl); malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc); Marshal.ReleaseComObject(malloc);
}
return menuItems;
} }