2010-09-23 12 views
11

W Eksploratorze Windows kliknij prawym przyciskiem myszy plik, pojawi się menu kontekstowe zawierające wbudowane elementy, takie jak "Wyślij do ..." i/lub akcje stron trzecich, takie jak "plik zip z Winzip". Moje pytanie to:Jak uzyskać dostęp do elementów menu kontekstowego powłoki systemu Windows?

  • Jak uzyskać pełną listę dostępnych pozycji menu dla określonego pliku?
  • Dla każdej pozycji menu, jak uzyskać podpis?
  • Jak wywołać określone działanie elementu menu dla określonego pliku dysku?

Z góry dziękuję!

[EDYCJA]: Podczas gdy inne informacje są absolutnie przydatne, rozwiązanie Delphi będzie docenione!

+0

chcesz tworzyć własne elementy menu kontekstowego lub manipulować istniejące pozycje menu? – Liton

+0

@Liton, nie tworząc własnych elementów menu kontekstowego powłoki, ale aby manipulować istniejącymi elementami wbudowanymi w menu lub elementami trzeciej części. –

Odpowiedz

9

Kluczem do uzyskania menu kontekstowego skorupy jest użycie interfejsu IContextMenu.

sprawdź ten świetny artykuł Shell context menu support, aby uzyskać więcej informacji.

UPDATE

przykładów delphi można zobaczyć JclShell urządzenie od Jedi JCL (sprawdzić działanie DisplayContextMenu) i jednostkę ShellCtrls zawarte w folderze próbek Delphi.

+0

Dzięki za link, sprawdzę to! –

+0

Witaj RRUZ, Funkcjonalność DisplayContextMenu z pakietu JCL robi dokładnie to, co chcę! Dziękuję Ci! –

7

Krótka odpowiedź

Spróbuj ShellBrowser Components z JAM Software. Mają komponent, który pozwoli ci pokazać menu kontekstowe Eksploratora za pomocą własnych poleceń wymieszanych z TPopupMenu.


Długa odpowiedź

Pierwsze menu Explorer, odpytywanie wszystkich jego właściwości, a gospodarzem je w swoim menu jest możliwe, ale naprawdę powinny być wygodne czytanie/pisanie niskiego poziomu kodu Win32 i pomocna będzie znajomość języka C. Będziesz także musiał uważać na kilka gotchów (omówionych poniżej). Zdecydowanie polecam przeczytanie serii How to host an IContextMenu Raymonda Chena w celu uzyskania wielu szczegółów technicznych.

Podejście to poleganie na zapytaniu o interfejs IContextMenu, następnie HMENU, a następnie użycie TrackPopupMenu, aby system Windows wyświetlał menu, a następnie wywoływanie funkcji InvokeCommand na końcu.

Niektóre z poniższych kodów są nieprzetestowane lub zostały zmienione z tego, czego używamy, więc kontynuuj na własne ryzyko.

Oto jak masz IContextMenu dla grupy plików w folderze bazowej:

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; 

Aby uzyskać rzeczywiste elementy menu musisz zadzwonić IContextMenu.QueryContextMenu. Możesz zniszczyć zwrócone HMENU używając DestroyMenu.

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; 

Oto jak faktycznie wywołać polecenie, które użytkownik wybrał z menu:

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; 

Teraz można korzystać z funkcji GetMenuItemInfo uzyskać podpis, bitmapy, etc, ale o wiele łatwiej podejście to zadzwonić pod numer TrackPopupMenu i pozwolić systemowi Windows na wyświetlenie menu podręcznego. Że będzie wyglądać mniej więcej tak:

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; 

Jeśli rzeczywiście chcesz wyodrębnić menu przedmioty/tytuły i dodać je do swojego menu kontekstowego (używamy Toolbar 2000 i robić dokładnie to), tutaj są inne duże problemy będzie prowadzony w:

  • „Wyślij do” menu, a także wszelkie inne, które zostały zbudowane na żądanie nie będzie działać, jeśli nie odbierać wiadomości i przekazać je do interfejsów IContextMenu2/IContextMenu3.
  • Mapa bitowa menu ma kilka różnych formatów. Delphi nie radzi sobie z wysokokolorowymi kolorami Vista bez zachęcania, a starsze są mieszane z kolorem tła za pomocą XOR.
  • Niektóre pozycje menu są rysowane przez właściciela, więc musisz przechwytywać wiadomości tekstowe i malować je na swoim płótnie.
  • Łańcuchy podpowiedzi nie będą działać, dopóki ręcznie ich nie zapytasz.
  • Musisz zarządzać czasem życia IContextMenu i HMENU i zwolnić je dopiero po zamknięciu menu podręcznego.
+0

Cześć Craig, Dzięki za szczegółowe informacje techniczne, są one bardzo pomocne! Ale muszę zaakceptować odpowiedź RRUZ, ponieważ wskazał mi funkcję DisplayContextMenu z JCL, która robi dokładnie to, co chcę z pojedynczą linią ... –

0

Oto exmple jak logika systemu operacyjnego za „Wyślij do ... | adresat poczty” z menu kontekstowego mogą być używane z aplikacji Delphi, aby otworzyć domyślnego klienta poczty, wyświetlając nową wiadomość z mijały (wybrane) pliki załączone:

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

Powiązane problemy