簡體   English   中英

如何訪問Windows shell上下文菜單項?

[英]How to access Windows shell context menu items?

在Windows資源管理器中,右鍵單擊一個文件,顯示一個上下文菜單,其中包含內置項,如“發送到...”和/或第三方操作,如“帶Winzip的zip文件”。 我的問題是:

  • 如何獲取特定文件的可用菜單項的完整列表?
  • 對於每個菜單項,如何獲取標題?
  • 如何為特定磁盤文件調用特定菜單項操作?

先感謝您!

[編輯]:雖然其他信息絕對有用,但Delphi解決方案將非常受歡迎!

獲取Shell上下文菜單的關鍵是使用IContextMenu接口。

查看這篇很棒的文章Shell context menu support更多細節。

UPDATE

為Delphi例子可以看到JclShell從JEDI JCL單元(檢查DisplayContextMenu功能)和包含在Delphi的樣品夾在ShellCtrls單元。

簡短的回答

試試JAM Software的ShellBrowser組件 它們有一個組件,可以讓您顯示資源管理器的上下文菜單,其中您自己的命令來自TPopupMenu。


答案很長

獲取資源管理器菜單,查詢其所有屬性,並在您自己的菜單中托管它們是可能的,但您應該很樂意閱讀/編寫低級Win32代碼,並且C的工作知識將有所幫助。 你還需要注意一些陷阱(如下所述)。 我強烈建議閱讀Raymond Chen的“ 如何主持IContextMenu系列”以獲取大量技術細節。

更簡單的方法是查詢IContextMenu接口,然后查詢HMENU,然后使用TrackPopupMenu讓Windows顯示菜單,最后調用InvokeCommand。

以下某些代碼未經測試或已根據我們使用的內容進行了修改,因此請自行承擔風險。

以下是為基本文件夾中的一組文件獲取IContextMenu的方法

function GetExplorerMenu(AHandle: HWND; const APath: string;
  AFilenames: TStrings): IContextMenu;
var
  Desktop, Parent: IShellFolder;
  FolderPidl: PItemIDList;
  FilePidls: array of PItemIDList;
  PathW: WideString;
  i: Integer;
begin
  // Retrieve the Desktop's IShellFolder interface
  OleCheck(SHGetDesktopFolder(Desktop));
  // Retrieve the parent folder's PItemIDList and then it's IShellFolder interface
  PathW := WideString(IncludeTrailingPathDelimiter(APath));
  OleCheck(Desktop.ParseDisplayName(AHandle, nil, PWideChar(PathW),
    Cardinal(nil^), FolderPidl, Cardinal(nil^)));
  try
    OleCheck(Desktop.BindToObject(FolderPidl, nil, IID_IShellFolder, Parent));
  finally
    SHFree(FolderPidl);
  end;
  // Retrieve PIDLs for each file, relative the the parent folder
  SetLength(FilePidls, AFilenames.Count);
  try
    FillChar(FilePidls[0], SizeOf(PItemIDList) * AFilenames.Count, 0);
    for i := 0 to AFilenames.Count-1 do begin
      PathW := WideString(AFilenames[i]);
      OleCheck(Parent.ParseDisplayName(AHandle, nil, PWideChar(PathW),
        Cardinal(nil^), FilePidls[i], Cardinal(nil^)));
    end;
    // Get the context menu for the files from the parent's IShellFolder
    OleCheck(Parent.GetUIObjectOf(AHandle, AFilenames.Count, FilePidls[0],
      IID_IContextMenu, nil, Result));
  finally
    for i := 0 to Length(FilePidls) - 1 do
      SHFree(FilePidls[i]);
  end;
end;

要獲取實際的菜單項,您需要調用IContextMenu.QueryContextMenu 您可以使用DestroyMenu銷毀返回的HMENU。

function GetExplorerHMenu(const AContextMenu: IContextMenu): HMENU;
const
  MENUID_FIRST = 1;
  MENUID_LAST = $7FFF;
var
  OldMode: UINT;
begin
  OldMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
  try
    Result := CreatePopupMenu;
    AContextMenu.QueryContextMenu(Result, 0, MENUID_FIRST, MENUID_LAST, CMF_NORMAL);
  finally
    SetErrorMode(OldMode);
  end;
end;

以下是您實際調用用戶從菜單中選擇的命令的方法:

procedure InvokeCommand(const AContextMenu: IContextMenu; AVerb: PChar);
const
  CMIC_MASK_SHIFT_DOWN   = $10000000;
  CMIC_MASK_CONTROL_DOWN = $20000000;
var
  CI: TCMInvokeCommandInfoEx;
begin
  FillChar(CI, SizeOf(TCMInvokeCommandInfoEx), 0);
  CI.cbSize := SizeOf(TCMInvokeCommandInfo);
  CI.hwnd := GetOwnerHandle(Owner);
  CI.lpVerb := AVerb;
  CI.nShow := SW_SHOWNORMAL;
  // Ignore return value for InvokeCommand.  Some shell extensions return errors
  // from it even if the command worked.
  try
    AContextMenu.InvokeCommand(PCMInvokeCommandInfo(@CI)^)
  except on E: Exception do
    MessageDlg(Owner, E.Message, mtError, [mbOk], 0);
  end;
end;

procedure InvokeCommand(const AContextMenu: IContextMenu; ACommandID: UINT);
begin
  InvokeCommand(AContextMenu, MakeIntResource(Word(ACommandID)));
end;

現在您可以使用GetMenuItemInfo函數來獲取標題,位圖等,但更簡單的方法是調用TrackPopupMenu並讓Windows顯示彈出菜單。 這看起來像這樣:

procedure ShowExplorerMenu(AForm: TForm; AMousePos: TPoint; 
  const APath: string; AFilenames: TStrings; );
var
  ShellMenu: IContextMenu;
  Menu: HMENU;
  MenuID: LongInt;
begin
  ShellMenu := GetExplorerMenu(AForm.Handle, APath, AFilenames);
  Menu := GetExplorerHMenu(ShellMenu);
  try
    MenuID := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_TOPALIGN or TPM_RETURNCMD, 
      AMousePos.X, AMousePos.Y, 0, AForm.Handle, nil);
    InvokeCommand(ShellMenu, MenuID - MENUID_FIRST);
  finally
    DestroyMenu(Menu);
  end;
end;

如果你真的想要提取菜單項/標題並將它們添加到你自己的彈出菜單中(我們使用Toolbar 2000並完全這樣做),這里是你將遇到的其他重大問題:

  • 除非您處理消息並將消息傳遞給IContextMenu2 / IContextMenu3接口,否則“發送到”菜單以及按需構建的任何其他菜單都將無法工作。
  • 菜單位圖有幾種不同的格式。 Delphi不會在沒有哄騙的情況下處理Vista高色彩的產品,並且使用XOR將較舊的產品混合到背景顏色上。
  • 有些菜單項是所有者繪制的,因此您必須捕獲繪制消息並將它們繪制到您自己的畫布上。
  • 除非您手動查詢它們,否則提示字符串將不起作用。
  • 您需要管理IContextMenu和HMENU的生命周期,並且只有在關閉彈出菜單后才會釋放它們。

下面是一個例子,如何從Delphi應用程序中使用“發送到... |郵件收件人”上下文菜單項后面的操作系統邏輯來打開默認郵件客戶端,顯示附帶傳遞(選定)文件的新郵件:

如何使用Delphi模擬“發送到...”?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM