今天在QQ群里有人问怎样实现将自己的窗口嵌入桌面,让它和桌面融为一体,就像很多桌面日历软件那样。

我当时想到的就是建立一个Child  Window,将他的父窗口设置成桌面Shell窗口就可以了。但是后来想想觉得不对,因为很多桌面日历窗口都有半透明和阴影效果,明显是用Layered Window实现的,而大家知道Layered Window一定要用Pop Up Window才能实现的。

那么如何用Pop up Window实现这种效果呢? 这里关键的一点就是要将该窗口的Owner设置成桌面的Shell 窗口。

很多以为Pop Up Window的Owner窗口只能在Create时关联, 建立后没法动态修改,实际上微软是有接口让我们改的,只是他们不建议我们动态改,因为这样会影响窗口的层次关系,尤其是对于Modal Dialog。

我们将窗口Owner改成桌面Shell窗口的代码如下:
BOOL CheckParent(HWND hWnd)
{
    static HWND s_hWndOldParent = NULL;

   HWND hWndProgram = NULL;
   HWND hWndShellDLL = NULL;
   hWndProgram = FindWindow(_T("Progman"), _T("Program Manager"));
   if(hWndProgram != NULL)
   {
       hWndShellDLL = FindWindowEx(hWndProgram, NULL, _T("SHELLDLL_DefView"), NULL);
   }

   if(hWndShellDLL != NULL
       && hWndShellDLL != s_hWndOldParent)
   {
        SetWindowLong(hWnd, GWL_HWNDPARENT, (LONG)hWndShellDLL);
        s_hWndOldParent = hWndShellDLL;
        return TRUE;
   }

   return FALSE;
}

另外还有一个问题是一般Pop up窗口在Show出来时会显示在最上面,而我们是要让它显示在最下面, 所以要设置下Z-Order:
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);


    CheckParent(hWnd);

   SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0,
       SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);

这样上面的代码就实现了的窗口永远在桌面上,即使你点“显示桌面”或是WIN+D,也不受影响。

另外,如果你要让你的窗口在激活时也不会跑到其他窗口上面,只要创建时设置WS_EX_NOACTIVATE属性就可以了。

还有个问题是桌面Shell有可能重启,比如我们Kill掉Explorer.exe进程,所以我们最好一开始就启一个定时器,然后不停调用CheckParent(HWND hWnd)。

测试代码下载: source code
posted on 2012-05-03 22:07 Richard Wei 阅读(8537) 评论(8)  编辑 收藏 引用 所属分类: windows desktop

FeedBack:
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 08:28 | 请教一下
看到你的这个说明,有两个问题,第一个是界面如果要半透明如何处理,设定桌面为父窗口。半透明就失效了,
第二个问题:现在360安全卫士下这个会失效,好像是让360给拦截了  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 08:29 | 请教一下
方便可以联系QQ:460502163, 告诉我一下解决方案吗?  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 10:41 | Richard Wei
@请教一下
好像没有你说的半透明失效问题,可以参照我上传的测试代码。
至于360是否拦截,我就不知道了, 理论上这个不是安全问题,不该拦截的。  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 11:21 | 请教一下
我已经测试了,360拦截,您可以试一下,估计是他们要做360桌面,拦截消息  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 11:22 | Richard Wei
@请教一下
没环境,让客户不要装360吧 :)  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-19 11:25 | 请教一下
你的代码测试怎么就可以呢?我自己写的代码就拦截。,悲哀  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-05-31 17:24 | wxdvc
强人

感谢分享啊!  回复  更多评论
  
# re: 桌面上嵌入窗口(桌面日历)原理探索
2012-09-29 16:17 | Richard Wei
在Win7的Aero模式下因为桌面的窗口层次有变化, 上面的代码会找不到窗口"SHELLDLL_DefView"窗口

简单改了下,未完全测试:
HWND GetShellDLLForWin7()
{
HWND hWndShell = NULL;
HWND hWndDesktop = GetDesktopWindow();
if(hWndDesktop == NULL) return NULL;

HWND hWndWorkerW = NULL;
while(TRUE)
{
hWndWorkerW = FindWindowEx(hWndDesktop, hWndWorkerW, _T("WorkerW"), NULL);
if(hWndWorkerW == NULL) break;

if(::GetWindowLong(hWndWorkerW, GWL_STYLE) & WS_VISIBLE)
{
break;
}
else
{
continue;
}
}

if(hWndWorkerW != NULL)
{
hWndShell = FindWindowEx(hWndWorkerW, NULL, _T("SHELLDLL_DefView"), NULL);
}

return hWndShell;
}

BOOL CheckParent(HWND hWnd)
{
static HWND s_hWndOldParent = NULL;

HWND hWndProgram = NULL;
HWND hWndShellDLL = NULL;
hWndProgram = FindWindow(_T("Progman"), _T("Program Manager"));
if(hWndProgram != NULL)
{
hWndShellDLL = FindWindowEx(hWndProgram, NULL, _T("SHELLDLL_DefView"), NULL);

//Win7
if(hWndShellDLL == NULL)
{
hWndShellDLL = GetShellDLLForWin7();
}
}

if(hWndShellDLL != NULL
&& hWndShellDLL != s_hWndOldParent)
{
SetWindowLong(hWnd, GWL_HWNDPARENT, (LONG)hWndShellDLL);
s_hWndOldParent = hWndShellDLL;
return TRUE;
}

return FALSE;
}  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理