[英]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並完全這樣做),這里是你將遇到的其他重大問題:
下面是一個例子,如何從Delphi應用程序中使用“發送到... |郵件收件人”上下文菜單項后面的操作系統邏輯來打開默認郵件客戶端,顯示附帶傳遞(選定)文件的新郵件:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.