Как сделать встроенный Explorer IShellView доступным для просмотра (т.е. вызвать событие BrowseObject)

Я встраиваю проводник Windows в свое приложение Win32. (Технически я размещаю ShellView папки в моем приложении, что и делает проводник Windows).

Проблема в том, что представление никогда не вызывает IShellBrowser.BrowseObject. Вместо того, чтобы просить меня перейти в новое место (через событие BrowseObject), представление оболочки запускает копию проводника Windows для просмотра папки.

Я хочу, чтобы вид оболочки по умолчанию (в просторечии известный как DefView) был доступен для просмотра.


Пример кода учебника

Сначала нам нужно получить IShellFolder для какой-то папки, которую я хочу отобразить. Самая простая папка - это папка Desktop, так как там есть _ 2_ для этого:

folder: IShellFolder;

SHGetDesktopFolder({out} folder);

Затем мы просим папку рабочего стола указать передайте нам его IShellView:

view: IShellView;

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

Теперь, когда у нас есть IShellView папки (в отличие от IContextMenu или IExtractIcon), мы теперь хотим показать представление оболочки, вызвав 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, узнаваемый список оболочки, показывающий мой рабочий стол:

введите описание изображения здесь

в комплекте с обработчиками контекстного меню:

введите описание изображения здесь

За исключением того, что когда я нажимаю Открыть, а не отправляю мне _ 6_ через _ 7_, который я предоставил, он открывает новое окно:

введите описание изображения здесь

То же самое происходит, когда я дважды щелкаю.

Как я могу сделать Microsoft 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;

Бонусное чтение


Смотрите также


person Ian Boyd    schedule 08.10.2011    source источник
comment
В образцах оболочки SDK есть несколько образцов, в которые встроен проводник. Я бы быстро их рассмотрел.   -  person David Heffernan    schedule 08.10.2011
comment
Если вы можете полагаться на Windows Vista, IExplorerBrowser интерфейс сделает это намного проще.   -  person Raymond Chen    schedule 09.10.2011
comment
@Ian Твоя жизнь завершена!   -  person David Heffernan    schedule 09.10.2011
comment
@DavidHeffernan я не могу найти образец, в который встроено IShellView, будь то стандартное представление или расширение пространства имен. Можете ли вы указать на то же имя файла / папки? Важно отметить, что я не создаю расширение пространства имен, а скорее действую как часть проводника Windows или общего диалогового окна файла (то есть реализации IShellBrowser, а не разговора к нему).   -  person Ian Boyd    schedule 09.10.2011
comment
@DavidHeffernan Да, я думал, тебе это понравится. Bonus Chatter: я знаю, что сделал.   -  person Ian Boyd    schedule 09.10.2011


Ответы (2)


В Vista или выше вы можете переключиться на хостинг ExplorerBrowser, с которого вы можете QI IObjectWithSite и передать объект, реализующий IServiceProvier с такими службами, как SID_SShellBrowser или SID_SInPlaceBrowser.

В XP вам, вероятно, потребуется обрабатывать сообщения DDE, исходящие из вашего процесса, поскольку в то время ассоциация папок по умолчанию была DDE.

person Sheng Jiang 蒋晟    schedule 08.10.2011
comment
В конце концов, я отказался от попыток решить проблему. я перешел на использование IExplorerBrowser, как было предложено. я отказался от попыток разместить ShellBrower, так как это было слишком сложно. (Черт, если бы парень из команды снарядов не мог этого понять: какой у меня шанс?) - person Ian Boyd; 27.07.2013

Некоторый недостающий код я использовал для воспроизведения этого примера.

TForm1 = class(TForm, IShellBrowser)

После этого реализуйте интерфейс IShellBrowser и получите переменную FShellBrowser.

self.GetInterface(IID_IShellBrowser, FShellBrowser)

используйте FShellBrowser в:

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

Щелчок по каталогу генерирует только:

OnViewWindowActive

GetControlWindow

Другая проблема с этим кодом: как сообщить Explorer, что размер формы изменен и вы хотите изменить размер размещенного Explorer. В обозревателе Explorer есть метод SetRect.

Я согласен использовать IExplorerBrowser.

person Raf Bakker    schedule 19.11.2013