Подтвердить что ты не робот

Как получить доступ к элементам контекстного меню оболочки Windows?

В проводнике Windows вы щелкаете правой кнопкой мыши по файлу, появляется контекстное меню, содержащее встроенные элементы, такие как "Отправить в..." и/или действия сторонних разработчиков, такие как "zip файл с Winzip". Мой вопрос:

  • Как получить полный список доступных пунктов меню для определенного файла?
  • Для каждого пункта меню, как получить подпись?
  • Как вызвать конкретное действие элемента меню для определенного файла диска?

Заранее благодарю вас!

[EDIT]: Хотя другая информация абсолютно полезна, решение Delphi будет высоко оценено!

4b9b3361

Ответ 1

Ключ для получения контекстного меню оболочки используется IContextMenu.

проверьте эту замечательную статью Shell context menu support для более подробной информации.

UPDATE

для примеров delphi вы можете увидеть блок JclShell из JEDI JCL (проверьте функцию DisplayContextMenu) и блок ShellCtrls, включенный в образцы папки Delphi.

Ответ 2

Короткий ответ

Попробуйте Компоненты ShellBrowser из программного обеспечения JAM. У них есть компонент, который позволит вам показать контекстное меню Explorer с вашими собственными командами, смешанными с TPopupMenu.


Длинный ответ

Получение меню "Проводник", запрос всех его свойств и размещение их в вашем собственном меню возможно, но вам действительно должно быть удобно читать/писать низкоуровневый код Win32, а работающие знания C помогут. Вам также нужно будет следить за некоторыми gotchas (см. Ниже). Я настоятельно рекомендую прочитать Raymond Chen Как разместить ряд 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 IShellFolder interface
  OleCheck(SHGetDesktopFolder(Desktop));
  // Retrieve the parent folder PItemIDList and then it 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 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 = $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;

Вот как вы на самом деле вызываете команду, которую пользователь выбрал в меню:

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;

Теперь вы можете использовать функцию GetMenuItemInfo для получения заголовка, растрового изображения и т.д., но гораздо проще использовать вызов TrackPopupMenu и пусть Windows покажет всплывающее меню. Это будет выглядеть примерно так:

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 и освобождать их только после закрытия всплывающего меню.

Ответ 3

Вот пример того, как логическая логическая система, связанная с контекстным пунктом "Отправить в..." получателю почты, может быть использована из приложения Delphi для открытия почтового клиента по умолчанию, отображения новой почты с переданной (выбранной ):

Как я могу имитировать" Отправить... с Delphi?