如何在c ++ winapi中获取活动文件资源管理器窗口的路径

Posted

技术标签:

【中文标题】如何在c ++ winapi中获取活动文件资源管理器窗口的路径【英文标题】:How to get the path of an active file explorer window in c++ winapi 【发布时间】:2017-05-06 01:59:40 【问题描述】:

我一直在思考如何才能做到这一点。基本上我的应用程序需要使用winapi在c++中找出活动文件资源管理器(即前台的文件资源管理器)的目录路径。

而不是这个:

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

这显然会返回我希望它返回活动目录的窗口标题。

【问题讨论】:

也许这个帖子能给你一个提示:***.com/questions/20960316/… en.wikipedia.org/wiki/Dangling_pointer 停止黑客攻击并使用外壳。此外,如果您要调用 Win32 函数,请养成检查错误的习惯。忽略返回值后果自负。 【参考方案1】:

创建IShellWindows 的实例并使用它来枚举所有当前打开的资源管理器窗口。使用各种相关接口,可以从IShellWindows枚举的每一项中以PIDL的形式获取窗口句柄和当前文件夹。如果窗口句柄等于GetForegroundWindow()的结果,则将PIDL转换为路径。

在下文中,我提供了一些用于获取有关所有资源管理器窗口的信息的代码。它部分基于code of Raymond Chen,但使用智能指针来减少脆弱和清洁的代码。我还通过异常添加了错误处理。

首先需要包含和一些实用程序代码:

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

// 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 >;

现在我们定义一个函数GetCurrentExplorerFolders()来返回所有当前打开的资源管理器窗口的信息,包括当前文件夹的窗口句柄和PIDL

// 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;

示例显示如何调用GetCurrentExplorerFolders(),将PIDL 转换为路径并捕获异常。

int main()

    ::CoInitialize( nullptr );
        
    try
    
        std::wcout << L"Currently open explorer windows:\n";
        for( const auto& info : GetCurrentExplorerFolders() )
        
            // Max. length of path with \\?\ prefix (https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) 
            wchar_t path[ 32767 ];
            if( ::SHGetPathFromIDListEx( info.pidl.get(), path, ARRAYSIZE(path), 0 ) )
                std::wcout << L"hwnd: 0x" << std::hex << info.hwnd << L", path: " << path << L"\n";
        
    
    catch( std::system_error& e )
    
        std::cout << "ERROR: " << e.what() << "\nError code: " << e.code() << "\n";
    

    ::CoUninitialize();

可能的输出:

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

【讨论】:

这超出了我的预期,非常感谢!奇迹般有效!伙计,我真的阅读了更多关于 COM 的内容 如果有办法获取路径长度,而不是像32767 这样猜测缓冲区大小,那就太好了。 @raymai97 不幸的是,SHGetPathFromIDListEx 没有报告所需的缓冲区大小。但32767 不是猜测。它是使用“\\?\”前缀时系统支持的最大路径大小,如MSDN page 中所述。

以上是关于如何在c ++ winapi中获取活动文件资源管理器窗口的路径的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C WinAPI 获取当前麦克风输入电平?

C#、C++、WinAPI - 从另一个进程获取窗口数

WinApi - 如何计算文件内的图标?

C++/WinAPI 中的 .NET WebClient.DownloadData(url) 替代方案?

从剪贴板获取数据并重新设置 - WINAPI,C [重复]

如何在winapi-rs中获取IWICImagingFactory的实例