{"record":{"id":"c47fe0c4b79dd823","repo":"Flow-Launcher/Flow.Launcher","slug":"shparsedisplayname-failed","errorCode":null,"errorMessage":"SHParseDisplayName failed","messagePattern":"SHParseDisplayName failed","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Plugins/Flow.Launcher.Plugin.Explorer/Helper/ShellContextMenuDisplayHelper.cs","lineNumber":186,"sourceCode":"        SHGetMalloc(out var pMalloc);\n        return (IMalloc)Marshal.GetTypedObjectForIUnknown(pMalloc, typeof(IMalloc));\n    }\n\n    public static void ExecuteContextMenuItem(string fileName, uint menuItemId)\n    {\n        IMalloc malloc = null;\n        IntPtr originalPidl = IntPtr.Zero;\n        IntPtr pShellFolder = IntPtr.Zero;\n        IntPtr pContextMenu = IntPtr.Zero;\n        IntPtr hMenu = IntPtr.Zero;\n        IContextMenu contextMenu = null;\n        IShellFolder shellFolder = null;\n\n        try\n        {\n            malloc = GetMalloc();\n            var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);\n            if (hr != 0) throw new Exception(\"SHParseDisplayName failed\");\n\n            originalPidl = pidl;\n\n            var guid = typeof(IShellFolder).GUID;\n            hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);\n            if (hr != 0) throw new Exception(\"SHBindToParent failed\");\n\n            shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));\n            hr = shellFolder.GetUIObjectOf(\n                IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu\n            );\n            if (hr != 0) throw new Exception(\"GetUIObjectOf failed\");\n\n            contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));\n\n            hMenu = CreatePopupMenu();\n            contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Explore);\n","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/Flow-Launcher/Flow.Launcher/blob/7fc63b07bbaa10a0f0b2601487913fcba9ed24b0/Plugins/Flow.Launcher.Plugin.Explorer/Helper/ShellContextMenuDisplayHelper.cs#L168-L204","documentation":"Thrown when the Win32 shell32 function SHParseDisplayName returns a non-zero HRESULT for the user-supplied file name. SHParseDisplayName converts a display name (e.g. an absolute path or shell namespace URL) into a PIDL that the rest of the Shell COM API consumes; a non-zero HRESULT means the Shell namespace could not resolve the name to an item. The exception intentionally discards the HRESULT, so the original failure code is lost. It is raised inside ExecuteContextMenuItem, which builds and invokes a Windows Explorer context menu for a file/folder.","triggerScenarios":"Calling ExecuteContextMenuItem(fileName, menuItemId) where fileName is null/empty, relative, uses forward slashes on a build where the Shell rejects them, points to a network/UNC path whose server is unreachable, references an item that has been deleted or moved since the result list was built, contains illegal characters, or is on a removable drive that has been ejected. SHParseDisplayName is also sensitive to long-path behavior and to per-user namespace extensions that may be unloaded.","commonSituations":"The file shown in Flow Launcher's result list is deleted by another process between the search and the right-click action; the user right-clicks a stale network share; the application runs under a context where the Desktop folder is not initialised; paths coming from a third-party search engine (Everything) exceed MAX_PATH and the long-path opt-in is off.","solutions":["Before calling ExecuteContextMenuItem, validate with File.Exists(fileName) || Directory.Exists(fileName) and surface a user-facing message instead of throwing.","Capture and propagate the HRESULT: throw new COMException(\"SHParseDisplayName failed\", hr) so callers can branch on E_INVALIDARG vs STG_E_FILENOTFOUND.","Normalise the path (Path.GetFullPath, replace '/' with '\\', expand environment variables) before invoking the helper.","Handle COMException/Exception at the call site and log fileName plus the HRESULT so future reports are actionable."],"exampleFix":"// before\nvar hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);\nif (hr != 0) throw new Exception(\"SHParseDisplayName failed\");\n\n// after\nvar hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);\nif (hr != 0) throw new COMException($\"SHParseDisplayName failed for '{fileName}'\", hr);","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(fileName) || fileName.Contains('/')) fileName = fileName.Replace('/', '\\\\');\nif (!File.Exists(fileName) && !Directory.Exists(fileName))\n    throw new FileNotFoundException($\"Shell item does not exist: {fileName}\", fileName);","typeGuard":"static bool IsResolvableShellPath(string path) => !string.IsNullOrWhiteSpace(path) && (File.Exists(path) || Directory.Exists(path) || path.StartsWith(\"::\"));","tryCatchPattern":"try { ShellContextMenuDisplayHelper.ExecuteContextMenuItem(fileName, menuItemId); }\ncatch (COMException ex) { logger.Error($\"Shell op failed: 0x{ex.ErrorCode:X} for {fileName}\"); }\ncatch (Exception ex) when (ex.Message == \"SHParseDisplayName failed\") { logger.Warn($\"Unresolvable path: {fileName}\"); }","preventionTips":["Validate the path exists before calling ExecuteContextMenuItem.","Always pass absolute, backslash-separated, environment-expanded paths.","Capture HRESULTs instead of throwing bare Exception so callers can branch.","Re-resolve paths immediately before invoking to avoid stale-result races."],"tags":["win32","shell-com","context-menu","pidl","csharp"],"backgroundTag":null,"analyzedSha":"7fc63b07bbaa10a0f0b2601487913fcba9ed24b0","analyzedAt":"2026-08-13T14:54:20.914Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}