如何获取桌面的窗口句柄?

16
Windows API提供了一个API GetDesktopWindow( ),用于返回窗口句柄。
但我使用Spy++测试后发现,桌面的窗口句柄和“Windows桌面”的窗口句柄并不相同。
由于“Windows桌面”是一个列表视图,那我需要做什么呢?
1) HANDLE hWnd = GetDesktopWindow() ;
2) FindWindow(hWnd, ..... ) with the SyslistView32 as the Window class.

一旦我获取了窗口句柄,我想使用SendMessage()执行操作,例如获取所选文件的名称、所选文件数量等。

请给出您的意见。我正在使用Windows SDK进行此操作。


也许您可以编辑问题,提供更多关于您获取句柄后打算如何使用它的信息? - Mark Ransom
请注意,XP/Vista/7桌面上的对象并不是文件。例如,在那里发现的常见对象是“My Computer”和“Recycle Bin”。它们以其PIDLs为已知。 PIDL是文件名的概括。因此,您可能对所选PIDL感兴趣。 - MSalters
那么,如果我复制一个文档,比如“Questions.doc”(一个Word文档),我需要获取它的PIDL吗?还是直接获取文件即可。 - Sujay Ghosh
这篇微软博客文章恰好回答了这个问题。 - GetFree
2个回答

23

考虑到最近在Meta上的一次讨论抱怨像这样的问题“没有得到适当的回答”,我将尝试回答这个问题。并不是要暗示meklarian的回答不好——事实上,恰恰相反。但显然已经被认为不令人满意,因此也许我可以填补一些额外的细节。

你的问题源于对桌面窗口实际上是什么的普遍混淆。GetDesktopWindow函数确切地执行了它所记录的操作:返回指向桌面窗口的句柄。然而,这不是包含桌面图标的同一个窗口。那是一个完全不同的窗口,它首次出现在Windows 95中。实际上,它是一个设置为“大图标”视图的ListView控件,其父窗口是实际的桌面窗口。

Windows Shell团队的开发人员Raymond Chen在以下Windows Confidential文章中提供了一些额外的细节:来自Windows 3.0的剩菜

在Windows 3.0中,桌面上的图标代表最小化的窗口,但在Windows 95中,桌面充当了一个图标容器。实际上,Windows 95桌面是由资源管理器创建的一个覆盖整个屏幕的窗口(但位于桌面上的所有其他窗口下方)。这个窗口显示了你的图标。虽然还有一个窗口管理器桌面窗口(如果调用Get­Desktop­Window函数会返回该窗口),但你从未看到它,因为它被Windows 95桌面覆盖了——就像我同事家地下室的木板覆盖了原始墙壁和墙后的时间胶囊一样。自Windows 95推出以来,这种桌面设计基本没有改变。在典型的计算机上,原始桌面仍然存在,但已完全被资源管理器桌面覆盖。因此,GetDesktopWindow函数返回的窗口是实际的桌面窗口,这是我们在Windows 3.0时期拥有的唯一窗口。而包含所有图标的资源管理器桌面只是另一个窗口,位于桌面窗口之上(尽管完全覆盖了原始窗口),直到Windows 95才加入。
如果您想获取资源管理器桌面窗口的句柄,需要除了简单调用GetDesktopWindow函数之外进行一些额外的工作。特别地,您需要遍历实际桌面窗口的子窗口以找到资源管理器用于显示图标的窗口。通过调用FindWindowEx函数来获取层次结构中的每个窗口,直到找到所需窗口。该窗口的类名为SysListView32。您还可能希望使用GetShellWindow函数,它返回一个句柄,指向 Shell 的桌面窗口,以帮助您开始操作。
代码可能如下所示(警告:此代码未经测试,我也不建议使用它!):
HWND hShellWnd = GetShellWindow();
HWND hDefView = FindWindowEx(hShellWnd, NULL, _T("SHELLDLL_DefView"), NULL);
HWND folderView = FindWindowEx(hDefView, NULL, _T("SysListView32"), NULL);
return folderView;

我在那里指出,我实际上不建议使用那段代码。为什么呢?因为几乎在每一种你想要获取桌面窗口的情况下(无论是实际的桌面窗口还是资源管理器桌面),你都在做错事。 这不是与桌面窗口交互的正确方式。事实上,你根本不应该与它交互!还记得小时候学习的规矩吗?未经允许不要玩别人的东西。桌面属于Windows(更具体地说,是Shell),它没有给你玩它的玩具的许可!就像好孩子一样,当你试图未经允许玩它的玩具时,Shell会发脾气。
同样的雷蒙德·陈在他的博客上发布了另一篇文章,详细介绍了一个非常特殊的案例,题为桌面窗口有什么特别之处? 除了他提供的例子,这根本不是UI自动化的方法。它太脆弱、太有问题,而且很容易在将来的Windows版本中出现问题。相反,定义你真正想要完成的任务,然后搜索可以实现该任务的函数。
如果这样的函数不存在,那么要学习的教训不是Microsoft只是想让开发人员的生活更难。而是你本来就不应该这样做

感谢您的评论和链接。这与UI自动化无关。我对"你本来就不应该这么做"这一说法持有异议——事实上,通过迭代窗口和有时进行COM管道处理是到达窗口的唯一方法。 - Sujay Ghosh
@Sujay:那个评论完全错了重点。你不应该进入这些窗口。我把它比作在没有得到别人允许的情况下偷走他们的玩具。这与COM无关,而是关于由Shell拥有的Windows桌面。 - Cody Gray
你不应该进入这些窗口。我的工作需要我使用这些窗口。使用这些窗口很基本,你也可以进入进程 - http://en.wikipedia.org/wiki/DLL_injection :-),而且有几个软件可以做到这一点。(我不仅谈论病毒)..LOL - Sujay Ghosh
@Sujay:我不会忽视糟糕的编程实践。你仍然没有明确说明你需要做什么。除此之外,你完全可以忽略我的警告,不要这样做。它没有记录,容易出错,而且你被特别建议不要这样做,但这从来没有阻止过任何人这样做。你现在拥有寻找两个桌面窗口的知识和能力。 - Cody Gray
我完全同意,这一定会出错,而且确实出了问题,因为这完全取决于微软放置Windows的方式。我也不喜欢它,但不幸的是这是唯一的方法。例如,开发访问Windows XP中Windows资源管理器右侧面板的代码将无法在Windows 7上工作,因为微软已经改变了设计。关于这个问题,我早就找到了解决方案,我使用Spy获取窗口。 - Sujay Ghosh

8
如果您想要使用在GetDesktopWindow()中定义的桌面窗口,请使用该窗口句柄。这是您应该用来查找顶层窗口和其他相关活动的窗口句柄。
在Spy++中看到的只是在您的会话中作为桌面绘制的内容。如果您在Spy++中使用自动定位功能,您将看到SysListView32声明的窗口是资源管理器 shell 的子窗口。很少有人需要访问此窗口。此外,此窗口的存在可能会因Windows版本而有所变化。
编辑(附加信息)
如果您想要与实际的 shell 桌面交互或放置东西,您可能最好使用其他 API。以下是两个可以完成此操作的 API,具体取决于目标 Windows 版本。 Windows Sidebar @ MSDN 此选项适用于 Vista 和 Windows 7

在MSDN上使用Active Desktop

尽管经常被用户和系统管理员禁用,但此功能可在Windows 2000和XP上使用。

1
但是 Spy++ 也返回窗口的句柄,而 Spy++ 返回的句柄和 GetDesktopWindow 返回的句柄是不同的。 - Sujay Ghosh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接