2010-09-23 29 views
11

在Windows資源管理器中,右鍵單擊某個文件,會顯示一個上下文菜單,其中包含內置項目,如「發送到...」和/或第三方操作,例如「使用Winzip的zip文件」。我的問題是:如何訪問Windows shell的上下文菜單項?

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

預先感謝您!

[編輯]:雖然其他信息是絕對有用的,但Delphi解決方案將非常感謝!

+0

你想創建自己的上下文菜單項或操作已有菜單項? – Liton 2010-09-23 10:36:40

+0

@Liton,不是創建我自己的shell上下文菜單項,而是操作現有的菜單內置或第三部分項目。 – 2010-09-23 10:59:04

回答

9

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

查看此文章Shell context menu support瞭解更多詳情。

UPDATE

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

+0

感謝您的鏈接,我會檢查出來! – 2010-09-23 11:01:03

+0

嗨RRUZ,JCL包裝的DisplayContextMenu功能正是我想要的!謝謝! – 2010-09-24 08:13:16

7

簡短的回答

從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正是這樣做的),這裏的另一大問題你會碰到:

  • 「發送到」菜單,除非你處理的消息,並將其傳遞給IContextMenu2/IContextMenu3界面,內置點播任何其他人將無法正常工作。
  • 菜單位圖有幾種不同的格式。德爾福不處理Vista的高彩色沒有哄騙,而舊的是使用XOR混合到背景顏色。
  • 某些菜單項是所有者繪製的,因此您必須捕獲繪畫消息並將它們繪製到您自己的畫布上。
  • 除非您手動查詢它們,否則提示字符串將不起作用。
  • 您需要管理IContextMenu和HMENU的生命週期,並且只有在彈出菜單關閉後才能釋放它們。
+0

嗨克雷格,感謝您的詳細技術信息,他們是非常有幫助的!但是我必須接受RRUZ的答案,因爲他指出我從JCL的DisplayContextMenu函數,它完全符合我想要的一行代碼...... – 2010-09-24 08:23:46

0

這裏是
的一個實例如何背後的操作系統邏輯「發送到... |郵件收件人」上下文菜單項可以從Delphi應用程序用來打開默認郵件客戶端,顯示新郵件與通過連接(選擇)文件:

How can I simulate ‘Send To…’ with Delphi?

相關問題