2011-03-12 107 views

回答

8

Kermia從JEDI JCL庫檢查JclShell單元,本單元內部存在一個名爲DisplayContextMenu的函數,它顯示與文件關聯的上下文菜單。這個函數將對IContextMenu接口的調用封裝起來,使你的工作變得更容易。

function DisplayContextMenu(const Handle: HWND; const FileName: string; 
    Pos: TPoint): Boolean; 
+0

+1你作爲Handle參數傳遞了什麼?它是幹什麼用的? – 2011-03-12 19:22:52

+0

@David你可以傳遞你的表單的句柄,這個句柄用在'IContextMenu.InvokeCommand'中,它接收'CMINVOKECOMMANDINFO'結構http://msdn.microsoft.com/en-us/library/bb773215%28v= vs.85%29.aspx。 – RRUZ 2011-03-12 19:48:29

+0

這個單位有任何更新的版本? (德爾福2010) – Kermia 2011-03-13 09:39:50

2

當你想在你的Delphi應用程序中顯示類似控件的shell時,我建議你看看類似tpShellShock的東西。它提供了樹視圖,列表視圖等,它們可以像Explorer Windows一樣連接在一起。它將顯示文件的相應圖標。我相信它也提供了你所談論的設施。

如果您使用的是現代Unicode Delphi,那麼可能需要一些移植工作,但是當我這樣做時,它證明是相對簡單的。

毫無疑問,還有其他的庫提供了shell控件,這只是我熟悉的一個。

否則,如果您想堅持使用您當前的解決方案,那麼實現您自己的菜單操作是最容易的。打開和屬性只是簡單的調用ShellExecute與適當的動詞。 Delete是對DeleteFile的調用,Rename是對MoveFile的調用。

5

檢查IContextMenu接口。但請注意,Windows shell不會通過文件名識別它的對象 - 實際上它們不能是文件。它使用ID的串聯,並且您可能需要在調用其上的某些shell函數之前獲取文件所支持的項目ID列表。

+0

看起來像它可以讓你枚舉shell認爲是在上下文菜單中。在我閱讀它時,您仍然需要自己填充菜單,然後通過此界面調用命令來響應Delphi OnClick事件。那是對的嗎? – 2011-03-12 11:45:19

+0

+1當然看起來是實現OnClick處理程序的好方法。 – 2011-03-12 13:38:51

+0

是的,你必須創建菜單(至少它允許你使用任何你喜歡的菜單),無論如何它給你每個元素支持的命令,並讓你調用shell定義的正確命令。它只對文件不起作用,儘管這超出了用戶的問題。 – 2011-03-12 15:17:33

3

下面是使用一個列表框,它是在項目目錄中填入文件名的「OnContextPopup」事件,啓動的文件的快捷菜單上的名字時,右鍵單擊一個實現示例:

type 
    TForm1 = class(TForm) 
    ListBox1: TListBox; 
    procedure FormCreate(Sender: TObject); 
    procedure ListBox1ContextPopup(Sender: TObject; MousePos: TPoint; 
     var Handled: Boolean); 
    private 
    protected 
    procedure WndProc(var Msg: TMessage); override; 
    public 
    end; 

var 
    Form1: TForm1; 

implementation 

uses 
    shlobj, comobj; 

{$R *.dfm} 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    SearchRec: TSearchRec; 
begin 
    ListBox1.Clear; 

    // populate list box with files in the project folder 
    if FindFirst(ExtractFilePath(Application.ExeName) + '*.*', 
       0, SearchRec) = 0 then 
    repeat 
     ListBox1.Items.Add(SearchRec.Name); 
    until FindNext(SearchRec) <> 0; 
    FindClose(SearchRec); 
end; 

var 
    // Required to handle messages for owner drawn items, as in 'SendTo' menu. 
    // Also used as a flag in WndProc to know if we're tracking a shortcut menu. 
    ContextMenu2: IContextMenu2 = nil; 

procedure TForm1.ListBox1ContextPopup(Sender: TObject; MousePos: TPoint; 
    var Handled: Boolean); 
var 
    Item: Integer; 
    DeskFolder, Folder: IShellFolder; 
    Eaten, Attributes: ULONG; 
    pIdl, FolderpIdl: PItemIDList; 
    ContextMenu: IContextMenu; 
    Menu: HMENU; 
    Pos: TPoint; 
    Cmd: DWORD; 
    CommandInfo: TCMInvokeCommandInfo; 
begin 
    Item := (Sender as TListBox).ItemAtPos(MousePos, True); 
    Handled := Item <> -1; 
    if not Handled then 
    Exit; 
    TListBox(Sender).ItemIndex := Item; 

    // IShellFolder for Desktop folder (root) 
    OleCheck(SHGetDesktopFolder(DeskFolder)); 

    // Item ID List for the folder that the file is in 
    Attributes := 0; 
    OleCheck(DeskFolder.ParseDisplayName(Handle, nil, 
        PWideChar(WideString(ExtractFilePath(Application.ExeName))), 
        Eaten, FolderpIdl, Attributes)); 

    // IShellFolder for the folder the file is in 
    OleCheck(DeskFolder.BindToObject(FolderpIdl, nil, IID_IShellFolder, Folder)); 
    CoTaskMemFree(FolderpIdl); 

    // Item ID List for the file, relative to the folder it is in 
    Attributes := 0; 
    OleCheck(Folder.ParseDisplayName(Handle, nil, 
      PWideChar(WideString(ExtractFileName(TListBox(Sender).Items[Item]))), 
      Eaten, pIdl, Attributes)); 

    // IContextMenu for the relative Item ID List 
    OleCheck(Folder.GetUIObjectOf(Handle, 1, pIdl, IID_IContextMenu, 
            nil, ContextMenu)); 
    CoTaskMemFree(pIdl); 

    Menu := CreatePopupMenu; 
    try 
    // Populate our menu with shortcut items 
    OleCheck(ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE)); 

    // ContextMenu2 used in WndProc 
    ContextMenu.QueryInterface(IID_IContextMenu2, ContextMenu2); 
    try 
     Pos := TWinControl(Sender).ClientToScreen(MousePos); 
     // launch the menu 
     Bool(Cmd) := TrackPopupMenu(Menu, 
          TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD, 
          Pos.X, Pos.Y, 0, Handle, nil); 
    finally 
     // clear so that we don't intervene every owner drawn menu item message in 
     // WndProc 
     ContextMenu2 := nil; 
    end; 

    // Invoke command if we have one 
    if Bool(Cmd) then begin 
     FillChar(CommandInfo, SizeOf(CommandInfo), 0); 
     CommandInfo.cbSize := SizeOf(CommandInfo); 
     CommandInfo.hwnd := Handle; 
     CommandInfo.lpVerb := MakeIntResource(Cmd - 1); 
     CommandInfo.nShow := SW_SHOWNORMAL; 

     OleCheck(ContextMenu.InvokeCommand(CommandInfo)); 
    end; 

    finally 
    DestroyMenu(Menu); 
    end; 
end; 

procedure TForm1.WndProc(var Msg: TMessage); 
begin 
    if ((Msg.Msg = WM_INITMENUPOPUP) or (Msg.Msg = WM_DRAWITEM) 
       or (Msg.Msg = WM_MEASUREITEM)) and Assigned(ContextMenu2) then 
    ContextMenu2.HandleMenuMsg(Msg.Msg, Msg.WParam, Msg.LParam) 
    else 
    inherited; 
end;