コード例 #1
0
        private object HandleMenuMessage(Dictionary <string, object> message, DisposableDictionary table)
        {
            switch (message.Get("Arguments", ""))
            {
            case "LoadContextMenu":
                var contextMenuResponse = new ValueSet();
                var filePath            = (string)message["FilePath"];
                var extendedMenu        = (bool)message["ExtendedMenu"];
                var showOpenMenu        = (bool)message["ShowOpenMenu"];
                var split     = filePath.Split('|').Where(x => !string.IsNullOrWhiteSpace(x));
                var cMenuLoad = ContextMenu.GetContextMenuForFiles(split.ToArray(),
                                                                   (extendedMenu ? Shell32.CMF.CMF_EXTENDEDVERBS : Shell32.CMF.CMF_NORMAL) | Shell32.CMF.CMF_SYNCCASCADEMENU, FilterMenuItems(showOpenMenu));
                table.SetValue("MENU", cMenuLoad);
                return(cMenuLoad);

            case "ExecAndCloseContextMenu":
                var cMenuExec = table.GetValue <ContextMenu>("MENU");
                if (message.TryGetValue("ItemID", out var menuId))
                {
                    switch (message.Get("CommandString", (string)null))
                    {
                    case "mount":
                        var vhdPath = cMenuExec.ItemsPath.First();
                        Win32API.MountVhdDisk(vhdPath);
                        break;

                    case "format":
                        var drivePath = cMenuExec.ItemsPath.First();
                        Win32API.OpenFormatDriveDialog(drivePath);
                        break;

                    default:
                        cMenuExec?.InvokeItem((int)(long)menuId);
                        break;
                    }
                }
                // The following line is needed to cleanup resources when menu is closed.
                // Unfortunately if you uncomment it some menu items will randomly stop working.
                // Resource cleanup is currently done on app closing,
                // if we find a solution for the issue above, we should cleanup as soon as a menu is closed.
                //table.RemoveValue("MENU");
                return(null);

            default:
                return(null);
            }
        }
コード例 #2
0
        public async Task ParseArgumentsAsync(NamedPipeServerStream connection, Dictionary <string, object> message, string arguments)
        {
            switch (arguments)
            {
            case "LoadContextMenu":
                var contextMenuResponse        = new ValueSet();
                var loadThreadWithMessageQueue = new ThreadWithMessageQueue <Dictionary <string, object> >(HandleMenuMessage);
                var cMenuLoad = await loadThreadWithMessageQueue.PostMessageAsync <ContextMenu>(message);

                contextMenuResponse.Add("Handle", handleTable.AddValue(loadThreadWithMessageQueue));
                contextMenuResponse.Add("ContextMenu", JsonConvert.SerializeObject(cMenuLoad));
                var serializedCm = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(contextMenuResponse));
                await Win32API.SendMessageAsync(connection, contextMenuResponse, message.Get("RequestID", (string)null));

                break;

            case "ExecAndCloseContextMenu":
                var menuKey = (string)message["Handle"];
                var execThreadWithMessageQueue = handleTable.GetValue <ThreadWithMessageQueue <Dictionary <string, object> > >(menuKey);
                if (execThreadWithMessageQueue != null)
                {
                    await execThreadWithMessageQueue.PostMessage(message);
                }
                // The following line is needed to cleanup resources when menu is closed.
                // Unfortunately if you uncomment it some menu items will randomly stop working.
                // Resource cleanup is currently done on app closing,
                // if we find a solution for the issue above, we should cleanup as soon as a menu is closed.
                //handleTable.RemoveValue(menuKey);
                break;

            case "InvokeVerb":
                var filePath = (string)message["FilePath"];
                var split    = filePath.Split('|').Where(x => !string.IsNullOrWhiteSpace(x));
                var verb     = (string)message["Verb"];
                using (var cMenu = ContextMenu.GetContextMenuForFiles(split.ToArray(), Shell32.CMF.CMF_DEFAULTONLY))
                {
                    var result = cMenu?.InvokeVerb(verb);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", result }
                    }, message.Get("RequestID", (string)null));
                }
                break;
            }
        }
コード例 #3
0
        private async Task ParseFileOperationAsync(NamedPipeServerStream connection, Dictionary <string, object> message)
        {
            switch (message.Get("fileop", ""))
            {
            case "Clipboard":
                await Win32API.StartSTATask(() =>
                {
                    System.Windows.Forms.Clipboard.Clear();
                    var fileToCopy = (string)message["filepath"];
                    var operation  = (DataPackageOperation)(long)message["operation"];
                    var fileList   = new System.Collections.Specialized.StringCollection();
                    fileList.AddRange(fileToCopy.Split('|'));
                    if (operation == DataPackageOperation.Copy)
                    {
                        System.Windows.Forms.Clipboard.SetFileDropList(fileList);
                    }
                    else if (operation == DataPackageOperation.Move)
                    {
                        byte[] moveEffect       = new byte[] { 2, 0, 0, 0 };
                        MemoryStream dropEffect = new MemoryStream();
                        dropEffect.Write(moveEffect, 0, moveEffect.Length);
                        var data = new System.Windows.Forms.DataObject();
                        data.SetFileDropList(fileList);
                        data.SetData("Preferred DropEffect", dropEffect);
                        System.Windows.Forms.Clipboard.SetDataObject(data, true);
                    }
                    return(true);
                });

                break;

            case "DragDrop":
                var dropPath = (string)message["droppath"];
                var result   = await Win32API.StartSTATask(() =>
                {
                    var rdo = new RemoteDataObject(System.Windows.Forms.Clipboard.GetDataObject());

                    foreach (RemoteDataObject.DataPackage package in rdo.GetRemoteData())
                    {
                        try
                        {
                            if (package.ItemType == RemoteDataObject.StorageType.File)
                            {
                                string directoryPath = Path.GetDirectoryName(dropPath);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }

                                string uniqueName = Win32API.GenerateUniquePath(Path.Combine(dropPath, package.Name));
                                using (FileStream stream = new FileStream(uniqueName, FileMode.CreateNew))
                                {
                                    package.ContentStream.CopyTo(stream);
                                }
                            }
                            else
                            {
                                string directoryPath = Path.Combine(dropPath, package.Name);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }
                            }
                        }
                        finally
                        {
                            package.Dispose();
                        }
                    }
                    return(true);
                });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "Success", result }
                }, message.Get("RequestID", (string)null));

                break;

            case "DeleteItem":
            {
                var fileToDeletePath = ((string)message["filepath"]).Split('|');
                var permanently      = (bool)message["permanently"];
                var operationID      = (string)message["operationID"];
                var(succcess, deletedItems, recycledItems) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoConfirmation
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;
                            if (!permanently)
                            {
                                op.Options |= ShellFileOperations.OperationFlags.RecycleOnDelete
                                              | ShellFileOperations.OperationFlags.WantNukeWarning;
                            }
                            List <string> deletedItems  = new List <string>();
                            List <string> recycledItems = new List <string>();

                            for (var i = 0; i < fileToDeletePath.Length; i++)
                            {
                                using var shi = new ShellItem(fileToDeletePath[i]);
                                op.QueueDeleteOperation(shi);
                            }

                            handleTable.SetValue(operationID, false);
                            var deleteTcs      = new TaskCompletionSource <bool>();
                            op.PostDeleteItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToDeletePath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    deletedItems.Add(e.SourceItem.FileSystemPath);
                                    if (e.DestItem != null)
                                    {
                                        recycledItems.Add(e.DestItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => deleteTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                deleteTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await deleteTcs.Task && deletedItems.Count == fileToDeletePath.Length, deletedItems, recycledItems);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "DeletedItems", JsonConvert.SerializeObject(deletedItems) },
                        { "RecycledItems", JsonConvert.SerializeObject(recycledItems) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "RenameItem":
            {
                var fileToRenamePath  = (string)message["filepath"];
                var newName           = (string)message["newName"];
                var operationID       = (string)message["operationID"];
                var overwriteOnRename = (bool)message["overwrite"];
                var(succcess, renamedItems) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> renamedItems = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;
                            op.Options |= !overwriteOnRename ? ShellFileOperations.OperationFlags.RenameOnCollision : 0;

                            using var shi = new ShellItem(fileToRenamePath);
                            op.QueueRenameOperation(shi, newName);

                            handleTable.SetValue(operationID, false);
                            var renameTcs      = new TaskCompletionSource <bool>();
                            op.PostRenameItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    renamedItems.Add($"{Path.Combine(Path.GetDirectoryName(e.SourceItem.FileSystemPath), e.Name)}");
                                }
                            };
                            op.FinishOperations += (s, e) => renameTcs.TrySetResult(e.Result.Succeeded);

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                renameTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await renameTcs.Task && renamedItems.Count == 1, renamedItems);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "RenamedItems", JsonConvert.SerializeObject(renamedItems) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "MoveItem":
            {
                var fileToMovePath  = ((string)message["filepath"]).Split('|');
                var moveDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnMove = (bool)message["overwrite"];
                var(succcess, movedItems, movedSources) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> movedItems   = new List <string>();
                            List <string> movedSources = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;

                            op.Options |= !overwriteOnMove ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToMovePath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToMovePath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(moveDestination[i])))
                                    {
                                        op.QueueMoveOperation(shi, shd, Path.GetFileName(moveDestination[i]));
                                    }
                            }

                            handleTable.SetValue(operationID, false);
                            var moveTcs      = new TaskCompletionSource <bool>();
                            op.PostMoveItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToMovePath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    if (e.DestFolder != null && !string.IsNullOrEmpty(e.Name))
                                    {
                                        movedItems.Add($"{Path.Combine(e.DestFolder.FileSystemPath, e.Name)}");
                                        movedSources.Add(e.SourceItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => moveTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                moveTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await moveTcs.Task && movedItems.Count == fileToMovePath.Length, movedItems, movedSources);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "MovedItems", JsonConvert.SerializeObject(movedItems) },
                        { "MovedSources", JsonConvert.SerializeObject(movedSources) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CopyItem":
            {
                var fileToCopyPath  = ((string)message["filepath"]).Split('|');
                var copyDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnCopy = (bool)message["overwrite"];
                var(succcess, copiedItems, copiedSources) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> copiedItems   = new List <string>();
                            List <string> copiedSources = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;

                            op.Options |= !overwriteOnCopy ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToCopyPath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToCopyPath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(copyDestination[i])))
                                    {
                                        op.QueueCopyOperation(shi, shd, Path.GetFileName(copyDestination[i]));
                                    }
                            }

                            handleTable.SetValue(operationID, false);
                            var copyTcs      = new TaskCompletionSource <bool>();
                            op.PostCopyItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToCopyPath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    if (e.DestFolder != null && !string.IsNullOrEmpty(e.Name))
                                    {
                                        copiedItems.Add($"{Path.Combine(e.DestFolder.FileSystemPath, e.Name)}");
                                        copiedSources.Add(e.SourceItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => copyTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                copyTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await copyTcs.Task && copiedItems.Count == fileToCopyPath.Length, copiedItems, copiedSources);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "CopiedItems", JsonConvert.SerializeObject(copiedItems) },
                        { "CopiedSources", JsonConvert.SerializeObject(copiedSources) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CancelOperation":
            {
                var operationID = (string)message["operationID"];
                handleTable.SetValue(operationID, true);
            }
            break;

            case "ParseLink":
                var linkPath = (string)message["filepath"];
                try
                {
                    if (linkPath.EndsWith(".lnk"))
                    {
                        using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", link.TargetPath },
                            { "Arguments", link.Arguments },
                            { "WorkingDirectory", link.WorkingDirectory },
                            { "RunAsAdmin", link.RunAsAdministrator },
                            { "IsFolder", !string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder }
                        }, message.Get("RequestID", (string)null));
                    }
                    else if (linkPath.EndsWith(".url"))
                    {
                        var linkUrl = await Win32API.StartSTATask(() =>
                        {
                            var ipf = new Url.IUniformResourceLocator();
                            (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Load(linkPath, 0);
                            ipf.GetUrl(out var retVal);
                            return(retVal);
                        });

                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", linkUrl },
                            { "Arguments", null },
                            { "WorkingDirectory", null },
                            { "RunAsAdmin", false },
                            { "IsFolder", false }
                        }, message.Get("RequestID", (string)null));
                    }
                }
                catch (Exception ex)
                {
                    // Could not parse shortcut
                    Program.Logger.Warn(ex, ex.Message);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "TargetPath", null },
                        { "Arguments", null },
                        { "WorkingDirectory", null },
                        { "RunAsAdmin", false },
                        { "IsFolder", false }
                    }, message.Get("RequestID", (string)null));
                }
                break;

            case "CreateLink":
            case "UpdateLink":
                var linkSavePath = (string)message["filepath"];
                var targetPath   = (string)message["targetpath"];
                if (linkSavePath.EndsWith(".lnk"))
                {
                    var arguments        = (string)message["arguments"];
                    var workingDirectory = (string)message["workingdir"];
                    var runAsAdmin       = (bool)message["runasadmin"];
                    using var newLink          = new ShellLink(targetPath, arguments, workingDirectory);
                    newLink.RunAsAdministrator = runAsAdmin;
                    newLink.SaveAs(linkSavePath);     // Overwrite if exists
                }
                else if (linkSavePath.EndsWith(".url"))
                {
                    await Win32API.StartSTATask(() =>
                    {
                        var ipf = new Url.IUniformResourceLocator();
                        ipf.SetUrl(targetPath, Url.IURL_SETURL_FLAGS.IURL_SETURL_FL_GUESS_PROTOCOL);
                        (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Save(linkSavePath, false);     // Overwrite if exists
                        return(true);
                    });
                }
                break;

            case "GetFilePermissions":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var filePermissions = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "FilePermissions", JsonConvert.SerializeObject(filePermissions) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFilePermissions":
            {
                var filePermissionsString = (string)message["permissions"];
                var filePermissionsToSet  = JsonConvert.DeserializeObject <FilePermissions>(filePermissionsString);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", filePermissionsToSet.SetPermissions() }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFileOwner":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var ownerSid        = (string)message["ownersid"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetOwner(ownerSid) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetAccessRuleProtection":
            {
                var filePathForPerm     = (string)message["filepath"];
                var isFolder            = (bool)message["isfolder"];
                var isProtected         = (bool)message["isprotected"];
                var preserveInheritance = (bool)message["preserveinheritance"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetAccessRuleProtection(isProtected, preserveInheritance) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "OpenObjectPicker":
                var hwnd         = (long)message["HWND"];
                var pickedObject = await FilePermissions.OpenObjectPicker(hwnd);

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "PickedObject", pickedObject }
                }, message.Get("RequestID", (string)null));

                break;
            }
        }
コード例 #4
0
        private object HandleMenuMessage(Dictionary <string, object> message, DisposableDictionary table)
        {
            switch (message.Get("Arguments", ""))
            {
            case "LoadContextMenu":
                var contextMenuResponse = new ValueSet();
                var filePath            = (string)message["FilePath"];
                var extendedMenu        = (bool)message["ExtendedMenu"];
                var showOpenMenu        = (bool)message["ShowOpenMenu"];
                var split     = filePath.Split('|').Where(x => !string.IsNullOrWhiteSpace(x));
                var cMenuLoad = ContextMenu.GetContextMenuForFiles(split.ToArray(),
                                                                   (extendedMenu ? Shell32.CMF.CMF_EXTENDEDVERBS : Shell32.CMF.CMF_NORMAL) | Shell32.CMF.CMF_SYNCCASCADEMENU, FilterMenuItems(showOpenMenu));
                table.SetValue("MENU", cMenuLoad);
                return(cMenuLoad);

            case "ExecAndCloseContextMenu":
                var cMenuExec = table.GetValue <ContextMenu>("MENU");
                if (message.TryGetValue("ItemID", out var menuId))
                {
                    var isFont = new[] { ".fon", ".otf", ".ttc", ".ttf" }.Contains(Path.GetExtension(cMenuExec.ItemsPath[0]), StringComparer.OrdinalIgnoreCase);
                    var verb = message.Get("CommandString", (string)null);
                    switch (verb)
                    {
                    case string _ when verb == "install" && isFont:
                    {
                        var userFontDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "Windows", "Fonts");
                        var destName    = Path.Combine(userFontDir, Path.GetFileName(cMenuExec.ItemsPath[0]));
                        Win32API.RunPowershellCommand($"-command \"Copy-Item '{cMenuExec.ItemsPath[0]}' '{userFontDir}'; New-ItemProperty -Name '{Path.GetFileNameWithoutExtension(cMenuExec.ItemsPath[0])}' -Path 'HKCU:\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts' -PropertyType string -Value '{destName}'\"", false);
                    }
                    break;

                    case string _ when verb == "installAllUsers" && isFont:
                    {
                        var winFontDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts");
                        Win32API.RunPowershellCommand($"-command \"Copy-Item '{cMenuExec.ItemsPath[0]}' '{winFontDir}'; New-ItemProperty -Name '{Path.GetFileNameWithoutExtension(cMenuExec.ItemsPath[0])}' -Path 'HKLM:\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts' -PropertyType string -Value '{Path.GetFileName(cMenuExec.ItemsPath[0])}'\"", true);
                    }
                    break;

                    case string _ when verb == "mount":
                        var vhdPath = cMenuExec.ItemsPath[0];
                        Win32API.MountVhdDisk(vhdPath);
                        break;

                    case string _ when verb == "format":
                        var drivePath = cMenuExec.ItemsPath[0];
                        Win32API.OpenFormatDriveDialog(drivePath);
                        break;

                    default:
                        cMenuExec?.InvokeItem((int)(long)menuId);
                        break;
                    }
                }
                // The following line is needed to cleanup resources when menu is closed.
                // Unfortunately if you uncomment it some menu items will randomly stop working.
                // Resource cleanup is currently done on app closing,
                // if we find a solution for the issue above, we should cleanup as soon as a menu is closed.
                //table.RemoveValue("MENU");
                return(null);

            default:
                return(null);
            }
        }