在 dotNet GUI 呼叫檔案總管的 Context Menu

文章目錄

需求

顯示一個檔案總管的 Context Menu, 輸入參數是檔案名稱陣列.

環境

  1. Visual Studio Community 2022
  2. 3rd party library: https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu
  3. 這個是 WinForm (Windows Forms), 如果你是用 WPF (Windows Presentation Foundation), 需要在 .csproj 加上 <UseWindowsForms>true</UseWindowsForms>

顯示一個檔案總管的 Context Menu

  1. Download the component from https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu.
  2. Extract ShellContextMenu.cs from the above zip file and put it into your project.
  3. 執行以下指令, 就會在游標處顯示檔案總管的 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}
    
  4. 這邊要注意的是, 他的輸入參數是 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, 可參考下面作法:

  1. 注意: 以下只是把這裡的內容複製過來 https://www.codeproject.com/Articles/22012/Explorer-Shell-Context-Menu?msg=3666382#xx3666382xx
  2. 修改 ShellContextMenu.cs
    1. 尋找這段程式碼

      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));
      
    2. 在上面這段程式碼的後面加上以下程式, 目的是定義這個新的功能表的相關資訊

      • 第 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);
      
    3. 找個地方加入這個 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);
      
    4. 在程式裡面找下面這段程式, 然後把原來的 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}
      
    5. 以上就完成了

(未完成) 在 Context Menu 新增一個新的 menu

上面只能新增一個或多個 menu, 沒有辦法做成階層式的 (類似 開啟檔案 或是 傳送到), 程式裡面有看到 MIIM.SUBMENU, 有空再試試看.

Posts in this Series