如何使嵌入式Explorer IShellView可浏览(即触发BrowseObject事件)

时间:2011-10-08 17:41:28

标签: windows winapi windows-explorer windows-shell

我在我的Win32应用程序中“嵌入Windows资源管理器”。 (从技术上讲,我在我的应用程序中托管ShellView文件夹,这是Windows资源管理器所做的。)

问题是视图永远不会调用IShellBrowser.BrowseObject。 shell视图不是要求 me 导航到新位置(通过BrowseObject事件),而是启动Windows资源管理器的副本以查看该文件夹。

我希望默认的shell视图(通俗地称为DefView)可以浏览。


示例代码教程

首先,我们需要获取我要显示的某个文件夹的IShellFolder。要获取的最简单的文件夹是Desktop文件夹,因为它有SHGetDesktopFolder API

folder: IShellFolder;

SHGetDesktopFolder({out} folder);

接下来,我们要求桌面文件夹hand给我IShellView

view: IShellView;

folder.CreateViewObject(Self.Handle, IID_IShellView, {out}view);

既然我们有文件夹的 IShellView (而不是 IContextMenu IExtractIcon ),我们现在想要展示shell通过调用IShellView.CreateViewWindow来查看:

hostRect: TRect; //where the view is to display itself
folderSettings: TFolderSettings; //display settings for the view
hwndView: HWND; //the newly created view's window handle

folderSettings.ViewMode := FVM_DETAILS; //details mode please, rather than icon/list/etc
folderSettings.fFlags := 0;
hostRect := Rect(20, 20, 660, 500); //the view can position itself there

view.CreateViewWindow(nil, folderSettings, shellBrowser, {var}hostRect, {out}hView);
view.UIActivate(SVUIA_ACTIVATE_NOFOCUS);

et voila,可识别的shell列表视图,显示我的桌面:

enter image description here

完成上下文菜单处理程序:

enter image description here

除非我点击打开,而不是通过我提供的BrowseObject界面向我发送IShellBrowser事件,否则会打开一个新窗口:

enter image description here

双击时也是如此。

如何让微软的DefView可浏览?


更新 ShellBrowser实现

创建IShellView时,必须为其提供一个实现IShellBrowser的对象。此ShellBrowser对象是视图与主机容器通信的方式。

在15种方法中,我只仔细看四 - 其余的可以返回E_NOTIMPL(这当然可能是问题):

{IShellBrowser}
  • BrowseObject(PCUIDLIST_RELATIVE pidl, UINT wFlags);

    //Informs Windows Explorer to browse to another folder.
    //This is the notification i want!
    Result := BrowseToAnotherFolder(pidl, wFlags);
    
  • GetControlWindow(UINT id, HWND *lphwnd);

    //Gets the window handle to a browser control.
    //Since i don't have a toolbar, tree, status or progress windows, return 0
    lphwnd := 0;
    Result := S_OK;
    
  • SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret);

    //Sends control messages to either the toolbar or the status bar in a Windows 
    //From MSDN: Notes to Implementers
    //    If your Windows Explorer does not have these controls, you can return E_NOTIMPL.
    Result := E_NOTIMPL;
    
  • GetViewStateStream(DWORD grfMode, IStream **ppStrm);

    //Gets an IStream interface that can be used for storage of view-specific state information.
    Result := E_NOTIMPL; //i'm don't have a stream to give you
    
  • TranslateAcceleratorSB(LPMSG lpmsg, WORD wID);

    //Translates accelerator keystrokes intended for the browser's frame 
    //    while the view is active.
    Result := E_NOTIMPL; //i won't be doing any translating
    
  • OnViewWindowActive(IShellView *ppshv);

    //Called by the Shell view when the view window or one of its child 
    //    windows gets the focus or becomes active.
    Result := S_OK; //i got the notification, thanks
    
  • QueryActiveShellView(IShellView **ppshv);

    //Retrieves the currently active (displayed) Shell view object.
    ppshv := view;
    Result := S_OK; //i would never view another, you know that
    
  • EnableModelessSB(BOOL fEnable);

    //Tells Windows Explorer to enable or disable its modeless dialog boxes.
    Result := S_OK; //You want to enable modeless dialog boxes? Interesting.
    
  • InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);

    //Allows the container to insert its menu groups into the composite
    //  menu that is displayed when an extended namespace is being viewed or used.
    Result := E_NOTIMPL; //i no has menus
    
  • RemoveMenusSB(HMENU hmenuShared);

    //Permits the container to remove any of its menu elements 
    //   from the in-place composite menu and to free all associated resources.
    Result := E_NOTIMPL; //i no has menus
    
  • SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject);

    //Installs the composite menu in the view window.
    Result := E_NOTIMPL; //i no has menus
    
  • SetStatusTextSB

    //Sets and displays status text about the in-place object 
    //in the container's frame-window status bar.
    Result := E_NOTIMPL; //i no has status bar
    
  • SetToolbarItems(LPTBBUTTONSB lpButtons, UINT nButtons, UINT uFlags);

    //Adds toolbar items to Windows Explorer's toolbar.
    Result := E_NOTIMPL; //i no has toolbar
    

然后是祖先IOleWindow

  • GetWindow([out] HWND *phwnd);

    //Retrieves a handle to one of the windows participating in 
    //   in-place activation (frame, document, parent, or in-place object window).
    phwnd := Self.Handle; //here's the handle, again, of your parent
    Result := S_OK; 
    
  • `ContextSensitiveHelp([in] BOOL fEnterMode);

    //Determines whether context-sensitive help mode should be entered 
    //during an in-place activation session.
    
    Result := S_OK; //Ok, thanks, i'll make a note of it
    

的IServiceProvider

function TShellBrowser.QueryService(const rsid, iid: TGuid; out Obj): HResult;
var
    sb: IShellBrowser;
    s: string;
const
    SID_SInPlaceBrowser: TGUID = '{1D2AE02B-3655-46CC-B63A-285988153BCA}';
    SID_IShellBrowser: TGUID = '{000214E2-0000-0000-C000-000000000046}';
begin
    {
        This code is executed when you double click a folder.
        It's needed to implement inline browsing.
        If you double click a folder the default action of IShellBrowser is to open a new Windows Explorer.
        To open the folder in the current window you must implement IServiceProvider.

        http://blogs.msdn.com/b/ieinternals/archive/2009/12/30/windows-7-web-browser-control-will-not-browse-file-system.aspx
    }
    Result := E_NOINTERFACE; //Return $E_NOINTERFACE

    OutputDebugString(PChar('TShellBrowser.QueryService: '+IIDToString(rsid)));
    {
        Make a small change to your application to enable the filesystem object to navigate in-place within the WebOC
        when running on Windows 7.
        To do so, your hosting application will implement the IServiceProvider interface,
        and hand back the WebBrowser control’s SID_SShellBrowser when asked for SID_SInPlaceBrowser
    }
    if IsEqualGUID(rsid, SID_SInPlaceBrowser) then
    begin
        sb := Self as IShellBrowser;
        Pointer(Obj) := Pointer(sb);
        sb._AddRef;
        Result := S_OK;
    end;
end;

奖金阅读


另见

2 个答案:

答案 0 :(得分:2)

在Vista或更高版本上,您可以切换到托管ExplorerBrowser,您可以从中使用QI IObjectWithSite并使用SID_SShellBrowser或SID_SInPlaceBrowser等服务传递实现IServiceProvier的对象。

在XP上,您可能需要处理源自您的进程的DDE消息,因为当时默认文件夹关联是DDE。

答案 1 :(得分:1)

我用来重现这个例子的一些代码丢失了。

TForm1 = class(TForm, IShellBrowser)

之后实现IShellBrowser接口并获取FShellBrowser变量。

self.GetInterface(IID_IShellBrowser, FShellBrowser)

在{/ p>中使用FShellBrowser

FShellView.CreateViewWindow(FPreviousView, FFolderSettings, FShellBrowser, FHostRect, FViewHandle)

单击目录只会生成:

OnViewWindowActive

GetControlWindow

此代码的另一个问题是: 如何与资源管理器通信,表单已调整大小,并且您希望调整托管资源管理器的大小。 Explorer浏览器具有SetRect方法。

我同意使用IExplorerBrowser。