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.
chcesz tworzyć własne elementy menu kontekstowego lub manipulować istniejące pozycje menu? – Liton
@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. –