Results 1 to 10 of 10

Thread: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    Name:  shellwin2.jpg
Views: 1414
Size:  25.2 KB
    Shell Window Registration V2

    People who've made Explorer replacements or other apps that display a window have found that they can, at best, get the folder passed to them when someone uses 'Show in folder'/'Show in Explorer' in apps like Firefox, Chrome, and torrent clients. However, if you register everything properly, any app calling SHOpenFolderAndSelectItems will be able to pass the names of selected files to a folder you've registered as displaying.

    Note that for it to start your app if it's not running with the path in question registered, it will have to be the default that opens when you pass a folder to ShellExecuteEx (i.e. registered as an Explorer replacement on the system).

    After Windows XP the API changed. I've completed this demo for Windows 7-10, which uses the SelectItem in IShellView. Windows XP uses IShellFolderViewDual instead.

    Requirements
    oleexp.tlb v4.81 (Released with this demo on 2022 Jan 19)
    oleexpimp.tlb v2.11 (Updated with the update to this demo)
    oleexp Add-ons mIID.bas and mPKEY.bas (Included in the oleexp download, be sure to use the one from 4.8)



    The question of how to do this has been asked many times in various forums for various languages, but apart from alluding to the need to use IShellWindows.RegisterPending, nobody definitely answered.

    Special thanks to The_trick for identifying the problem with MSDN's IShellWindows documentation... RegisterPending absolutely does not accept a VT_VARIANT|VT_BYREF variant, but will accept any number of types of variants that can be converted to a pidl. This demo uses a simple BSTR (VB's String type), and The_trick notes it also accepts a CSIDLs, IPersistIDList, IPersistFolder2, IStream, and Byte Arrays (perhaps containing a full ITEMIDLIST structure rather than a pointer to one?). More work is needed to identify the best way to work with this.

    The key is using both RegisterPending and Register, since RegisterPending accepts a path but not an object implementing all the required interfaces, and Register takes the object but not a path (and it never tries looking anything up). It uses APIs to convert the ThreadID to an hWnd to associate them-- you'll find that the cookie RegisterPending returns will be the same as the one RegisterReturns, even if you don't use the same variable like my code; this shows that, unlike MSDN indicates, this calls are two parts of the same whole.

    In a full app, you'd call RegisterPending, create the file display, then finalize with Register.

    Code:
        Set pSW = New ShellWindows
        Set pDisp = ucSW1.object
        pSW.RegisterPending App.ThreadID, Text1.Text, VarPtr(vre), SWC_BROWSER, lCookie
        pSW.Register pDisp, Form1.hWnd, SWC_BROWSER, lCookie

    In the future I plan on releasing a more complete implementation of these various folder interfaces that work with my Shell Browser control, to create a full replacement for Explorer.

    As a bonus, the UserControl has full prototypes for several additional interfaces that weren't used.

    Notes

    -RegisterPending and Register are linked... two parts of the same whole both needed for a shell window. The way it wprks, your app can register multiple locations with RegisterPending, but each one must have a unique hWnd associated with it... this is because when Register is called to finalize it, it looks up the thread with GetWindowThreadProcessId to complete the information.

    -If you do register multiple windows, you must complete them one at a time (RegisterPending, load, Register)... because each Register call will take the most recent matching thread ID because it steps backwards through the structures that have been added.

    -Whenever you're closing out a folder, call IShellWindows.Revoke with the cookie (keep track of them per-window). Register will return the same cookie RegisterPending did; if these get mismatched, it would be because you called RegisterPending multiple times before Register, and represent a problem.

    UPDATE: Version 2
    -Project has now been updated to provide all the details requested by the IShellWindows enumeration demo:
    [VB6] Get extended details about Explorer windows by getting their IFolderView

    -It's been established that only Windows XP uses IShellFolderViewDual.SelectItem... Windows 7-10 use IShellView. I wanted to solve it for XP purely for the challenge though. Getting the pidl was very, very complicated, as it was stored inside a SAFEARRAY inside a VT_ARRAY|VT_UI4 variant. It took some doing but I worked it out:

    Code:
    Dim pidlItem As Long
    Dim sa1 As SAFEARRAY1D
    Dim pSA As Long
    
    CopyMemory ByVal VarPtr(pSA), ByVal (VarPtr(pvfi) + 8&), 4& 'Offset 8 is where the data begins. Here, a pointer to the SAFEARRAY
    CopyMemory ByVal VarPtr(sa1), ByVal pSA, LenB(sa1) 'We now have the full SAFEARRAY struct...
    pidlItem = sa1.pvData '...and pvData is our pidl.
    With that completed that functionality is now available on Windows XP, however the parts of the demo responding to the IShellWindows enumeration demo use IShellItem APIs not present on XP that you'll need to replace.

    Requirements Update:
    -This demo now requires oleexp.tlb 4.81 and oleexpimp.tlb v2.11, released at the same time as this demo.

    -oleexp add-on mPKEY.bas now also used.
    Attached Files Attached Files
    Last edited by fafalone; Jan 19th, 2022 at 11:13 PM.

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    Registering a Shell Item, or any source of IPersistIDList

    You can add these module-level variables:
    Code:
    Private pItem As oleexp.IShellItem
    Private pPID As oleexp.IPersistIDList
    and then instead of registering a string, which is of course subject to many limitations, you can register by Shell Item and pidl via IPersistIDList:

    Code:
        oleexp.SHCreateItemFromParsingName StrPtr(Text1.Text), Nothing, IID_IShellItem, pItem
        Set pPID = pItem
        Dim pVar As Variant
        Set pVar = pPID
        pSW.RegisterPending App.ThreadID, pVar, vre, SWC_BROWSER, lCookiePending
    Last edited by fafalone; Jan 25th, 2022 at 08:29 AM.

  3. #3

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    UPDATE: Version 2
    -Project has now been updated to provide all the details requested by the IShellWindows enumeration demo:
    [VB6] Get extended details about Explorer windows by getting their IFolderView

    -It's been established that only Windows XP uses IShellFolderViewDual.SelectItem... Windows 7-10 use IShellView. I wanted to solve it for XP purely for the challenge though. Getting the pidl was very, very complicated, as it was stored inside a SAFEARRAY inside a VT_ARRAY|VT_UI4 variant. It took some doing but I worked it out:

    Code:
    Dim pidlItem As Long
    Dim sa1 As SAFEARRAY1D
    Dim pSA As Long
    
    CopyMemory ByVal VarPtr(pSA), ByVal (VarPtr(pvfi) + 8&), 4& 'Offset 8 is where the data begins. Here, a pointer to the SAFEARRAY
    CopyMemory ByVal VarPtr(sa1), ByVal pSA, LenB(sa1) 'We now have the full SAFEARRAY struct...
    pidlItem = sa1.pvData '...and pvData is our pidl.
    With that completed that functionality is now available on Windows XP, however the parts of the demo responding to the IShellWindows enumeration demo use IShellItem APIs not present on XP that you'll need to replace.

    Requirements Update:
    -This demo now requires oleexp.tlb 4.81 and oleexpimp.tlb v2.11, released at the same time as this demo.

    -oleexp add-on mPKEY.bas now also used.

  5. #5
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    740

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    Example: "How to select item in already opened folder (and deselect others)"

    Code:
    
    Dim k As Long
    Dim nCount As Long
    Dim shItem As IShellItem2
    
    'get number of elements 
    Call spfv.ItemCount(SVGIO_ALLVIEW, nCount)
    
    For k = 0 To nCount - 1
    
        spfv.GetItem k, IID_IShellItem, shItem
    
        'retrieve the name of item 
        shItem.GetDisplayName SIGDN_PARENTRELATIVEPARSING, lpPath 'or SIGDN_DESKTOPABSOLUTEPARSING (for full path) 
        sPath = LPWSTRtoStr(lpPath)
    
        Debug.Print sPath
    
        If sPath = "xxxxx" Then ' checking that we obtained the desired path 
    
            'select it, and remove both "selection" and "focus rectangle" from other items 
            Call spfv.SelectItem(k, SVSI_DESELECTOTHERS)
            Call spfv.SelectItem(k, SVSI_FOCUSED)
            Call spfv.SelectItem(k, SVSI_SELECT)
    
            Exit For
        End If
    Next
    
    (insert code in this project)

    PS. Sorry, if hijack your topic. I think, it's the most suitable place to quickly find.
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    You should be able to combine all those flags in a single call.

  7. #7
    New Member
    Join Date
    Oct 2022
    Posts
    2

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    Hi,
    I am trying to use this in C# project but it is so different from VB6 and I am trying to understand what is supposed to happen when user clicks "Show in folder" in Chrome. It looks like I've registered it correctly because I get cookie and Explorer doesn't open for that folder (Chrome file download there), but nothing happens. It looks like somehow "IServiceProvider_QueryService" from ucSWctl is called but I can't connect the dots how and why this is called. Your pDisp is I believe an IDispatch class and I expected Invoke would be called once user clicks in Chrome but it is not

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    IServiceProvider_QueryService basically just points back to the class that implements all of those interfaces, to tell Windows 'yes I implement that'.

    You'll need a class that implements everything; I assume you've defined the interfaces, so to implement them you'd need

    class ucSW : IServiceProvider, IWebBrowserApp, etc
    {
    void IServiceProvider.QueryService


    and so on. Though I honestly don't know the details of how interfaces work in C#, whether you'd have to implement IUnknown and IDispatch manually-- in VB6, they're automatically implemented behind the scenes.

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    Note: There was an issue with oleexp.tlb 5.01/oleexpimp.tlb 2.11 with this project (and likely others), if you're getting Uexpected error (32810), upgrade to the latest version, at least 5.03/2.12.

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,269

    Re: [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems

    There's now a 64bit compatible twinBASIC version of this project on GitHub.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width