|
11 | 11 | using System.Text.RegularExpressions;
|
12 | 12 | using System.Threading;
|
13 | 13 | using System.Threading.Tasks;
|
| 14 | +using System.Windows.Forms; |
14 | 15 | using Vanara.PInvoke;
|
15 | 16 | using Vanara.Windows.Shell;
|
16 | 17 | using Windows.ApplicationModel;
|
@@ -50,6 +51,9 @@ private static void Main(string[] args)
|
50 | 51 |
|
51 | 52 | try
|
52 | 53 | {
|
| 54 | + // Create handle table to store e.g. context menu references |
| 55 | + handleTable = new Win32API.DisposableDictionary(); |
| 56 | + |
53 | 57 | // Create shell COM object and get recycle bin folder
|
54 | 58 | recycler = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_RecycleBinFolder);
|
55 | 59 | ApplicationData.Current.LocalSettings.Values["RecycleBin_Title"] = recycler.Name;
|
@@ -89,6 +93,7 @@ private static void Main(string[] args)
|
89 | 93 | {
|
90 | 94 | watcher.Dispose();
|
91 | 95 | }
|
| 96 | + handleTable?.Dispose(); |
92 | 97 | recycler?.Dispose();
|
93 | 98 | appServiceExit?.Dispose();
|
94 | 99 | mutex?.ReleaseMutex();
|
@@ -129,6 +134,7 @@ private static async void Watcher_Changed(object sender, FileSystemEventArgs e)
|
129 | 134 | private static AppServiceConnection connection;
|
130 | 135 | private static AutoResetEvent appServiceExit;
|
131 | 136 | private static ShellFolder recycler;
|
| 137 | + private static Win32API.DisposableDictionary handleTable; |
132 | 138 | private static IList<FileSystemWatcher> watchers;
|
133 | 139 |
|
134 | 140 | private static async void InitializeAppServiceConnection()
|
@@ -233,17 +239,36 @@ private static async Task parseArguments(AppServiceRequestReceivedEventArgs args
|
233 | 239 | process.Start();
|
234 | 240 | break;
|
235 | 241 |
|
236 |
| - case "LoadMUIVerb": |
237 |
| - var responseSet = new ValueSet(); |
238 |
| - responseSet.Add("MUIVerbString", Win32API.ExtractStringFromDLL((string)args.Request.Message["MUIVerbLocation"], (int)args.Request.Message["MUIVerbLine"])); |
239 |
| - await args.Request.SendResponseAsync(responseSet); |
| 242 | + case "LoadContextMenu": |
| 243 | + var contextMenuResponse = new ValueSet(); |
| 244 | + var loadThreadWithMessageQueue = new Win32API.ThreadWithMessageQueue<ValueSet>(HandleMenuMessage); |
| 245 | + var cMenuLoad = await loadThreadWithMessageQueue.PostMessage<Win32API.ContextMenu>(args.Request.Message); |
| 246 | + contextMenuResponse.Add("Handle", handleTable.AddValue(loadThreadWithMessageQueue)); |
| 247 | + contextMenuResponse.Add("ContextMenu", JsonConvert.SerializeObject(cMenuLoad)); |
| 248 | + await args.Request.SendResponseAsync(contextMenuResponse); |
240 | 249 | break;
|
241 | 250 |
|
242 |
| - case "ParseAguments": |
243 |
| - var responseArray = new ValueSet(); |
244 |
| - var resultArgument = Win32API.CommandLineToArgs((string)args.Request.Message["Command"]); |
245 |
| - responseArray.Add("ParsedArguments", JsonConvert.SerializeObject(resultArgument)); |
246 |
| - await args.Request.SendResponseAsync(responseArray); |
| 251 | + case "ExecAndCloseContextMenu": |
| 252 | + var menuKey = (string)args.Request.Message["Handle"]; |
| 253 | + var execThreadWithMessageQueue = handleTable.GetValue<Win32API.ThreadWithMessageQueue<ValueSet>>(menuKey); |
| 254 | + if (execThreadWithMessageQueue != null) |
| 255 | + { |
| 256 | + await execThreadWithMessageQueue.PostMessage(args.Request.Message); |
| 257 | + } |
| 258 | + // The following line is needed to cleanup resources when menu is closed. |
| 259 | + // Unfortunately if you uncomment it some menu items will randomly stop working. |
| 260 | + // Resource cleanup is currently done on app closing, |
| 261 | + // if we find a solution for the issue above, we should cleanup as soon as a menu is closed. |
| 262 | + //handleTable.RemoveValue(menuKey); |
| 263 | + break; |
| 264 | + |
| 265 | + case "InvokeVerb": |
| 266 | + var filePath = (string)args.Request.Message["FilePath"]; |
| 267 | + var split = filePath.Split('|').Where(x => !string.IsNullOrWhiteSpace(x)); |
| 268 | + using (var cMenu = Win32API.ContextMenu.GetContextMenuForFiles(split.ToArray(), Shell32.CMF.CMF_DEFAULTONLY)) |
| 269 | + { |
| 270 | + cMenu?.InvokeVerb((string)args.Request.Message["Verb"]); |
| 271 | + } |
247 | 272 | break;
|
248 | 273 |
|
249 | 274 | case "Bitlocker":
|
@@ -276,6 +301,56 @@ private static async Task parseArguments(AppServiceRequestReceivedEventArgs args
|
276 | 301 | }
|
277 | 302 | }
|
278 | 303 |
|
| 304 | + private static object HandleMenuMessage(ValueSet message, Win32API.DisposableDictionary table) |
| 305 | + { |
| 306 | + switch ((string)message["Arguments"]) |
| 307 | + { |
| 308 | + case "LoadContextMenu": |
| 309 | + var contextMenuResponse = new ValueSet(); |
| 310 | + var filePath = (string)message["FilePath"]; |
| 311 | + var extendedMenu = (bool)message["ExtendedMenu"]; |
| 312 | + var showOpenMenu = (bool)message["ShowOpenMenu"]; |
| 313 | + var split = filePath.Split('|').Where(x => !string.IsNullOrWhiteSpace(x)); |
| 314 | + var cMenuLoad = Win32API.ContextMenu.GetContextMenuForFiles(split.ToArray(), |
| 315 | + extendedMenu ? Shell32.CMF.CMF_EXTENDEDVERBS : Shell32.CMF.CMF_NORMAL, FilterMenuItems(showOpenMenu)); |
| 316 | + table.SetValue("MENU", cMenuLoad); |
| 317 | + return cMenuLoad; |
| 318 | + |
| 319 | + case "ExecAndCloseContextMenu": |
| 320 | + var cMenuExec = table.GetValue<Win32API.ContextMenu>("MENU"); |
| 321 | + cMenuExec?.InvokeItem(message.Get("ItemID", -1)); |
| 322 | + // The following line is needed to cleanup resources when menu is closed. |
| 323 | + // Unfortunately if you uncomment it some menu items will randomly stop working. |
| 324 | + // Resource cleanup is currently done on app closing, |
| 325 | + // if we find a solution for the issue above, we should cleanup as soon as a menu is closed. |
| 326 | + //table.RemoveValue("MENU"); |
| 327 | + return null; |
| 328 | + |
| 329 | + default: |
| 330 | + return null; |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + private static Func<string, bool> FilterMenuItems(bool showOpenMenu) |
| 335 | + { |
| 336 | + var knownItems = new List<string>() { |
| 337 | + "opennew", "openas", "opencontaining", "opennewprocess", |
| 338 | + "runas", "runasuser", "pintohome", |
| 339 | + "cut", "copy", "delete", "properties", "link", |
| 340 | + "WSL", "Windows.ModernShare", "Windows.Share", "setdesktopwallpaper", |
| 341 | + Win32API.ExtractStringFromDLL("shell32.dll", 30312), // SendTo menu |
| 342 | + Win32API.ExtractStringFromDLL("shell32.dll", 34593), // Add to collection |
| 343 | + }; |
| 344 | + |
| 345 | + bool filterMenuItemsImpl(string menuItem) |
| 346 | + { |
| 347 | + return string.IsNullOrEmpty(menuItem) ? false : knownItems.Contains(menuItem) |
| 348 | + || (!showOpenMenu && menuItem.Equals("open", StringComparison.OrdinalIgnoreCase)); |
| 349 | + } |
| 350 | + |
| 351 | + return filterMenuItemsImpl; |
| 352 | + } |
| 353 | + |
279 | 354 | private static async Task parseFileOperation(AppServiceRequestReceivedEventArgs args)
|
280 | 355 | {
|
281 | 356 | var fileOp = (string)args.Request.Message["fileop"];
|
@@ -552,32 +627,8 @@ await Win32API.StartSTATask(() =>
|
552 | 627 | {
|
553 | 628 | continue;
|
554 | 629 | }
|
555 |
| - |
556 |
| - var files = group.Select(x => new ShellItem(x)); |
557 |
| - using var sf = files.First().Parent; |
558 |
| - Shell32.IContextMenu menu = null; |
559 |
| - try |
560 |
| - { |
561 |
| - menu = sf.GetChildrenUIObjects<Shell32.IContextMenu>(null, files.ToArray()); |
562 |
| - menu.QueryContextMenu(HMENU.NULL, 0, 0, 0, Shell32.CMF.CMF_DEFAULTONLY); |
563 |
| - var pici = new Shell32.CMINVOKECOMMANDINFOEX(); |
564 |
| - pici.lpVerb = new SafeResourceId(Shell32.CMDSTR_OPEN, CharSet.Ansi); |
565 |
| - pici.nShow = ShowWindowCommand.SW_SHOW; |
566 |
| - pici.cbSize = (uint)Marshal.SizeOf(pici); |
567 |
| - menu.InvokeCommand(pici); |
568 |
| - } |
569 |
| - finally |
570 |
| - { |
571 |
| - foreach (var elem in files) |
572 |
| - { |
573 |
| - elem.Dispose(); |
574 |
| - } |
575 |
| - |
576 |
| - if (menu != null) |
577 |
| - { |
578 |
| - Marshal.ReleaseComObject(menu); |
579 |
| - } |
580 |
| - } |
| 630 | + using var cMenu = Win32API.ContextMenu.GetContextMenuForFiles(group.ToArray(), Shell32.CMF.CMF_DEFAULTONLY); |
| 631 | + cMenu?.InvokeVerb(Shell32.CMDSTR_OPEN); |
581 | 632 | }
|
582 | 633 | }
|
583 | 634 | return true;
|
|
0 commit comments