在Windows資源管理器中,右鍵單擊某個文件,會顯示一個上下文菜單,其中包含內置項目,如「發送到...」和/或第三方操作,例如「使用Winzip的zip文件」。我的問題是:如何訪問Windows shell的上下文菜單項?
- 如何獲取特定文件的可用菜單項的完整列表?
- 對於每個菜單項,如何獲取標題?
- 如何爲特定的磁盤文件調用特定的菜單項操作?
預先感謝您!
[編輯]:雖然其他信息是絕對有用的,但Delphi解決方案將非常感謝!
在Windows資源管理器中,右鍵單擊某個文件,會顯示一個上下文菜單,其中包含內置項目,如「發送到...」和/或第三方操作,例如「使用Winzip的zip文件」。我的問題是:如何訪問Windows shell的上下文菜單項?
預先感謝您!
[編輯]:雖然其他信息是絕對有用的,但Delphi解決方案將非常感謝!
獲取Shell上下文菜單的關鍵是使用IContextMenu
接口。
查看此文章Shell context menu support
瞭解更多詳情。
UPDATE
爲Delphi例子可以看到從JEDI JCL的JclShell單元(檢查DisplayContextMenu
功能)和包含在Delphi的樣品夾在ShellCtrls單元。
感謝您的鏈接,我會檢查出來! – 2010-09-23 11:01:03
嗨RRUZ,JCL包裝的DisplayContextMenu功能正是我想要的!謝謝! – 2010-09-24 08:13:16
簡短的回答
從JAM軟件嘗試ShellBrowser Components。他們有一個組件,可以讓你用TPopupMenu自帶的命令顯示Explorer的上下文菜單。
龍答案
獲取資源管理器菜單,查詢它的所有屬性,並在自己的菜單收留他們是可能的,但你真的應該是舒適的讀/寫低級別的Win32代碼而C的工作知識將有所幫助。您還需要注意一些疑難問題(下面將介紹)。我強烈建議閱讀Raymond Chen的How to host an IContextMenu系列瞭解很多技術細節。
更容易的方法是查詢IContextMenu接口,然後使用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;
如果你真的想提取的菜單項/字幕,並將其添加到您自己的彈出菜單(我們使用工具欄2000正是這樣做的),這裏的另一大問題你會碰到:
嗨克雷格,感謝您的詳細技術信息,他們是非常有幫助的!但是我必須接受RRUZ的答案,因爲他指出我從JCL的DisplayContextMenu函數,它完全符合我想要的一行代碼...... – 2010-09-24 08:23:46
這裏是
的一個實例如何背後的操作系統邏輯「發送到... |郵件收件人」上下文菜單項可以從Delphi應用程序用來打開默認郵件客戶端,顯示新郵件與通過連接(選擇)文件:
你想創建自己的上下文菜單項或操作已有菜單項? – Liton 2010-09-23 10:36:40
@Liton,不是創建我自己的shell上下文菜單項,而是操作現有的菜單內置或第三部分項目。 – 2010-09-23 10:59:04