等待IContextMenu.InvokeCommand启动的过程 [英] Wait for process started by 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 AnsiString
s? 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
, SHELLEXECUTEINFO
和
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屋!