等待IContextMenu.InvokeCommand启动的过程 [英] Wait for process started by IContextMenu.InvokeCommand

查看:88
本文介绍了等待IContextMenu.InvokeCommand启动的过程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 TListView ,其项目是文件,用户可以通过双击打开它们.

I have a TListView whose items are files, which the user can open via double clicking on them.

为此,我将文件保存在Windows temp文件夹中,启动一个线程,该线程使用 ShellExecuteEx()打开保存的文件,然后等待 ShellExecuteInfo.hProcess ,就像这样:

To do this, I save the file in the windows temp folder, start a thread that opens the saved file with ShellExecuteEx(), and let it wait for ShellExecuteInfo.hProcess, like this:

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 事件在用户关闭文件后回写对该文件所做的任何更改.

This way, I can use the TThread.OnTerminate event to write back any changes made to the file after the user closes it.

我现在借助 JclShell.DisplayContextMenu()(使用 IContextMenu )显示Windows上下文菜单.

I now show the windows context menu with the help of JclShell.DisplayContextMenu() (which uses IContextMenu).

我的目标:等待上下文菜单中选择的已执行操作(例如属性",删除"等)完成(或以任何方式获得通知),以便我可以检查临时文件中的更改以将其写回,或在删除的情况下删除 TListItem .

MY GOAL: To wait for the performed action (e.g. 'properties' , 'delete', ..) chosen in the context menu to finish (or get notified in any kind of fashion), so that I can check the temporary file for changes to write those back, or remove the TListItem in case of deletion.

由于 CMINVOKECOMMANDINFO 不会像 SHELLEXECUTEINFO 那样返回进程句柄,所以我无法以相同的方式进行操作.

Since CMINVOKECOMMANDINFO does not return a process handle like SHELLEXECUTEINFO does, I am unable to do it in the same way.

MakeIntResource(commandId-1)分配给 SHELLEXECUTEINFO.lpVerb 时,对 ShellExecuteEx()的调用因 EAccessViolation . SHELLEXECUTEINFO 似乎不支持此方法.

Assigning MakeIntResource(commandId-1) to SHELLEXECUTEINFO.lpVerb made the call to ShellExecuteEx() crash with an EAccessViolation. This method seems unsupported for SHELLEXECUTEINFO.

我尝试使用 IContextMenu.GetCommandString()获取命令字符串,并从 TrackPopupMenu()中获取命令ID,以便稍后将其传递给 SHELLEXECUTEINFO.lpVerb,但是 GetCommandString()不会为某些单击的项目返回命令.

I have tried to get the command string with IContextMenu.GetCommandString() and the command ID from TrackPopupMenu() to later pass it to SHELLEXECUTEINFO.lpVerb, but GetCommandString() wouldn't return commands for some items clicked.

工作菜单项:

属性,编辑,复制,剪切,打印,7z:添加到存档(动词为"SevenZipCompress",不会返回processHandle),KapserskyScan(动词是"KL_scan",不会返回processHandle)

properties, edit, copy, cut, print, 7z: add to archive (verb is 'SevenZipCompress', wont return processHandle), KapserskyScan (verb is 'KL_scan', wont return processHandle)

不起作用:

打开方式"或发送给"中的任何内容

anything within "open with" or "send to"

这仅仅是 IContextMenu 实现的错误吗?

Is this simply the fault of the IContextMenu implementation?

也许与我对 AnsiString s的使用有关?不过,我无法获得 GCS_VERBW 的帮助.有没有比这更好的方法来可靠地获取 CommandString ?

Maybe it has something to do with my use of AnsiStrings? I couldn't get GCS_VERBW to work, though. Are there better ways to reliably get the CommandString than this?

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

I have read Raymond Chen's blog on How to host an IContextMenu, as well as researched on MSDN (for example CMINVOKECOMMANDINFO, GetCommandString(), SHELLEXECUTEINFO and TrackPopupMenu()), but I might have missed something trivial.

推荐答案

我最终使用了 TJvChangeNotify 来监视Windows临时文件夹,同时将受监视的文件保留在 TDictionary< FileName:String,LastWrite:TDateTime> 中.

I ended up using TJvChangeNotify to monitor the windows temp folder, while keeping the monitored-files in a TDictionary<FileName:String, LastWrite: TDateTime>.

因此,只要TJvChangeNotify触发 OnChangeNotify 事件,我就可以检查我的哪些受监视文件已删除(通过检查是否存在)或已更改(通过比较上次写入时间).

So whenever TJvChangeNotify fires the OnChangeNotify event, i can check which of my monitored-files have been deleted (by checking existence) or have changed (by comparing the last write time).

示例 ChangeNotifyEvent :

procedure TFileChangeMonitor.ChangeNotifyEvent(Sender: TObject; Dir: string;
  Actions: TJvChangeActions);
var
  LFile: TPair<String, TDateTime>;
  LSearchRec: TSearchRec;
  LFoundErrorCode: Integer;
begin
  for LFile in FMonitoredFiles do
  begin
    LFoundErrorCode := FindFirst(LFile.Key, faAnyFile, LSearchRec);
    try
      if LFoundErrorCode = NOERROR then
      begin
        if LSearchRec.TimeStamp > LFile.Value then
        begin
          // do something with the changed file
          {...}

          // update last write time
          FMonitoredFiles.AddOrSetValue(LFile.Key, LSearchRec.TimeStamp);
        end;
      end // 
      else if (LFoundErrorCode = ERROR_FILE_NOT_FOUND) then
      begin
        // do something with the deleted file
        {...}

        // stop monitoring the deleted file
        FMonitoredFiles.Remove(LFile.Key);
      end;
    finally
      System.SysUtils.FindClose(LSearchRec);
    end;
  end;
end;

这篇关于等待IContextMenu.InvokeCommand启动的过程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆