在Windows资源pipe理器中,右键点击一个文件,会出现一个上下文菜单,其中包含内置项目,如“发送到…”和/或第三方操作,例如“使用Winzip压缩文件”。 我的问题是:
先谢谢你!
[编辑]:虽然其他信息是绝对有用的,delphi解决scheme将非常感激!
获取Shell上下文菜单的关键是使用IContextMenu
接口。
查看这个很棒的文章Shell context menu support
了解更多细节。
UPDATE
对于delphi示例,您可以从JEDI JCL(查看DisplayContextMenu
函数)和Delphi的samples文件夹中包含的ShellCtrls单元中看到JclShell单元。
简短的回答
尝试从JAM软件的ShellBrowser组件 。 他们有一个组件,可以让你显示资源管理器的上下文菜单,并且你可以从TPopupMenu中混入自己的命令。
长答案
获取资源管理器菜单,查询其所有属性并将其托管在自己的菜单中是可能的,但是您真的应该可以自如地阅读/编写低级别的Win32代码,并且对C的使用知识将会有所帮助。 你还需要注意一些陷阱(下面将会介绍)。 我强烈建议阅读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'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 。 你可以使用DestroyMenu销毁返回的HMENU。
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;
如果你真的想提取菜单项/标题,并将它们添加到你自己的弹出菜单(我们使用工具栏2000,并做到这一点),这里是您将遇到的其他重大问题:
下面是一个例子,如何从Delphi应用程序中使用“发送到…邮件收件人”上下文菜单项背后的操作系统逻辑来打开默认的邮件客户端,显示一个新的邮件,附带传递的(选择的)文件:
如何用Delphi模拟“Send To …”?