在 dotNet GUI 呼叫檔案總管的 Context Menu
文章目錄
需求
顯示一個檔案總管的 Context Menu, 輸入參數是檔案名稱陣列.
環境
- Visual Studio Community 2022
- 3rd party library: https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu
- 這個是 WinForm (Windows Forms), 如果你是用 WPF (Windows Presentation Foundation), 需要在 .csproj 加上 <UseWindowsForms>true</UseWindowsForms>
顯示一個檔案總管的 Context Menu
- Download the component from https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu.
- Extract ShellContextMenu.cs from the above zip file and put it into your project.
- 執行以下指令, 就會在游標處顯示檔案總管的 context menu.
1using Peter; 2 3public MainWindow() { 4 ShellContextMenu ctxMnu = new ShellContextMenu(); 5 FileInfo[] arrFI = new FileInfo[1]; 6 arrFI[0] = new FileInfo(@"c:\cmd.txt"); 7 ctxMnu.ShowContextMenu(arrFI, System.Windows.Forms.Cursor.Position); 8}
- 這邊要注意的是, 他的輸入參數是 array, 因為實際上可能先選多個檔案, 然後再按右鍵, 測試範例如下
1using Peter; 2 3public MainWindow() 4 ShellContextMenu ctxMnu = new ShellContextMenu(); 5 FileInfo[] arrFI = new FileInfo[2]; 6 arrFI[0] = new FileInfo(@"c:\cmd1.txt"); 7 arrFI[1] = new FileInfo(@"c:\cmd2.txt"); 8 ctxMnu.ShowContextMenu(arrFI, System.Windows.Forms.Cursor.Position); 9}
如果遇到 System.OverflowException
我一開始在 Visual Studio 2022 + .Net 7.0 一切正常, 但換成 .net 6.0 就遇到 System.OverflowException, 查了一下, 可參考這篇, 修改 HiWord/LoWord 就好. https://www.codeproject.com/Articles/15059/C-File-Browser?msg=3541887#xx3541887xx
如果 "傳送到" 有問題
參考這裡修改: https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu?msg=3706303#xx3706303xx
在 Context Menu 新增一個新的 menu
上面的功能是把檔案總管的 Context Menu 原封不動搬過來, 如果要新增一些自己的 menu, 可參考下面作法:
- 注意: 以下只是把這裡的內容複製過來 https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu?msg=3666382#xx3666382xx
- 修改 ShellContextMenu.cs
-
尋找這段程式碼
1pMenu = CreatePopupMenu(); 2int nResult = _oContextMenu.QueryContextMenu( 3 pMenu, 4 0, 5 CMD_FIRST, 6 CMD_LAST, 7 CMF.EXPLORE | 8 CMF.NORMAL | 9 ((Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0));
-
在上面這段程式碼的後面加上以下程式, 目的是定義這個新的功能表的相關資訊
- 第 6 行: 這個數字之後會用來判斷要執行哪個 function
- 第 8 行: 這是 menu 的名字
- 第 10 行: 新功能表如果要加在最上面, 就 uncomment 這行
- 第 12 行: 新功能表如果要加在最下面, 就 uncomment 這行
1MENUITEMINFO mii = new MENUITEMINFO(); 2mii.cbSize = Marshal.SizeOf(mii); 3mii.fMask = MIIM.STRING | MIIM.ID; 4mii.fState = MFS.DEFAULT; 5mii.fType = MFT.BYCOMMAND; 6mii.wID = 500; //Application-defined ID of menu item 7mii.dwItemData = IntPtr.Zero; 8mii.dwTypeData = "Your_Item_Name"; 9//this function adds your item at the top of Context Menu 10//InsertMenuItem(pMenu, (uint)(0), true, ref mii); 11//this function adds your item at the end of Context Menu 12//InsertMenuItem(pMenu, (uint)(GetMenuItemCount(pMenu) + 1), true, ref mii);
-
找個地方加入這個 function, 這個是上面那段程式裡面
InsertMenuItem
的實作1// Inserts a new menu item at the specified position in a menu 2[DllImport("user32", 3 SetLastError = true, 4 CharSet = CharSet.Auto)] 5private static extern bool InsertMenuItem( 6 IntPtr hMenu, 7 uint uItem, 8 bool fByPosition, 9 ref MENUITEMINFO lpmii); 10 11[DllImport("user32.dll")] 12private static extern int GetMenuItemCount(IntPtr hMenu);
-
在程式裡面找下面這段程式, 然後把原來的 InvokeCommand 改成 18~24 行的寫法, 這裡的 500 就會和上面對應
1//this code is already written in ShowContextMenu.cs 2//this API-function returns user selection 3uint nSelected = TrackPopupMenuEx( 4 pMenu, 5 TPM.RETURNCMD, 6 pointScreen.X, 7 pointScreen.Y, 8 this.Handle, 9 IntPtr.Zero); 10 11DestroyMenu(pMenu); 12pMenu = IntPtr.Zero; 13 14if (nSelected != 0) 15{ 16 //nSelected contains ID of selected item 17 //check if user selected your item 18 if (nSelected == 500) 19 //doing anything 20 MessageBox.Show("I'm the new menu!!!"); 21 else 22 InvokeCommand(_oContextMenu, nSelected, _strParentFolder, pointScreen); 23}
-
以上就完成了
-
(未完成) 在 Context Menu 新增一個新的 menu
上面只能新增一個或多個 menu, 沒有辦法做成階層式的 (類似 開啟檔案 或是 傳送到), 程式裡面有看到 MIIM.SUBMENU, 有空再試試看.