2009-10-17 49 views
7

我想寫以下過程/函數:如何使用Delphi彈出給定文件的Windows上下文菜單?

procedure ShowSysPopup(aFile: string; x, y: integer); 

將建立並顯示(在座標x和y),其中一個人看到在Windows資源管理器中的給定文件的右鍵外殼菜單。我對「展示」部分不太感興趣,但更多的是關於如何構建這樣的菜單。

回答

16

我爲您做了一個快速解決方案。 這些單位增加了「使用」部分:

... ShlObj, ActiveX, ComObj 

,這裏是你的程序,我只需要添加新的參數「HND」隨身攜帶,你會用它來顯示上下文菜單的TWinControl的手柄。

procedure ShowSysPopup(aFile: string; x, y: integer; HND: HWND); 
var 
    Root: IShellFolder; 
    ShellParentFolder: IShellFolder; 
    chEaten,dwAttributes: ULONG; 
    FilePIDL,ParentFolderPIDL: PItemIDList; 
    CM: IContextMenu; 
    Menu: HMenu; 
    Command: LongBool; 
    ICM2: IContextMenu2; 

    ICI: TCMInvokeCommandInfo; 
    ICmd: integer; 
    P: TPoint; 
Begin 
    OleCheck(SHGetDesktopFolder(Root));//Get the Desktop IShellFolder interface 

    OleCheck(Root.ParseDisplayName(HND, nil, 
    PWideChar(WideString(ExtractFilePath(aFile))), 
    chEaten, ParentFolderPIDL, dwAttributes)); // Get the PItemIDList of the parent folder 

    OleCheck(Root.BindToObject(ParentFolderPIDL, nil, IShellFolder, 
    ShellParentFolder)); // Get the IShellFolder Interface of the Parent Folder 

    OleCheck(ShellParentFolder.ParseDisplayName(HND, nil, 
    PWideChar(WideString(ExtractFileName(aFile))), 
    chEaten, FilePIDL, dwAttributes)); // Get the relative PItemIDList of the File 

    ShellParentFolder.GetUIObjectOf(HND, 1, FilePIDL, IID_IContextMenu, nil, CM); // get the IContextMenu Interace for the file 

    if CM = nil then Exit; 
    P.X := X; 
    P.Y := Y; 

    Windows.ClientToScreen(HND, P); 

    Menu := CreatePopupMenu; 

    try 
    CM.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE or CMF_CANRENAME); 
    CM.QueryInterface(IID_IContextMenu2, ICM2); //To handle submenus. 
    try 
     Command := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or 
     TPM_RETURNCMD, p.X, p.Y, 0, HND, nil); 
    finally 
     ICM2 := nil; 
    end; 

    if Command then 
    begin 
     ICmd := LongInt(Command) - 1; 
     FillChar(ICI, SizeOf(ICI), #0); 
     with ICI do 
     begin 
     cbSize := SizeOf(ICI); 
     hWND := 0; 
     lpVerb := MakeIntResourceA(ICmd); 
     nShow := SW_SHOWNORMAL; 
     end; 
     CM.InvokeCommand(ICI); 
    end; 
    finally 
    DestroyMenu(Menu) 
    end; 
End; 

修改/添加初始化,定稿節這樣

initialization 
    OleInitialize(nil); 
finalization 
    OleUninitialize; 

,在這裏你可以如何使用這個程序:

procedure TForm2.Button1Click(Sender: TObject); 
begin 
    ShowSysPopup(Edit1.Text,Edit1.Left,Edit1.Top, Handle); 
end; 

我希望這會爲你工作。

問候,

編輯: 如果你想顯示多個文件檢查上下文菜單this article in my blog

+0

這似乎有點不完整,IContextMenu2消息沒有處理,即HandleMenuMsg沒有響應菜單消息被調用。因此,某些子菜單(如'打開')將不會填充。 [Here](http://stackoverflow.com/a/5287265)是我正在談論的一個例子。 – 2012-11-09 23:20:35

+0

此外,沒有實現IShellCommandVerb接口的類,您的布爾型「Handled」參數和接口本身不提供任何服務。正如你在代碼中看到的那樣,你正在查詢* nil *如果它支持接口,當然你從來沒有被賦予接口,只是擺脫了那麼多不必要的代碼和不必要的類型聲明。 – 2012-11-10 00:31:19

+0

我冒昧地自己做。這個語句「if支持(nil,IShellCommandVerb,SCV)」真的很突出。請更正您的博客上的代碼..我的第一個評論仍然有效,但。 – 2012-11-15 23:18:16

2

你確定這就是你想要做的嗎?因爲如果你這樣做,你實際上將不得不重現Windows Shell中的所有代碼以及它的所有行爲和與大量代碼的交互。

上下文菜單基本上由「shell擴展」構成。這些是COM DLL在系統中註冊的。當調用上下文菜單時,shell遵循一組規則,這些規則確定擴展DLL的位置(在註冊表中)。

I found this to be a useful guide to these rules

但找到擴展DLL的甚至不是故事的一半。對於每個DLL,shell然後實例化由該DLL註冊的COM對象,並通過配置或調用菜單命令來調用DLL響應的那些對象。

shell本身並不構建菜單,也不需要構建可供查詢的菜單或從任何地方直接讀取菜單 - 菜單完全由shell擴展動態構建。

shell將一個句柄傳遞給每個擴展的菜單,還有一些信息告訴擴展名它將用於添加到該菜單的任何項目的命令ID。該擴展可以添加幾乎任何它喜歡的菜單句柄,包括子菜單等,它可能會添加不同的項目,取決於當前文件選擇的屬性,而不僅僅是文件擴展名(例如龜鱉SVN客戶端添加根據與這些文件的當前SVN狀態相關的不同菜單項)。所以如果你想自己構建一個這樣的菜單,就像我說的那樣,你將不得不復制整個shell擴展框架(或者至少是那些初始化菜單的那些部分,假設出於某種原因,你不需要然後需要或需要調用自己的菜單命令)在您自己的代碼中。

也許這可能有助於解釋您爲什麼希望做到這一點以及嘗試實現什麼。可能有更簡單的方法去做。

+0

看起來有可能是一個辦法做到這一點不復制所有shell代碼畢竟。我發現這是一個.NET示例。 http://www.andrewvos.com/?p=420 – Deltics 2009-10-17 23:44:10

0

儘管我同意Deltics認爲它是很多工作,但大多數(如果不是全部)項目所需的信息在註冊表中是免費提供的。在Deltics中列出的指南的答案看起來不錯,並會給你大部分的項目。很多可以在註冊表中查找基本條目,而其他需要調用COM對象。

相關問題