简体   繁体   中英

How to get the path of an active file explorer window in c++ winapi

I've been banging my head on the wall of how I can go about doing this. Basically my application needs to figure out the directory path of the active file explorer (ie the file explorer in the foreground) in windows in c++ using the winapi.

Instead of this:

TCHAR* getWindowDir(){
 TCHAR* windowTitle = new TCHAR[MAX_PATH];
 HWND windowHandle = GetForegroundWindow();
 GetWindowText(windowHandle,windowTitle,MAX_PATH);
 return windowTitle;
}

Which obviously returns the window title I want it to return the active directory.

Create an instance of IShellWindows and use that to enumerate all currently open Explorer windows. Using various related interfaces, you can get the window handle and the current folder in form of a PIDL from each item enumerated by IShellWindows . If the window handle is equal to the result of GetForegroundWindow() , convert the PIDL into a path.

In the following I present some code for getting information about all Explorer windows. It is partially based on code of Raymond Chen but uses smart pointers for less fragile and cleaner code. I've also added error handling via exceptions.

First the required includes and some utility code:

#include <Windows.h>
#include <shlobj.h>
#include <atlcomcli.h>  // for COM smart pointers
#include <atlbase.h>    // for COM smart pointers
#include <vector>
#include <system_error>
#include <memory>
#include <iostream>

// Throw a std::system_error if the HRESULT indicates failure.
template< typename T >
void ThrowIfFailed( HRESULT hr, T&& msg )
{
    if( FAILED( hr ) )
        throw std::system_error{ hr, std::system_category(), std::forward<T>( msg ) };
}

// Deleter for a PIDL allocated by the shell.
struct CoTaskMemDeleter
{
    void operator()( ITEMIDLIST* pidl ) const { ::CoTaskMemFree( pidl ); }
};
// A smart pointer for PIDLs.
using UniquePidlPtr = std::unique_ptr< ITEMIDLIST, CoTaskMemDeleter >;

Now we define a function GetCurrentExplorerFolders() to return information about all currently open Explorer windows, including window handle and PIDL of the current folder.

// Return value of GetCurrentExplorerFolders()
struct ExplorerFolderInfo
{
    HWND hwnd = nullptr;  // window handle of explorer
    UniquePidlPtr pidl;   // PIDL that points to current folder
};

// Get information about all currently open explorer windows.
// Throws std::system_error exception to report errors.
std::vector< ExplorerFolderInfo > GetCurrentExplorerFolders()
{
    CComPtr< IShellWindows > pshWindows;
    ThrowIfFailed(
        pshWindows.CoCreateInstance( CLSID_ShellWindows ),
        "Could not create instance of IShellWindows" );

    long count = 0;
    ThrowIfFailed(
        pshWindows->get_Count( &count ),
        "Could not get number of shell windows" );

    std::vector< ExplorerFolderInfo > result;
    result.reserve( count );

    for( long i = 0; i < count; ++i )
    {
        ExplorerFolderInfo info;

        CComVariant vi{ i };
        CComPtr< IDispatch > pDisp;
        ThrowIfFailed(
            pshWindows->Item( vi, &pDisp ),
            "Could not get item from IShellWindows" );

        if( ! pDisp )
            // Skip - this shell window was registered with a NULL IDispatch
            continue;

        CComQIPtr< IWebBrowserApp > pApp{ pDisp };
        if( ! pApp )
            // This window doesn't implement IWebBrowserApp 
            continue;

        // Get the window handle.
        pApp->get_HWND( reinterpret_cast<SHANDLE_PTR*>( &info.hwnd ) );

        CComQIPtr< IServiceProvider > psp{ pApp };
        if( ! psp )
            // This window doesn't implement IServiceProvider
            continue;

        CComPtr< IShellBrowser > pBrowser;
        if( FAILED( psp->QueryService( SID_STopLevelBrowser, &pBrowser ) ) )
            // This window doesn't provide IShellBrowser
            continue;

        CComPtr< IShellView > pShellView;
        if( FAILED( pBrowser->QueryActiveShellView( &pShellView ) ) )
            // For some reason there is no active shell view
            continue;

        CComQIPtr< IFolderView > pFolderView{ pShellView };
        if( ! pFolderView )
            // The shell view doesn't implement IFolderView
            continue;

        // Get the interface from which we can finally query the PIDL of
        // the current folder.
        CComPtr< IPersistFolder2 > pFolder;
        if( FAILED( pFolderView->GetFolder( IID_IPersistFolder2, (void**) &pFolder ) ) )
            continue;

        LPITEMIDLIST pidl = nullptr;
        if( SUCCEEDED( pFolder->GetCurFolder( &pidl ) ) )
        {
            // Take ownership of the PIDL via std::unique_ptr.
            info.pidl = UniquePidlPtr{ pidl };
            result.push_back( std::move( info ) );
        }
    }

    return result;
}

Example showing how to call GetCurrentExplorerFolders() , convert PIDL into path and catch exceptions.

int main()
{
    ::CoInitialize( nullptr );
        
    try
    {
        std::wcout << L"Currently open explorer windows:\n";
        for( const auto& info : GetCurrentExplorerFolders() )
        {
            CComHeapPtr<wchar_t> pPath;
            if( SUCCEEDED( ::SHGetNameFromIDList( info.pidl.get(), SIGDN_FILESYSPATH, &pPath ) ) )
            {
                std::wcout << L"hwnd: 0x" << std::hex << info.hwnd 
                           << L", path: " << static_cast<LPWSTR>( pPath ) << L"\n";
            }
        }
    }
    catch( std::system_error& e )
    {
        std::cout << "ERROR: " << e.what() << "\nError code: " << e.code() << "\n";
    }

    ::CoUninitialize();
}

Possible output:

Currently open explorer windows:
hwnd: 0x0030058E, path: C:\Windows
hwnd: 0x000C06D4, path: C:\Program Files

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM