Skip to content

Commit ee64c25

Browse files
committed
Explorer plugin native context menu: free all allocated unmanaged memory
1 parent ce5a38b commit ee64c25

File tree

1 file changed

+112
-62
lines changed

1 file changed

+112
-62
lines changed

Plugins/Flow.Launcher.Plugin.Explorer/Helper/ShellContextMenuDisplayHelper.cs

Lines changed: 112 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -156,92 +156,142 @@ private static IMalloc GetMalloc()
156156

157157
public static void ExecuteContextMenuItem(string fileName, uint menuItemId)
158158
{
159-
var malloc = GetMalloc();
160-
var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);
161-
if (hr != 0) throw new Exception("SHParseDisplayName failed");
159+
IMalloc malloc = null;
160+
IntPtr originalPidl = IntPtr.Zero;
161+
IntPtr pShellFolder = IntPtr.Zero;
162+
IntPtr pContextMenu = IntPtr.Zero;
163+
IntPtr hMenu = IntPtr.Zero;
164+
IContextMenu contextMenu = null;
165+
IShellFolder shellFolder = null;
162166

163-
var originalPidl = pidl;
167+
try
168+
{
169+
malloc = GetMalloc();
170+
var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);
171+
if (hr != 0) throw new Exception("SHParseDisplayName failed");
164172

165-
var guid = typeof(IShellFolder).GUID;
166-
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl);
167-
if (hr != 0) throw new Exception("SHBindToParent failed");
173+
originalPidl = pidl;
168174

169-
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
170-
hr = shellFolder.GetUIObjectOf(
171-
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu
172-
);
173-
if (hr != 0) throw new Exception("GetUIObjectOf failed");
175+
var guid = typeof(IShellFolder).GUID;
176+
hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
177+
if (hr != 0) throw new Exception("SHBindToParent failed");
174178

175-
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
179+
shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
180+
hr = shellFolder.GetUIObjectOf(
181+
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
182+
);
183+
if (hr != 0) throw new Exception("GetUIObjectOf failed");
176184

177-
var hMenu = CreatePopupMenu();
178-
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
185+
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
179186

180-
var directory = Path.GetDirectoryName(fileName);
181-
var invokeCommandInfo = new CMINVOKECOMMANDINFO
182-
{
183-
cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO)),
184-
fMask = (uint)ContextMenuInvokeCommandFlags.Unicode,
185-
hwnd = IntPtr.Zero,
186-
lpVerb = (IntPtr)(menuItemId - ContextMenuStartId),
187-
lpParameters = null,
188-
lpDirectory = directory ?? "",
189-
nShow = 1,
190-
hIcon = IntPtr.Zero,
191-
};
192-
193-
hr = contextMenu.InvokeCommand(ref invokeCommandInfo);
194-
if (hr != 0)
195-
{
196-
throw new Exception($"InvokeCommand failed with code {hr:X}");
187+
hMenu = CreatePopupMenu();
188+
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
189+
190+
var directory = Path.GetDirectoryName(fileName);
191+
var invokeCommandInfo = new CMINVOKECOMMANDINFO
192+
{
193+
cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO)),
194+
fMask = (uint)ContextMenuInvokeCommandFlags.Unicode,
195+
hwnd = IntPtr.Zero,
196+
lpVerb = (IntPtr)(menuItemId - ContextMenuStartId),
197+
lpParameters = null,
198+
lpDirectory = directory ?? "",
199+
nShow = 1,
200+
hIcon = IntPtr.Zero,
201+
};
202+
203+
hr = contextMenu.InvokeCommand(ref invokeCommandInfo);
204+
if (hr != 0)
205+
{
206+
throw new Exception($"InvokeCommand failed with code {hr:X}");
207+
}
197208
}
209+
finally
210+
{
211+
if (hMenu != IntPtr.Zero)
212+
DestroyMenu(hMenu);
213+
214+
if (contextMenu != null)
215+
Marshal.ReleaseComObject(contextMenu);
198216

199-
DestroyMenu(hMenu);
200-
Marshal.ReleaseComObject(contextMenu);
201-
Marshal.ReleaseComObject(shellFolder);
217+
if (pContextMenu != IntPtr.Zero)
218+
Marshal.Release(pContextMenu);
202219

203-
if (originalPidl != IntPtr.Zero)
204-
malloc.Free(originalPidl);
220+
if (shellFolder != null)
221+
Marshal.ReleaseComObject(shellFolder);
205222

206-
Marshal.ReleaseComObject(malloc);
223+
if (pShellFolder != IntPtr.Zero)
224+
Marshal.Release(pShellFolder);
225+
226+
if (originalPidl != IntPtr.Zero)
227+
malloc?.Free(originalPidl);
228+
229+
if (malloc != null)
230+
Marshal.ReleaseComObject(malloc);
231+
}
207232
}
208233

209234
public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath)
210235
{
211-
var malloc = GetMalloc();
212-
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _);
213-
if (hr != 0) throw new Exception("SHParseDisplayName failed");
236+
IMalloc malloc = null;
237+
IntPtr originalPidl = IntPtr.Zero;
238+
IntPtr pShellFolder = IntPtr.Zero;
239+
IShellFolder shellFolder = null;
240+
IntPtr pContextMenu = IntPtr.Zero;
241+
IContextMenu contextMenu = null;
242+
IntPtr hMenu = IntPtr.Zero;
214243

215-
var originalPidl = pidl;
244+
try
245+
{
246+
malloc = GetMalloc();
247+
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _);
248+
if (hr != 0) throw new Exception("SHParseDisplayName failed");
216249

217-
var guid = typeof(IShellFolder).GUID;
218-
hr = SHBindToParent(pidl, guid, out var pShellFolder, ref pidl);
219-
if (hr != 0) throw new Exception("SHBindToParent failed");
250+
originalPidl = pidl;
220251

221-
var shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
222-
hr = shellFolder.GetUIObjectOf(
223-
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out var pContextMenu
224-
);
225-
if (hr != 0) throw new Exception("GetUIObjectOf failed");
252+
var guid = typeof(IShellFolder).GUID;
253+
hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
254+
if (hr != 0) throw new Exception("SHBindToParent failed");
226255

227-
var contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
256+
shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
257+
hr = shellFolder.GetUIObjectOf(
258+
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
259+
);
260+
if (hr != 0) throw new Exception("GetUIObjectOf failed");
228261

229-
var hMenu = CreatePopupMenu();
230-
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
262+
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
231263

232-
var menuItems = new List<ContextMenuItem>();
233-
ProcessMenuWithIcons(hMenu, contextMenu, menuItems);
264+
hMenu = CreatePopupMenu();
265+
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Normal);
234266

235-
DestroyMenu(hMenu);
236-
Marshal.ReleaseComObject(contextMenu);
237-
Marshal.ReleaseComObject(shellFolder);
267+
var menuItems = new List<ContextMenuItem>();
268+
ProcessMenuWithIcons(hMenu, contextMenu, menuItems);
238269

239-
if (originalPidl != IntPtr.Zero)
240-
malloc.Free(originalPidl);
270+
return menuItems;
271+
}
272+
finally
273+
{
274+
if (hMenu != IntPtr.Zero)
275+
DestroyMenu(hMenu);
276+
277+
if (contextMenu != null)
278+
Marshal.ReleaseComObject(contextMenu);
279+
280+
if (pContextMenu != IntPtr.Zero)
281+
Marshal.Release(pContextMenu);
241282

242-
Marshal.ReleaseComObject(malloc);
283+
if (shellFolder != null)
284+
Marshal.ReleaseComObject(shellFolder);
243285

244-
return menuItems;
286+
if (pShellFolder != IntPtr.Zero)
287+
Marshal.Release(pShellFolder);
288+
289+
if (originalPidl != IntPtr.Zero)
290+
malloc?.Free(originalPidl);
291+
292+
if (malloc != null)
293+
Marshal.ReleaseComObject(malloc);
294+
}
245295
}
246296

247297

0 commit comments

Comments
 (0)