从资源管理器窗口中获取信息
2008-10-06 15:03
183 查看
有的时候,软件开发是创造新的东西,不过更常见的是把现有的东西组合到一起。今天的难题就属于后一种。
给定一个窗口句柄,你可以判定:(1)是否是一个资源管理器窗口,如果是,那么(2)它正在显示哪个文件夹,而且(3)当前焦点在哪一项上。
这其实不是一件难事。你只需把许多小碎片拼凑起来就可以。
一切从 ShellWindows 对象开始,它代表所有打开的外壳窗口。你可以使用 Item 属性遍历它们。用 C++ 来做这事看起来有点笨拙,因为 ShellWindows 对象就是为像 JScript 或者 Visual Basic 这样的脚本语言的使用所设计的。
对于每一个元素,我们可以询问其窗口句柄以判断是否是我们需要的。
不错,现在我们已经通过各元素的 IWebBrowserApp 接口找到了目标,接下来需要得到顶级外壳浏览器对象(top shell browser)。这可以通过查询 SID_STopLevelBrowser 服务并请求 IShellBrowser 接口来完成。
我们再使用 QueryActiveShellView 方法从 IShellBrowser 接口请求当前的外壳视图(shell view)。
当然,我们真正想要的其实是 IFolderView 接口,它是一个自动化对象,囊括了所有的好东西。
挺好。想从视图里得到些什么呢,正被浏览的 IShellFolder 的位置怎么样?做这件事情,我们需要使用 IPersistFolder2::GetCurFolder。GetFolder 方法可以使我们访问到外壳文件夹(shell folder),我们要从那里请求 IPersistFolder2。(大部分情况下你需要 IShellFolder 接口,因为大多数的酷玩意儿都在那儿。)
我们把 pidl 转换为用于显示的路径。
用我们已经得到的这些可以干什么呢?哦,对了,来看一下当前的焦点在什么上吧。
要把聚焦项的名字显示出来,我们需要该项的 pidl 和 IShellFolder。(看看,我告诉过你好东西就在 IShellFolder 那儿)。项可以通过 Item 方法得到。
(如果想得到选中项的列表,我们可以使用 Items 方法,需要传入 SVGIO_SELECTION。)
得到 pidl 之后,还需要 IShellFolder:
然后我们用这两样东西以及 GetDisplayNameOf 方法的辅助来获取项的显示名字。
我们可以使用辅助函数 StrRetToBuf 把那个乖僻的 STRRET 结构转换为令人生厌的字符串缓冲区。(怪异的 STRRET 结构的历史容以后再说。)
好,下面我们把所有这些都组合起来。代码看起来相当丑陋,因为我把所有东西都放到了一个大函数里而没有把它们分离为子函数。在“现实生活”中,我会把它们分为小的辅助函数,那样更便于管理。
打开 scratch(译者注:原作者的一个测试用程序原型) 程序,添加一下函数:
剩下的就是定期调用此函数并打印结果。
现在已经准备就绪了。运行程序,把它放到一边,然后启动资源管理器并观察程序,会发现它可以跟踪你所在的文件夹和焦点所在的项。
我希望我已经证实了我的观点:通常,你所需的东西的所有片断已经存在了,你只不过是需要指出怎样把它们拼在一块。注意,每一小片都不大。只是你必须能意识到可以用一种有趣的方法把它们放到一起。
练习:修改程序,使它得到文件夹并将其切换到详细信息视图
给定一个窗口句柄,你可以判定:(1)是否是一个资源管理器窗口,如果是,那么(2)它正在显示哪个文件夹,而且(3)当前焦点在哪一项上。
这其实不是一件难事。你只需把许多小碎片拼凑起来就可以。
一切从 ShellWindows 对象开始,它代表所有打开的外壳窗口。你可以使用 Item 属性遍历它们。用 C++ 来做这事看起来有点笨拙,因为 ShellWindows 对象就是为像 JScript 或者 Visual Basic 这样的脚本语言的使用所设计的。
IShellWindows *psw; if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**)&psw))) { VARIANT v; V_VT(&v) = VT_I4; IDispatch *pdisp; BOOL fFound = FALSE; for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK; V_I4(&v)++) { ... pdisp->Release(); } psw->Release(); }
对于每一个元素,我们可以询问其窗口句柄以判断是否是我们需要的。
IWebBrowserApp *pwba; if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) { HWND hwndWBA; if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) && hwndWBA == hwndFind) { fFound = TRUE; ... } pwba->Release(); }
不错,现在我们已经通过各元素的 IWebBrowserApp 接口找到了目标,接下来需要得到顶级外壳浏览器对象(top shell browser)。这可以通过查询 SID_STopLevelBrowser 服务并请求 IShellBrowser 接口来完成。
IServiceProvider *psp; if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) { IShellBrowser *psb; if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb))) { ... psb->Release(); } psp->Release(); }
我们再使用 QueryActiveShellView 方法从 IShellBrowser 接口请求当前的外壳视图(shell view)。
IShellView *psv; if (SUCCEEDED(psb->QueryActiveShellView(&psv))) { ... psv->Release(); }
当然,我们真正想要的其实是 IFolderView 接口,它是一个自动化对象,囊括了所有的好东西。
IFolderView *pfv; if (SUCCEEDED(psv->QueryInterface(IID_IFolderView, (void**)&pfv))) { ... pfv->Release(); }
挺好。想从视图里得到些什么呢,正被浏览的 IShellFolder 的位置怎么样?做这件事情,我们需要使用 IPersistFolder2::GetCurFolder。GetFolder 方法可以使我们访问到外壳文件夹(shell folder),我们要从那里请求 IPersistFolder2。(大部分情况下你需要 IShellFolder 接口,因为大多数的酷玩意儿都在那儿。)
IPersistFolder2 *ppf2; if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2, (void**)&ppf2))) { LPITEMIDLIST pidlFolder; if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) { ... CoTaskMemFree(pidlFolder); } ppf2->Release(); }
我们把 pidl 转换为用于显示的路径。
if (!SHGetPathFromIDList(pidlFolder, g_szPath)) { lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH); } ...
用我们已经得到的这些可以干什么呢?哦,对了,来看一下当前的焦点在什么上吧。
int iFocus; if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) { ... }
要把聚焦项的名字显示出来,我们需要该项的 pidl 和 IShellFolder。(看看,我告诉过你好东西就在 IShellFolder 那儿)。项可以通过 Item 方法得到。
LPITEMIDLIST pidlItem; if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) { ... CoTaskMemFree(pidlItem); }
(如果想得到选中项的列表,我们可以使用 Items 方法,需要传入 SVGIO_SELECTION。)
得到 pidl 之后,还需要 IShellFolder:
IShellFolder *psf; if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder, (void**)&psf))) { ... psf->Release(); }
然后我们用这两样东西以及 GetDisplayNameOf 方法的辅助来获取项的显示名字。
STRRET str; if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem, SHGDN_INFOLDER, &str))) { ... }
我们可以使用辅助函数 StrRetToBuf 把那个乖僻的 STRRET 结构转换为令人生厌的字符串缓冲区。(怪异的 STRRET 结构的历史容以后再说。)
StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);
好,下面我们把所有这些都组合起来。代码看起来相当丑陋,因为我把所有东西都放到了一个大函数里而没有把它们分离为子函数。在“现实生活”中,我会把它们分为小的辅助函数,那样更便于管理。
打开 scratch(译者注:原作者的一个测试用程序原型) 程序,添加一下函数:
#include <shlobj.h> #include <exdisp.h> TCHAR g_szPath[MAX_PATH]; TCHAR g_szItem[MAX_PATH]; void CALLBACK RecalcText(HWND hwnd, UINT, UINT_PTR, DWORD) { HWND hwndFind = GetForegroundWindow(); g_szPath[0] = TEXT('/0'); g_szItem[0] = TEXT('/0'); IShellWindows *psw; if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**)&psw))) { VARIANT v; V_VT(&v) = VT_I4; IDispatch *pdisp; BOOL fFound = FALSE; for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK; V_I4(&v)++) { IWebBrowserApp *pwba; if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) { HWND hwndWBA; if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) && hwndWBA == hwndFind) { fFound = TRUE; IServiceProvider *psp; if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) { IShellBrowser *psb; if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb))) { IShellView *psv; if (SUCCEEDED(psb->QueryActiveShellView(&psv))) { IFolderView *pfv; if (SUCCEEDED(psv->QueryInterface(IID_IFolderView, (void**)&pfv))) { IPersistFolder2 *ppf2; if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2, (void**)&ppf2))) { LPITEMIDLIST pidlFolder; if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) { if (!SHGetPathFromIDList(pidlFolder, g_szPath)) { lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH); } int iFocus; if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) { LPITEMIDLIST pidlItem; if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) { IShellFolder *psf; if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder, (void**)&psf))) { STRRET str; if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem, SHGDN_INFOLDER, &str))) { StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH); } psf->Release(); } CoTaskMemFree(pidlItem); } } CoTaskMemFree(pidlFolder); } ppf2->Release(); } pfv->Release(); } psv->Release(); } psb->Release(); } psp->Release(); } } pwba->Release(); } pdisp->Release(); } psw->Release(); } InvalidateRect(hwnd, NULL, TRUE); }
剩下的就是定期调用此函数并打印结果。
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) { SetTimer(hwnd, 1, 1000, RecalcText); return TRUE; } void PaintContent(HWND hwnd, PAINTSTRUCT *pps) { TextOut(pps->hdc, 0, 0, g_szPath, lstrlen(g_szPath)); TextOut(pps->hdc, 0, 20, g_szItem, lstrlen(g_szItem)); }
现在已经准备就绪了。运行程序,把它放到一边,然后启动资源管理器并观察程序,会发现它可以跟踪你所在的文件夹和焦点所在的项。
我希望我已经证实了我的观点:通常,你所需的东西的所有片断已经存在了,你只不过是需要指出怎样把它们拼在一块。注意,每一小片都不大。只是你必须能意识到可以用一种有趣的方法把它们放到一起。
练习:修改程序,使它得到文件夹并将其切换到详细信息视图
相关文章推荐
- 在资源管理器/我的电脑详细信息视图里按下Ctrl+(小键盘+)
- 用opencms java API获取资源信息
- 缓存,用户信息不能随便修改的,因为这里面的id是唯一的身份标识,获取资源全靠他了,
- 有没有办法获取QQ聊天窗口信息
- JavaScript 获取访问主机资源信息
- Qt获取设置窗口的布局管理器
- javaScript高级教程(六) 获取窗口,屏幕,文档信息
- js获取屏幕和窗口的信息
- 从资源文件中获取版本信息
- 在jQuery中获取浏览器窗口的尺寸信息
- 获取指定窗口信息(坐标,窗口风格)
- 关于WIN32 窗口信息的各种获取
- crm2011处理save事件和获取当前窗口信息
- 【工具】获取窗口与控件信息
- VC获取当前窗口的进程信息
- shell获取远程机器上的系统资源信息
- [Java]在xp系统下java调用wmic命令获取窗口返回信息无反应(阻塞)的解决方案
- ios获取媒体资源以及媒体信息(元数据)---AVAsset
- Java获取Windows CMD窗口执行命令的返回信息【转自国外某网站】