У меня есть TListView
, элементы которого являются файлами, которые пользователь может открыть, дважды щелкнув по ним.
Чтобы сделать это, я сохраняю файл во временной папке Windows, запускаю поток, который открывает сохраненный файл с помощью ShellExecuteEx()
, и позволяет ему ждать ShellExecuteInfo.hProcess
, например:
TNotifyThread = class(TThread)
private
FFileName: string;
FFileAge: TDateTime;
public
constructor Create(const FileName: string; OnClosed: TNotifyEvent); overload;
procedure Execute; override;
property FileName: String read FFileName;
property FileAge: TDateTime read FFileAge;
end;
{...}
constructor TNotifyThread.Create(const FileName: string; OnClosed: TNotifyEvent);
begin
inherited Create(True);
if FileExists(FileName) then
FileAge(FileName, FFileAge);
FreeOnTerminate := True;
OnTerminate := OnClosed;
FFileName := FileName;
Resume;
end;
procedure TNotifyThread.Execute;
var
se: SHELLEXECUTEINFO;
ok: boolean;
begin
with se do
begin
cbSize := SizeOf(SHELLEXECUTEINFO);
fMask := SEE_MASK_INVOKEIDLIST or SEE_MASK_NOCLOSEPROCESS or SEE_MASK_NOASYNC;
lpVerb := PChar('open');
lpFile := PChar(FFileName);
lpParameters := nil;
lpDirectory := PChar(ExtractFilePath(ParamStr(0)));
nShow := SW_SHOW;
end;
if ShellExecuteEx(@se) then
begin
WaitForSingleObject(se.hProcess, INFINITE);
if se.hProcess <> 0 then
CloseHandle(se.hProcess);
end;
end;
Таким образом, я могу использовать событие TThread.OnTerminate
для записи любых изменений, внесенных в файл после его закрытия пользователем.
Теперь я показываю контекстное меню Windows с помощью JclShell.DisplayContextMenu()
(который использует IContextMenu
).
МОЯ ЦЕЛЬ: дождаться завершения выполненного действия (например, «Свойства», «Удалить», ..), выбранного в контекстном меню (или получить уведомление)любым способом), чтобы я мог проверить временный файл на наличие изменений, чтобы записать их обратно, или удалить TListItem
в случае удаления.
, поскольку CMINVOKECOMMANDINFO
не возвращаетдескриптор процесса, как SHELLEXECUTEINFO
делает, я не могу сделать это таким же образом.
Назначение MakeIntResource(commandId-1)
на SHELLEXECUTEINFO.lpVerb
сделало вызов ShellExecuteEx()
сбой с EAccessViolation
.Этот метод кажется неподдерживаемым для SHELLEXECUTEINFO
.
Я пытался получить командную строку с IContextMenu.GetCommandString()
и идентификатор команды из TrackPopupMenu()
, чтобы позже передать ее в SHELLEXECUTEINFO.lpVerb
, но GetCommandString()
не будетt возвращать команды для некоторых элементов, по которым щелкнули.
рабочие элементы меню:
свойства, редактировать, копировать, вырезать, печатать, 7z: добавить в архив (глагол«SevenZipCompress», не возвращает processHandle), KapserskyScan (глагол «KL_scan», не возвращает processHandle)
не работает:
что-либов «open with» или «send to»
Это просто ошибка реализации IContextMenu
?
Может быть, это как-то связано с моим использованием AnsiString
s?Я не мог заставить GCS_VERBW
работать, хотя.Есть ли лучшие способы надежного получения CommandString
, чем этот?
function CustomDisplayContextMenuPidlWithoutExecute(const Handle: THandle;
const Folder: IShellFolder;
Item: PItemIdList; Pos: TPoint): String;
var
ContextMenu: IContextMenu;
ContextMenu2: IContextMenu2;
Menu: HMENU;
CallbackWindow: THandle;
LResult: AnsiString;
Cmd: Cardinal;
begin
Result := '';
if (Item = nil) or (Folder = nil) then
Exit;
Folder.GetUIObjectOf(Handle, 1, Item, IID_IContextMenu, nil,
Pointer(ContextMenu));
if ContextMenu <> nil then
begin
Menu := CreatePopupMenu;
if Menu <> 0 then
begin
if Succeeded(ContextMenu.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE)) then
begin
CallbackWindow := 0;
if Succeeded(ContextMenu.QueryInterface(IContextMenu2, ContextMenu2)) then
begin
CallbackWindow := CreateMenuCallbackWnd(ContextMenu2);
end;
ClientToScreen(Handle, Pos);
cmd := Cardinal(TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or
TPM_RIGHTBUTTON or TPM_RETURNCMD, Pos.X, Pos.Y, 0, CallbackWindow, nil));
if Cmd <> 0 then
begin
SetLength(LResult, MAX_PATH);
cmd := ContextMenu.GetCommandString(Cmd-1, GCS_VERBA, nil, LPSTR(LResult), MAX_PATH);
Result := String(LResult);
end;
if CallbackWindow <> 0 then
DestroyWindow(CallbackWindow);
end;
DestroyMenu(Menu);
end;
end;
end;
Я читал блог Раймонда Чена на Как разместить IContextMenu , а также исследовал на MSDN (дляпример CMINVOKECOMMANDINFO
, GetCommandString()
, SHELLEXECUTEINFO
и TrackPopupMenu()
), но я мог пропустить что-то тривиальное.