Как получить доступ к элементам контекстного меню оболочки Windows?
в Проводнике Windows щелкните правой кнопкой мыши файл, появится контекстное меню, которое содержит встроенные элементы, такие как "отправить"..."и / или сторонние действия, такие как "zip-файл с Winzip". Мой вопрос:
- как получить полный список доступных пунктов меню для конкретного файла?
- для каждого пункта меню, Как получить подпись?
- как вызвать определенное действие пункта меню для определенного файла диска?
спасибо вперед!
[EDIT]: в то время как другая информация абсолютно полезна, решение Delphi будет высоко оценено!
3 ответов
ключ для получения контекстного меню оболочки используется IContextMenu
интерфейс.
проверьте эту замечательную статью Shell context menu support
для получения более подробной информации.
обновление
для примеров delphi вы можете увидеть JclShell блок от Jedi JCL (проверьте DisplayContextMenu
функция) и блок ShellCtrls, включенный в папку samples Delphi.
короткий ответ:
попробовать Компоненты ShellBrowser из программного обеспечения JAM. У них есть компонент, который позволит вам показать контекстное меню Проводника с вашими собственными командами, смешанными из TPopupMenu.
ответ
получение меню Проводника, запрос всех его свойств и размещение их в вашем собственном меню возможно, но вам действительно должно быть удобно читать / писать низкоуровневый код Win32 и рабочее знание C поможет. Вам также нужно будет следить за некоторыми gotchas (см. ниже). Я настоятельно рекомендую читать как разместить IContextMenu серия для много технических деталей.
на легче подход для запроса интерфейс IContextMenu, то с ним hmenu, затем с помощью TrackPopupMenu позволить иметь Windows показать меню, затем вызвать InvokeCommand в конце.
некоторые из кода ниже не проверено или было изменено из того, что мы используем, поэтому действуйте на свой страх и риск.
вот как вы получить IContextMenu, для группы файлов в базовой папки:
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;
чтобы получить актуальные пункты меню, вам нужно позвонить IContextMenu.QueryContextMenu. Вы можете уничтожить возвращенный HMENU, используя DestroyMenu.
function GetExplorerHMenu(const AContextMenu: IContextMenu): HMENU;
const
MENUID_FIRST = 1;
MENUID_LAST = FFF;
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;
вот как вы на самом деле вызываете команду, выбранную пользователем из меню:
procedure InvokeCommand(const AContextMenu: IContextMenu; AVerb: PChar);
const
CMIC_MASK_SHIFT_DOWN = 000000;
CMIC_MASK_CONTROL_DOWN = 000000;
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;
теперь вы можете использовать GetMenuItemInfo функция для получения заголовка, растрового изображения и т. д., Но гораздо проще вызвать TrackPopupMenu и пусть окна показывают всплывающее меню. Это будет выглядеть примерно так:
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;
если вы действительно хотите извлечь пункты меню/подписи и добавить их в свое собственное всплывающее меню (мы используем Toolbar 2000 и делаем именно это), вот другие большие проблемы, которые вы будете запускать в:
- меню "Отправить" и любые другие, которые построены по требованию, не будут работать, если вы не обрабатываете сообщения и не передаете их интерфейсам IContextMenu2/IContextMenu3.
- растровые изображения меню находятся в нескольких различных форматах. Delphi не обрабатывает Vista с высоким цветом без уговоров, а старые смешиваются с цветом фона с помощью XOR.
- некоторые пункты меню нарисованы владельцем, поэтому вам нужно захватить сообщения краски и иметь их нарисуйте свой собственный холст.
- строки подсказок не будут работать, если вы вручную не запросите их.
- вам нужно будет управлять временем жизни IContextMenu и HMENU и выпускать их только после закрытия всплывающего меню.
вот пример того, как логика операционной системы за "отправить ... Пункт контекстного меню" / mail recipient " можно использовать из приложения Delphi для открытия почтового клиента по умолчанию, отображая новую почту с переданными (выбранными) файлами: