Results 1 to 25 of 25

Thread: [VB6] Get extended details about Explorer windows by getting their IFolderView

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    [VB6] Get extended details about Explorer windows by getting their IFolderView


    IShellWindows / IFolderView

    There's some basic ways of working with open Explorer windows, but if you really want to do some intensive work with them, the IShellWindows interface lets you get a variety of other interfaces that allow you to get very detailed information about open windows and their items, as well as set view and selection options. You can get a direct line on the IShellBrowser, IShellView, IFolderView, and IShellFolder/IShellItem interfaces for all open folders using this method.
    For the purposes of this demonstration, all we're doing is listing some details, but if you're not familiar with those interfaces already just look through their members in Object Browser to see all the other things you can get and set.

    Requirements
    -Sample project requires Vista+. Some info is retrieved with IFolderView2... but if you just stuck to what is available with IFolderView, you could use this in XP too.
    -oleexp 4.0 or higher - oleexp.tlb must be a reference; IDE only- not needed after compiled.


    Technique
    The IShellWindows interface conveniently has a default implementation coclass (ShellWindows), so setting up the window enumeration is straightforward. The sample shows 2 different ways to loop through the open windows:
    Code:
    Set oSW = New ShellWindows
    If oSW.Count < 1 Then Exit Sub
    ReDim sSelected(oSW.Count - 1)
    'METHOD 1: .Item/.Count
    'For q = 0 To oSW.Count - 1
    '    Set pdp = oSW.Item(CVar(q))
    '    Set punkitem = pdp
    '---------------
    
    'METHOD 2: IEnumVARIANT
    Set spunkenum = oSW.NewEnum
    Set spev = spunkenum
    Do While spev.Next(1&, pVar, pclt) = NOERROR
        Set punkitem = pVar
    '---------------
    Interestingly, the pVar here (declared As Variant) actually can be cast by VB into a variable declared As IUnknown just with Set. I had thought it would have been much harder and involve getting the pointer and setting the object through API.

    Now that we've got a Variant representing a window, first we get the IShellBrowser interface for it. After that we're golden... we can get the IShellView through .QueryActiveShellView, and then IShellView also implements IFolderView/IFolderView2, so all we need is the Set keyword (this internally calls QueryInterface that you see in the C++ examples of this). Then IFolderView has .GetFolder which can get the IShellItem or IShellFolder of the folder.
    (critical error checking and debug logging code omitted, see full sample)
    Code:
                IUnknown_QueryService ObjPtr(punkitem), SID_STopLevelBrowser, IID_IShellBrowser, spsb 'get IShellBrowser
                    spsb.QueryActiveShellView spsv 'get IShellView
                        Set spfv = spsv 'get IFolderView2
                        spfv.GetFolder IID_IShellItem, lsiptr 'this is original olelib's problem having it as a long, which now needs:
                        If lsiptr Then vbaObjSetAddRef spsi, lsiptr 'get IShellItem
    And now we've got everything needed for advanced, detailed interaction with the open Explorer window and its contents.

    ---------------------------------------
    Here's the full EnumWindows routine that fills the ListView in the sample (and the sSelection module-level holder of the contents) and some other supporters. Don't be scared of all the variables!
    Code:
    Private Sub EnumWindows()
    Dim li As ListItem
    Dim i As Long, j As Long
    Dim s1 As String, s2 As String, s3 As String
    Dim lp1 As Long, lp2 As Long, lp3 As Long, lp4 As Long, lp5 As Long
    Dim siaSel As IShellItemArray
    Dim lpText As Long
    Dim sText As String
    Dim sItems() As String
    Dim punkitem As oleexp.IUnknown
    Dim lPtr As Long
    Dim pclt As Long
    Dim spsb As IShellBrowser
    Dim spsv As IShellView
    Dim spfv As IFolderView2
    Dim spsi As IShellItem
    Dim lpPath As Long
    Dim sPath As String
    Dim lsiptr As Long
    Dim ct As Long
    Dim oSW As ShellWindows
    Dim spev As oleexp.IEnumVARIANT
    Dim spunkenum As oleexp.IUnknown
    Dim pVar As Variant
    Dim pdp As oleexp.IDispatch
    Dim q As Long
    Dim tSC(10) As SORTCOLUMN
    lp1 = 0&
    lp2 = 0&
    lp3 = 0&
    lp4 = 0&
    lp5 = 0&
    Set oSW = New ShellWindows
    If oSW.Count < 1 Then Exit Sub
    ReDim sSelected(oSW.Count - 1)
    '                            On Error Resume Next
    'METHOD 1: .Item/.Count
    For q = 0 To oSW.Count - 1
        Set pdp = oSW.Item(CVar(q))
        Set punkitem = pdp
    '---------------
    
    'METHOD 2: IEnumVARIANT
    'Set spunkenum = oSW.NewEnum
    'Set spev = spunkenum
    'Do While spev.Next(1&, pVar, pclt) = NOERROR
    '    Set punkitem = pVar
    '---------------
        ct = ct + 1
        Debug.Print "in loop " & ct
        If True Then
            If (punkitem Is Nothing) = False Then
                Debug.Print "queryservice"
                IUnknown_QueryService ObjPtr(punkitem), SID_STopLevelBrowser, IID_IShellBrowser, spsb
                If (spsb Is Nothing) = False Then
                    Debug.Print "queryview"
                    spsb.QueryActiveShellView spsv
                    If (spsv Is Nothing) = False Then
    '                    Set spfv = spsv
                        Dim pUnk As oleexp.IUnknown
                        Set pUnk = spsv
                        pUnk.QueryInterface IID_IFolderView2, spfv
                        If (spfv Is Nothing) = False Then
                            Debug.Print "getfolder"
                            spfv.GetFolder IID_IShellItem, lsiptr ' spsi
                            If lsiptr Then vbaObjSetAddRef spsi, lsiptr
                            If (spsi Is Nothing) = False Then
                            
                                'we've got all relevant interfaces, start adding data
                                spsi.GetDisplayName SIGDN_NORMALDISPLAY, lpPath
                                sPath = LPWSTRtoStr(lpPath)
                                Debug.Print "Open path " & sPath
                                Set li = ListView1.ListItems.Add(, , sPath)
                                spfv.ItemCount SVGIO_ALLVIEW, lp1
                                spfv.ItemCount SVGIO_SELECTION, lp2
                                spfv.GetSortColumnCount lp3
                                If lp3 > 11 Then lp3 = 11
                                spfv.GetSortColumns tSC(0), lp3
                                For j = 0 To lp3
                                    If PSGetNameFromPropertyKey(tSC(j).PropKey, lpText) = S_OK Then
                                        sText = sText & LPWSTRtoStr(lpText) & "(" & tSC(j).direction & ") "
                                    Else
                                        Debug.Print "sortcol name needs psstringfrompropertykey; not implemented in this sample"
                                    End If
                                Next
                                spfv.GetViewModeAndIconSize lp4, lp5
                                With li
                                    .SubItems(1) = lp1
                                    .SubItems(2) = lp2
                                    .SubItems(3) = sText
                                    .SubItems(4) = ViewModeStr(lp4)
                                    spsi.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
                                    sPath = LPWSTRtoStr(lpPath)
                                    .SubItems(5) = sPath
                                End With
                                If lp2 > 0& Then
                                    spfv.GetSelection 0&, siaSel
                                    If (siaSel Is Nothing) = False Then
                                        sSelected(ct - 1) = GetNamesFromSIA(siaSel)
                                    End If
                                Else
                                    sSelected(ct - 1) = "(none)"
                                End If
                            Else
                                Debug.Print "Failed to get IShellItem"
                            End If
                        Else
                            Debug.Print "Failed to get IFolderView2 for hwnd " & spsv.GetWindow
                        End If
                    Else
                        Debug.Print "Failed to get IShellView"
                    End If
                Else
                    Debug.Print "Failed to get IShellBrowser"
                End If
            Else
                Debug.Print "Failed to cast enum lPtr to pUnk"
            End If
    
        Else
            Debug.Print "in loop but lptr=0"
        End If
    Set spsi = Nothing
    Set spsv = Nothing
    Set spsb = Nothing
    lsiptr = 0
    lp1 = 0
    lp2 = 0
    lp3 = 0
    lp4 = 0
    Erase tSC
    
    'switch to next for method 1
    'Loop
    Next
    
    
    End Sub
    Private Function GetNamesFromSIA(psia As IShellItemArray, Optional nType As SIGDN = SIGDN_NORMALDISPLAY) As String
    Dim pEnum As IEnumShellItems
    Dim psi As IShellItem
    Dim lp As Long
    Dim s1 As String
    Dim sOut As String
    Dim pcl As Long
    
    psia.EnumItems pEnum
    If (pEnum Is Nothing) Then Exit Function
    
    Do While (pEnum.Next(1&, psi, pcl) = NOERROR)
        psi.GetDisplayName nType, lp
        sOut = sOut & LPWSTRtoStr(lp) & ", "
    Loop
    If Len(sOut) > 2 Then
        sOut = Left$(sOut, Len(sOut) - 2) 'remove trailing comma
    End If
    GetNamesFromSIA = sOut
    
    End Function
    BUGFIX: The original code fails if Internet Explorer is open, because it comes up in the enumeration but does not support the IFolderView interfaces. I've corrected the code above, and updated the attachment. Note that the fix requires IID_IFolderView2, which isn't in the original sample project module (but is now). So if you're using the sample project or this code in a project without the full mIID.bas addon be sure to add that too. In the long run, you should always use mIID with oleexp.


    UPDATE - Jan 19 2022
    I wrote a demo about registering a window in your app as a Shell Window that will be included in this enumeration (as well as work with SHOpenFolderAndSelectItems). The IFolderView2 interface needed to be slightly modified to work in both contexts.

    This new version requires oleexp.tlb 4.81+ (released 19 Jan 2022).
    Attached Files Attached Files
    Last edited by fafalone; Nov 4th, 2022 at 10:01 PM. Reason: Fixed image

  2. #2
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Hi!

    Can this also be used to do a IShellWindows::RegisterPending to my app? See https://docs.microsoft.com/de-de/win...egisterpending

    thanks,
    voxy

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I'm not familiar with that functionality at all. But the methods are there and appear to be declared correct... give it a try?

  4. #4
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I'll try it and let you know. But more research is needed. Right now I'm not even sure what a "pending window" is supposed to be. Nor whether *pvarloc has to be updated whenever my window changes the location. The whole thing is mysterious. Why do I need it? I've heard that this is the only way that "Show in Folder" (as found in web browsers for the downloaded files) will pass the full path / file to a file manager, rather than just the path part.

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Show in folder like you want to open a folder and select an item?

    Not sure what context you're doing that in, but in case you don't know, there's the SHOpenFolderAndSelectItems API that does exactly that. It's confusing if you've never worked with pidls before, but here's a demo I made a while back.
    Last edited by fafalone; Jan 8th, 2022 at 05:20 PM.

  6. #6
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I'm on the other side of the story. My app is registered as the default file manager, but with "Show in Folder" it is only called with "[path]" in the command line, not with "[path\file]". My (low) hope is that RegisterPending could change that.
    On the other hand, I don't really understand how "Show in Folder" works because when Explorer is the default manager there is no command line at all when opening explorer.exe with "Show in Folder".

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    The SHOpenFileAndSelectItems selects by calling the IShellFolderViewDual interface's SelectItem method; have you implemented that interface?

    Code:
    HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
    {
        VARIANT var;
        HRESULT hr = InitVariantFromIDList(&var, pidl);
        if (SUCCEEDED(hr))
        {
            hr = psfv->SelectItem(&var, dwOpts);
            VariantClear(&var);
        }
        return hr;
    }
    
    HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
    {
        *ppsfv = NULL;
    
        IWebBrowserApp *pauto;
        HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
        if (SUCCEEDED(hr))
        {
            HWND hwnd;
            if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
            {
                // Make sure we make this the active window
                SetForegroundWindow(hwnd);
                ShowWindow(hwnd, SW_SHOWNORMAL);
            }
    
            IDispatch *pdoc;
            hr = pauto->get_Document(&pdoc);
            if (S_OK == hr) // careful, automation returns S_FALSE
            {
                hr = pdoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv));
                pdoc->Release();
            }
            else
                hr = E_FAIL;
            pauto->Release();
        }
        return hr;
    }
    
    // pidlFolder   - fully qualified pidl to the folder to open
    // cidl/apidl   - array of items in that folder to select
    //
    // if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
    // folder is opened and it is selected.
    //
    // dwFlags - optional flags, pass 0 for now
    
    SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
    {
        HRESULT hr;
        if (0 == cidl)
        {
            // overload the 0 item case to mean pidlFolder is the full pidl to the item
            LPITEMIDLIST pidlTemp;
            hr = SHILClone(pidlFolder, &pidlTemp);
            if (SUCCEEDED(hr))
            {
                ILRemoveLastID(pidlTemp); // strip to the folder
                LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
    
                hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
    
                ILFree(pidlTemp);
            }
        }
        else
        {
            IShellFolderViewDual *psfv;
            hr = OpenFolderAndGetView(pidlFolder, &psfv);
            if (SUCCEEDED(hr))
            {
                DWORD dwSelFlags = SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE;
                for (UINT i = 0; i < cidl; i++)
                {
                    hr = SelectPidlInSFV(psfv, apidl[i], dwSelFlags);
                    dwSelFlags = SVSI_SELECT;   // second items append to sel
                }
               psfv->Release();
            }
        }
        return hr;
    }
    Last edited by fafalone; Jan 9th, 2022 at 09:06 PM.

  8. #8
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Thanks, didn't know that. But I cannot find any information about how to implement this in VB6. I conclude from your question that it is possible though.

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    When you say your app is the registered file handler, I was assuming it was working similar to a shell extension, which has to implement all the standard folder interfaces to work in Explorer. Alternatives to Explorer like Explorer++ seem to implement IWebBrowser and IWebBrowserApp for having it's ListView recognized as a Shell Window; which you also need for the API to open the window in the first place; then the same QueryInterface responder that passes IWebBrowser[App] should also have IShellFolderViewDual available.

    You're probably not going to get much help in VB6 beyond shell extensions that will show implementing some other component interfaces like IShellFolder.

  10. #10
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I'm not even using a common control ListView, so I guess all this interface approach is going nowhere in my case. All I can use is a simple command line, and I get one (so the system treats me differently here from File Explorer), but unfortunately that command line only gives me the path, not the file. Oh well. Thanks anyway!

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    It doesn't matter if you use a ListView or not; it's up to your application how to implement calls like selecting an item.

  12. #12
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I see, thanks! (Currently this is beyond my skills, but I'm learning...)

  13. #13

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    A bit beyond mine too, at least without months to figure out. Given all the quirks with implementing interfaces in VB6, who knows if it's even possible. The best guide would probably be looking at the source of Explorer++, which is in C++ but as mentioned does go in to the right interfaces.

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Actually looking at it closer... Now thanks to that source code leak telling us what interfaces to implement... I think it might be as simple as using IShellWindows.Register and RegisterPending, pointing the IDispatch param at a class that implements that, and also implementations of IWebBrowserApp and IShelllFolderViewDual.

    As long as when there's no explorer open, your app is what gets opened by e.g. Run and a path... If it's recognized as the default at that level it should work. I'm going to play around with this later.

    Edit: I think the most important question right now... if you call ShellExecuteEx on a path, is it opened in your program? Or Explorer? Because if it's not opened in your program, then this will only ever work if your program is already open.
    Last edited by fafalone; Jan 15th, 2022 at 05:03 AM.

  15. #15
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    My program is called with that path as the (only) command line parameter, regardless whether it is already opened or not.

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Quote Originally Posted by voxy View Post
    My program is called with that path as the (only) command line parameter, regardless whether it is already opened or not.
    As long as your program is what opens with ShellExecute, I think this can be done. I'm on it.

    Incidentally; how does that work? How do you replace Explorer with your app for what Windows opens as the file browser in system calls? I'm not familiar with that part.

  17. #17
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Via the registry. These are the two crucial keys, exported as REG file:

    Code:
    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\Directory\Shell]
    @="MyFileManager"
    
    [HKEY_CLASSES_ROOT\Directory\Shell\MyFileManager\Command]
    @="\"C:\\Program Files (x86)\\MyFileManager\\MyFileManager.exe\" \"%1\""
    Directories dbl-clicked e.g. on Desktop will now open in MyFileManager.exe (well, passed as command line to it). Also the shell context menu for directories will now show "MyFileManager" in the place of "Open".

  18. #18

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Thanks to some key help from The_trick, I've gotten it working.

    I'm going to post a demo of the technique in a bit, but here's an overview of the steps:

    -When your app opens a folder, it needs to use both IShellWindows.RegisterPending and IShellWindows.Register to register the path you've opened, and your object implementing all the critical interfaces (a Form or a UserControl, because they have a default IDispatch we can inherit from).

    -Your object needs to implement IServiceProvider, IWebBrowserApp, and, depending on the OS, either IShellView or IShellFolderViewDual-- I don't know which yet, 10 uses IShellView, XP uses IShellFolderViewDual, it changed some time in between.

    -You'll probably want to implement IFolderView as well, for the purposes of other apps that get a list of windows.

    Code:
    Private Sub IServiceProvider_QueryService(guidService As UUID, riid As UUID, ppvObject As Long)
    Dim pUnk As oleexp.IUnknown
    List1.AddItem "IServiceProvider_QueryService Entry"
    If IsEqualGUID(riid, IID_IShellBrowser) Then
        Set pUnk = Me
        pUnk.QueryInterface IID_IShellBrowser, ppvObject
    ElseIf IsEqualGUID(riid, IID_IShellView) Then
        Set pUnk = Me
        pUnk.QueryInterface IID_IShellView, ppvObject
    ElseIf IsEqualGUID(riid, IID_IOleWindow) Then
        List1.AddItem "ISP QueryService IOleWindow"
    ElseIf IsEqualGUID(riid, IID_IShellFolderViewDual) Then
        List1.AddItem "ISP QueryService IShellFolderViewDual"
    ElseIf IsEqualGUID(riid, IID_IWebBrowser) Then
        List1.AddItem "ISP QueryService IWebBrowser"
    ElseIf IsEqualGUID(riid, IID_IWebBrowserApp) Then
        List1.AddItem "ISP QueryService IWebBrowserApp"
    Else
        List1.AddItem "ISP QS Serv=" & dbg_GUIDToString(guidService)
        List1.AddItem "ISP QS riid=" & dbg_GUIDToString(riid)
    End If
    Set pUnk = Nothing
    End Sub
    
    Private Property Get IWebBrowserApp_Document() As Object
    List1.AddItem "Entry: IWebBrowserApp_Document"
    Set IWebBrowserApp_Document = Me
    End Property
    
    
    Private Sub IShellView_SelectItem(ByVal pidlItem As Long, ByVal uFlags As SVSI_Flags)
    List1.AddItem "Entry: IShellView_SelectItem"
    If pidlItem Then
        Dim lp As Long
        Dim pItem As oleexp.IShellItem
        oleexp.SHCreateItemFromIDList pidlItem, IID_IShellItem, pItem
        If (pItem Is Nothing) = False Then
            pItem.GetDisplayName SIGDN_FILESYSPATH, lp
            List1.AddItem "ISV_SelectItem(0x" & Hex$(uFlags) & ") " & LPWSTRtoStr(lp)
        Else
            List1.AddItem "ISV_SelectItem Got pidl, couldn't create IShellItem"
        End If
    Else
        List1.AddItem "ISV_SelectItem entered by no pidl"
    End If
    End Sub
    Edit: Demo posted! Note that you'll need the latest version of my typelibs oleexp.tlb, oleexpimp.tlb, and the mIID.bas that comes with the new version, as I had to add interfaces for this.

    [VB6] Using IShellWindows to register for SHOpenFolderAndSelectItems
    Last edited by fafalone; Jan 19th, 2022 at 03:13 AM.

  19. #19
    Member
    Join Date
    Jan 2015
    Posts
    50

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Wow, impressive! Your work, as well as the complexity of what is required. (Hey Microsoft, what's wrong with sending a simple command line?)

    Thank you, man! I'll let you know when I got it running.

  20. #20

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    It was definitely great to come back to a fun challenge like this

    Tomorrow I'm going to work on implementing IFolderView so the new demo for this API also works with the enumerator in this thread.

    Some notes on registering windows:

    -In the code above and the demo, the SelectItem handler returns just the filename, but the SIGDN_ flag can be changed to SIGDN_DESKTOPABSOLUTEPARSING or SIGDN_FILESYSPATH to get the complete path, if that makes it easier (though you should obviously know the folder; the message only comes in if you've registered for the folder the file is in).

    -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.


    Ps- if you're going to implement IFolderView and IFolderView2 for compatibility with the enumerator in this thread, the versions in oleexp can't be used. If you downloaded the oleexp 4.8 zip in the first hour or so after I posted it, it might have been before my stealth update to oleexpimp.tlb v2.1, so check the version on that. Or just re-dl now.
    Last edited by fafalone; Jan 19th, 2022 at 12:43 PM.

  21. #21

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Here's a test version of that demo that's compatible with the enumator in this thread. It's just passing nonsense hardcoded values for the most part. Make sure mFile is set if you change the selected item count above 1.

    The only thing not working is GetSortColumns. I can't figure out how to pass an array of a user defined type in a way compatible with the IFolderView2 in oleexp. I might have to edit that and this project. But in the mean time, everything else works.

    This adds mPKEY.bas from the oleexp download too. And as mentioned, needs oleexpimp.tlb v2.1, which was stealth uploaded a few hours after the original 4.8 release.

    Still working on my full blown Explorer window with ucShellBrowse, that will take a while.

    Edit: Official demos updated.
    Last edited by fafalone; Jan 19th, 2022 at 11:08 PM.

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

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Thanks a much for delicious examples!

    Just a little remark, please, do sText = "" somewhere to prevent recurse fill column #4:
    Attached Images Attached Images  
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  23. #23
    PowerPoster yereverluvinuncleber's Avatar
    Join Date
    Feb 2014
    Location
    Norfolk UK (inbred)
    Posts
    2,235

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    Faf, this is very good. Very impressed. Forgive me drawing attention to this new thread but the question might be one that you have come across and may have an answer. I'm looking to obtain similar information as above but from the MMC console.

    https://www.vbforums.com/showthread....56#post5601556
    https://github.com/yereverluvinunclebert

    Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.

    By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.

  24. #24
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    785

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    This sample Fafalone posted seems abit "overkill" and to advanced as it doesn't have to be.
    And for this sample to work at least ONE instance of Explorer.exe need to be active!!
    This did Fafalone left out!!
    Or else it just return with no info!

    Code:
    Public Function ShGetIShellWindows(pISW As IShellWindows) As Long
      Static CLSID_ShellWindows As GUID
      
      Dim hr As Long
      
      hr = GuidFromStringW(StrPtr(sCLSID_ShellWindows), CLSID_ShellWindows)
      hr = CoCreateInstance(CLSID_ShellWindows, 0, CLSCTX_ALL, IID_IShellWindows, pISW)
      
      If hr = S_OK Then
        ShGetIShellWindows = S_OK
      Else
        ShGetIShellWindows = hr
      End If
    End Function
    
    Public Function ShGetIShellBrowser(ByVal bUseItem As Boolean, pISB_Out As IShellBrowser) As Long
      Dim pUnk As oleexp.IUnknown
      Dim pUnkEnum As oleexp.IUnknown
      Dim hr As Long
      Dim pISW As IShellWindows
      Dim pIEV As oleexp.IEnumVARIANT
      
      hr = ShGetIShellWindows(pISW)
      
      If bUseItem = True Then
        Set pUnk = pISW.item(pISW.Count - 1)
      Else
       'This is inteneded for the Count Method but still not inplemented
      End If
      
      If hr = S_OK Then
        If Not pUnk Is Nothing Then
          hr = IUnknown_QueryService(ObjPtr(pUnk), SID_STopLevelBrowser, IID_IShellBrowser, pISB_Out)
        End If
        ShGetIShellBrowser = hr
      End If
      ShGetIShellBrowser = hr
    End Function
    
    Private Sub Command6_Click()
      Dim pISB As IShellBrowser
      Dim pIFV As IFolderView2
      Dim pISI As IShellItem
      Dim pUnk As oleexp.IUnknown
      Dim lpName As Long
    
      ShGetIShellBrowser True, pISB
      
      If Not pISB Is Nothing Then
        pISB.QueryActiveShellView pISV
        Set pUnk = pISV
        pUnk.QueryInterface IID_IFolderView2, pIFV
        If Not pIFV Is Nothing Then
          pIFV.GetFolder IID__IShellItem, pISI
          pISI.GetDisplayName SIGDN_NORMALDISPLAY, lpName
          MsgBox "Name Found: " & GetStrAFromStrPtrW(lpName), vbInformation
        End If
      Else
        MsgBox "IShellBrowser Interface was never set! Or an instance of Explorer.exe was never active!", vbExclamation
      End If
    
    End Sub
    Count seems ALWAYS be 1!!
    Last edited by nebeln; Jan 22nd, 2024 at 06:00 PM.

  25. #25

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: [VB6] Get extended details about Explorer windows by getting their IFolderView

    I really needed to spell out that in order to get information about windows, at least one needs to be open??

    Also it doesn't have to be Explorer.exe... it can be anything that registers with IShellWindows. I provided a demo of how to do that. The newer twinBASIC versions of ucShellBrowse have an option to do that, so they'll show up in this list, and respond to your web browser or other program using SHOpenFolderAndSelectItems.

    Whether it's overkill or not; it depends on what information you want. Sure, you can write simpler things to get less info. I wrote something similar to this to just close a window by path:

    Code:
    Private Sub CloseExplorerWindowByPath(sPath As String)
        On Error GoTo e0
        Dim pWindows As ShellWindows
        Set pWindows = New ShellWindows
        Dim pWB2 As IWebBrowser2
        #If TWINBASIC Then
        Dim pDisp As IDispatch
        #Else
        Dim pDisp As oleexp.IDispatch
        #End If
        Dim pSP As IServiceProvider
        Dim pSB As IShellBrowser
        Dim pSView As IShellView
        Dim pFView As IFolderView2
        Dim pFolder As IShellItem
        Dim lpPath As LongPtr, sCurPath As String
        Dim nCount As Long
        Dim i As Long
        Dim hr As Long
        nCount = pWindows.Count
        If nCount < 1 Then
        	Debug.Print "No open Explorer windows found."
            Exit Sub
        End If
        For i = 0 To nCount - 1
            Set pDisp = pWindows.Item(i)
            If (pDisp Is Nothing) = False Then
                Set pSP = pDisp
                If (pSP Is Nothing) = False Then
                    pSP.QueryService SID_STopLevelBrowser, IID_IShellBrowser, pSB
                    If (pSB Is Nothing) = False Then
                        pSB.QueryActiveShellView pSView
                        If (pSView Is Nothing) = False Then
                            Set pFView = pSView
                            If (pFView Is Nothing) = False Then
                                pFView.GetFolder IID_IShellItem, pFolder
                                pFolder.GetDisplayName SIGDN_FILESYSPATH, lpPath
                                sCurPath = LPWSTRtoStr(lpPath)
                                Debug.Print "CompPath " & sCurPath & "||" & sPath
                                If LCase$(sCurPath) = LCase$(sPath) Then
                                    Set pWB2 = pDisp
                                    If (pWB2 Is Nothing) = False Then
                                        pWB2.Quit
                                        Exit Sub
                                    Else
                                        Debug.Print "Couldn't get IWebWebrowser2"
                                    End If
                                End If
                            Else
                                Debug.Print "Couldn't get IFolderView"
                            End If
                        Else
                        	Debug.Print "Couldn't get IShellView"
                        End If
                    Else
                        Debug.Print "Couldn't get IShellBrowser"
                    End If
                Else
                	Debug.Print "Couldn't get IServiceProvider"
                End If
            Else
                Debug.Print "Couldn't get IDispatch"
            End If
        Next
        Debug.Print "Couldn't find path."
    Exit Sub
    e0:
        Debug.Print "CloseExplorerPathByWindow.Error->0x" & Hex$(Err.Number) & ", " & Err.Description
    End Sub

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