2009-10-17 23 views
7

Tôi muốn viết các thủ tục/hàm sau:Làm cách nào để bật trình đơn ngữ cảnh của Windows cho một tệp nhất định bằng Delphi?

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

Mà sẽ xây dựng và hiển thị (tại tọa độ x và y) menu vỏ nhấp chuột phải mà ta thấy trong Windows Explorer cho các tập tin nhất định. Tôi không quan tâm đến phần 'hiển thị' nhưng nhiều hơn về cách người ta có thể xây dựng một menu như vậy.

Trả lời

16

Tôi đã thực hiện giải pháp nhanh cho bạn. thêm các đơn vị này vào "Sử dụng" phần:

... ShlObj, ActiveX, ComObj 

và đây là quy trình của bạn, tôi chỉ cần thêm mới tham số "HND" để thực hiện các xử lý của TWinControl mà bạn sẽ sử dụng để hiển thị Menu ngữ cảnh.

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; 

sửa đổi/bổ sung khởi, phần hoàn thiện như thế này

initialization 
    OleInitialize(nil); 
finalization 
    OleUninitialize; 

và ở đây làm thế nào bạn có thể sử dụng thủ tục này:

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

Tôi hy vọng điều này sẽ làm việc cho bạn.

Kính trọng,

Edit: nếu bạn muốn hiển thị menu ngữ cảnh để kiểm tra nhiều hơn một tập tin this article in my blog

+0

Điều này có vẻ hơi không đầy đủ, các tin nhắn IContextMenu2 không được xử lý, tức là HandleMenuMsg không được gọi để trả lời các thông báo menu. Kết quả là một số menu phụ (như 'Open with') sẽ không được điền. [Ở đây] (http://stackoverflow.com/a/5287265) là một ví dụ về những gì tôi đang nói đến. –

+0

Ngoài ra, nếu không có một lớp thực hiện giao diện IShellCommandVerb, tham số boolean 'Handled' của bạn và giao diện chính nó không phục vụ gì cả. Như bạn thấy trong mã của bạn, bạn đang truy vấn * nil * nếu nó hỗ trợ giao diện, tất nhiên bạn không bao giờ đưa ra giao diện, chỉ cần loại bỏ rất nhiều mã không cần thiết và khai báo kiểu không cần thiết. –

+0

Tôi tự do làm điều đó. Câu lệnh này 'nếu Support (nil, IShellCommandVerb, SCV) thì' đã thực sự nổi bật. Xin vui lòng, cũng sửa mã trên blog của bạn .. Bình luận đầu tiên của tôi vẫn còn hợp lệ mặc dù. –

2

Bạn có chắc đó là những gì bạn muốn làm không? Bởi vì nếu bạn làm thế, bạn có hiệu quả sẽ phải sao chép tất cả các mã trong Windows Shell và tất cả các hành vi và tương tác của nó với một máy chủ lưu trữ toàn bộ mã.

Trình đơn ngữ cảnh cơ bản được xây dựng bởi "tiện ích mở rộng vỏ". Đây là những COM DLL đăng ký với hệ thống. Khi trình đơn ngữ cảnh được gọi, trình bao theo sau một tập hợp các quy tắc xác định nơi cần tìm (trong sổ đăng ký) cho phần mở rộng của DLL.

I found this to be a useful guide to these rules.

Nhưng việc tìm DLL của tiện ích mở rộng thậm chí không phải là một nửa câu chuyện. Đối với mỗi DLL shell sau đó instantiates các đối tượng COM (s) đăng ký bởi DLL đó và làm cho các cuộc gọi đến những đối tượng mà DLL của phản ứng bằng cách cấu hình hoặc gọi lệnh menu.

Bản thân trình vỏ không xây dựng menu, cũng như thông tin cần thiết để tạo trình đơn có sẵn để được truy vấn hoặc đọc trực tiếp từ bất kỳ đâu - trình đơn được xây dựng hoàn toàn tự động bởi tiện ích mở rộng trình bao.

Shell sẽ xử lý trình đơn cho từng tiện ích mở rộng, cùng với một số thông tin cho tiện ích biết ID nào sẽ sử dụng cho bất kỳ mục nào mà nó thêm vào menu đó. Phần mở rộng có thể thêm khá nhiều bất cứ điều gì nó thích vào menu xử lý nó được đưa ra, bao gồm cả menu phụ vv, và nó cũng có thể thêm các mục khác nhau tùy thuộc vào thuộc tính của tập tin hiện tại chọn, không chỉ phần mở rộng tập tin (ví dụ như khách hàng Tortoise SVN thêm các mục menu khác nhau tùy theo những gì liên quan đến trạng thái SVN hiện tại của các tệp đó). Vì vậy, nếu bạn muốn tự xây dựng một trình đơn như vậy, bạn sẽ phải sao chép toàn bộ khung công tác mở rộng của trình bao (hoặc ít nhất là các phần của nó để khởi tạo trình đơn, giả sử vì một lý do nào đó mà bạn không thực hiện được). sau đó muốn hoặc cần phải gọi các lệnh menu) trong mã của riêng bạn.

Có lẽ điều đó có thể hữu ích nếu bạn giải thích lý do tại sao bạn muốn thực hiện điều này và những gì bạn đang cố gắng đạt được. Có thể có một cách dễ dàng hơn để đi về nó.

+0

Hình như có thể là một cách để làm điều đó mà không cần sao chép tất cả những gì mã shell sau khi tất cả . Tôi tìm thấy điều này nhưng đó là một ví dụ NET. http://www.andrewvos.com/?p=420 – Deltics

0

Mặc dù tôi đồng ý với Deltics rằng nó là rất nhiều công việc, thông tin cần thiết cho hầu hết (nếu không phải tất cả) của các mục này đều có sẵn trong sổ đăng ký. Các hướng dẫn được liệt kê trong Deltics câu trả lời nhìn tốt và sẽ cung cấp cho bạn hầu hết các mặt hàng. Rất nhiều có thể được tra cứu hình thức các mục cơ bản trong registry trong khi những người khác cần các cuộc gọi đến các đối tượng COM.

Các vấn đề liên quan