DirectX桌面版

19

我想使用DirectX为Windows 7创建一个动态桌面背景。我使用C#、SlimDX以及一些Windows API函数的P/Invoke引入。我不是很擅长本地Windows编程,但我在网上搜索过并认为我需要做的是:

1)找到包含桌面壁纸的窗口句柄,将其连接到DirectX设备并绘制到其中。

2)创建一个新的输出窗口,并将其插入到桌面壁纸上方但在桌面图标下方。

我尝试了这两种方法,但似乎都没有奏效。如果我从GetDesktopWindow()返回的句柄开始导航窗口层次结构,我可以按照Desktop -> WorkerW -> SHELLDLL_DefView -> SysListView32的顺序访问。如果我将DirectX设备连接到该句柄,我可以绘制整个桌面,但它也会遮盖住图标。如果我创建一个Windows表单,将其父级设置为SHELLDLL_DefView,然后使用SetWindowPos来调整其Z顺序,则只能使它出现在桌面壁纸后面或者桌面+图标前面。

看起来桌面壁纸是包含图标的文件夹视图的背景,因此我想要的操作不能生效。那么唯一的解决方法就是不使用桌面来放置图标,或者找到其他替代方案,例如覆盖桌面然后叠加一个透明窗口,其中包含某个文件夹内容的视图。

有人知道我应该做些什么吗,甚至是否可能实现我想要做的事情吗?似乎可以使用GDI(正如我所认为的wxSnow程序所做的那样)绘制到桌面背景,并且我看到VLC Media Player在Windows XP下使用其DirectX壁纸模式完成了与我想要的类似的功能(有趣的是,我无法在我的系统上启用此选项)。

谢谢!


哇,我甚至没有意识到DreamScene已经从Windows 7中删除了。我有点喜欢Vista的这个功能!这里有一篇博客文章,介绍如何重新安装它:http://www.mydigitallife.info/2009/01/14/how-to-install-and-enable-dreamscene-in-windows-7/(当然,这允许您在不需要编写程序的情况下为桌面添加动画效果,对于那些希望简单地为桌面添加动画效果而不需要编写程序的人来说,这非常有用)。 - Ricket
1
你最好使用C++进行开发,因为我认为DirectX 9是最后一个带有托管DirectX API的版本。 - James Black
1
SlimDX(http://slimdx.org/)是一个DirectX的封装器,与MDX不同的是,它经常更新以支持最新版本的DirectX。你说得对,我可能应该用C++开发,但我太喜欢C#了。 - Jonathan
@Ricket 我认为取消梦境场景的决定很大程度上是因为它存在缺陷。我用过一段时间,但它总是出问题,行为异常,以至于我不得不放弃它。 - Joel Coehoorn
3个回答

5

看起来这似乎不可能。请参考此链接:

http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/69839cec-3424-4300-9ac3-486b8c2fe492

如果您需要在桌面背景和桌面图标之间绘制一些控件,则可以按照以下步骤进行备选:
1. 在 Windows 控件库中创建用户控件。 2. 将用户控件嵌入 ActiveX 控件中。 3. 将 ActiveX 控件嵌入网页中。 4. 启用活动桌面并将网页设置为您的桌面背景。
由于 Vista 不支持活动桌面,因此只能在 XP 中完成此操作。
另一篇帖子建议您可能可以使用资源管理器窗口的背景(如果您可以获取构成它的窗口句柄)。当然,如果可能的话,也可能获取到图标后面的桌面窗口的句柄。
更新:目前我找到的唯一可能“有效”的方法就是反复创建位图文件并更改壁纸(正如您所提到的,这可能会很慢)。
那个全屏图像必须在某个地方驻留在内存中,但可能没有办法访问它,除非进行一些严重的低级内存操作。我会继续寻找。
更新 2:这可能有效,但我不确定:

http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/4af734fb-d2c1-414b-a9f1-759b76692802

它的核心是这样的:

HWND p = FindWindow("ProgMan", NULL);
HWND s = FindWindowEx(p, NULL, "SHELLDLL_DefView", NULL);
HWND dtw = FindWindowEx(s, NULL, "SysListView32", NULL);
HDC hdc = GetDC(dtw);

您基本上是从ProgMan窗口开始,然后向下钻取到桌面壁纸(“SysListView32”,我猜)。我将尝试这个。 更新3:不行-上述代码确实获取了桌面的DC,但它在图标上方,因此BitBlt会覆盖它们。它没有绘制到屏幕上,因为我可以在打开的表单下面绘制而不盖住它,所以至少有进展。
我猜除了“SysListView32”之外还有其他可用于图标背后的桌面窗口,或者有多个“SysListView32”窗口。 更新4:我非常确定使用这个东西会起作用:

http://msdn.microsoft.com/en-us/library/bb761155(v=VS.85).aspx http://msdn.microsoft.com/en-us/library/bb774742(v=VS.85).aspx

基本上,它是一个API方法,你调用它并传入一个结构体,其中包括一个位图句柄。如果调用成功,该位图将成为桌面。

DirectX是否将帧作为位图句柄(GDI兼容)公开,还是仅公开DC?在我的情况下,我的动画已经是一系列GDI兼容位图,所以我使用这种方法没有问题。如果这是唯一的路线,而DirectX不公开位图句柄(我认为他们不会),那么对于每个帧,您都需要创建一个新的GDI位图,这会使速度变慢。

实际上,可能有一种更简单的方法,但我不确定它是否可行。一旦您获得了壁纸实际位图的句柄,就可以使用SelectObject将其选择到设备上下文中,然后只需将该设备上下文用作BitBlt目标即可。不过,您可能需要向桌面发送重新绘制指令,这可能会触发每次重绘所有图标。

你能不能在这里做些工作呢? :)


1
GDI(如BitBlt等)本身没有任何性能问题。我用它来进行快速的全屏动画(>30 fps),这比相应的DirectX / DirectShow方法占用CPU更少。实际上,使用GDI进行动画的主要限制是根本无法处理“撕裂”效果,因为GDI完全不知道您的监视器扫描线的位置。 DirectX有一种半可靠的方法来解决这个问题,尽管使用它来防止撕裂仍然很麻烦。 - MusiGenesis
@Andreas:就我所知,无论是GDI还是DirectX都不能解决这个特定的问题。而且,据我所知,提问者只是想渲染一个2D动画(仅因为DirectX可以做3D并不意味着他在这里做的就是3D)。此外,GDI 可以处理3D,只是不是你所指的那种意义上(只要单元格是预先生成的,GDI可以用于任何复杂程度的动画 - 包括3D)。 - MusiGenesis
@MusiGenesis:是的,我知道。但是在没有硬件加速的情况下渲染实时3D会很慢。 - Andreas Rejbrand
@Andreas:这是我的最终结论——我是对的,你是错的:Jonathan 没有提到 3D。 :) - MusiGenesis
1
没问题,偶尔沉浸在这样的事情中也是很有趣的。我刚刚问了另一个与此相关的问题,因为我认为这几乎肯定是通过获取背景位图的句柄来完成的。 - MusiGenesis
显示剩余8条评论

1

我猜你可以通过动态生成位图并不断将其设置为桌面背景来获得相当多的帧率。然而,这听起来对于CPU来说是不必要的(几乎是愚蠢的)负担。

更新:现在我想了想(当我写这篇文章时已经很晚了),这种方法的主要问题是每次显示新帧时都需要从硬盘上读取和写入位图,这是不可行的。

你真的在使用桌面图标吗?在Windows 7中,开始菜单搜索功能和新任务栏使我不再使用桌面图标。

只是一个想法

也许可以通过SendMessage(GetDesktopWindow, WM_SOME_MESSAGE, wParam, lParam)的方式来修改桌面窗口,以便实现你想要的效果。我可能会在明天进一步调查这个问题(目前是当地时间3点)。


正如你所说,我认为使用GDI或使用API函数设置墙纸可能会占用大量CPU,而这恰恰是我想使用DirectX而不是DreamScene的主要原因。我记得看到过一些关于使用SendMessage()来实现我的目标的参考资料,但目前Windows API的这部分对我来说有点超出范围。如果看起来很有前途,我会去学习它的。否则,我会接受你的建议,停止将桌面用作随意存储的地方。 - Jonathan

1

我已经完成了一些Alpha混合窗口的动画,它们总是在Z-Order的顶部。一个想法是忽略尝试修改桌面位图,而只是将你的东西绘制为分层窗口,但以某种方式管理ZOrder,使其始终成为最底部的非桌面窗口(也许通过在正确的时间策略性地调用Form.SendToBack()或其他方法)。

根据您要做什么,这可能会给您相同的桌面动画效果。


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