天天看點

在桌面中嵌入窗體

前幾天在網上看到一個軟體的介紹:可以嵌入桌面,即使是“顯示桌面”也不會影響此程式。看作者說的好像有多麼的神奇一樣。周未就回來試一下。最後發現,Windows這個桌面還真是複雜和有意思。

  首先要分析Windows桌面。

  打開老牌軟體"Spy Window"。檢視一下桌面。取得一個“SysListView32”類的句柄(本系統為XP版本)。将其最小化,可以看出剛才取得的控件好像是透明的。因為将其最小化之後,還可以看到你所設定的桌面圖檔。

  重新用"Spy Window"擷取桌面上的控件句柄(也可以直接點選"Parent Window"取得其父視窗句柄),得到一個"SHELLDLL_DefView"類的句柄。将其最小化,可以看到桌面圖檔依然存在,難道又是一個透明控件嗎?先不理會它,我們繼續向“下”找。再一次取得“桌面”上一個類名為“Progman”的控件句柄。而且此時你會發現Spy Window的"Parent Window"按鈕已不可用了。

  這個類為“Progman”的視窗“下面”真的沒有其它視窗了嗎?按“Ctrl+Alt+Del“在任務管理器裡結束“explorer”,後再使用“Spy Window”看一下,是不是又有一個類名為“#32769”的視窗出了。試着對此視窗進行禁用,最小化,隐藏操作試一下。好像一切都是無效的。

  到此為至,應該說把這張桌面的結構搞清楚了。相當于圖像進行中的四個圖層,而且是透明圖層。

  按類名由前至裡的排序為:

  SysListView32

  SHELLDLL_DefView

  Progman

  #32769

  看來這個桌面果然不是一般的複雜。

  回憶一下以前用代碼來隐藏桌面的操作:

  FindWindow(''''Progman'''',Nil);

  ShowWindow(...);

  這裡的''''Progman''''就是第三層(本文中我們就以層來稱呼它們)的視窗了。在結束程序“Explorer”時,此視窗消失,說明此視窗是由“Explorer.exe”建立的。

  下面進行将程式嵌入到桌面裡的操作。

  這裡所需要的隻有一個語句:

  FrmMain.ParentWindow:=ParentHandle;其中,ParentHandle是你所要嵌人的控件句柄。

  按此實作,可以建立一個窗體,拖入一個TButton,一個TEdit。在Button的Click事件中寫入代碼FrmMain.ParentWindow:=StrToInt(EdtHandle.Text);

  下面,先來嵌入“第一層桌面”看一下。用"Spy Window”取得目前桌面句柄,也就是第一層''''SysListView32''''。轉為十進制後複制到EdtHandle。點選按鈕。

  程式是不是轉為非焦點狀态了。按一下“Win+D”(顯示桌面)。是不是視窗仍停留在桌面上。

  好像文章開頭的目的已經實作了。

  仔細測試一下目前的窗體,是不是與原來有很大的不同。首先,視窗的标題欄總是非焦點狀态。第二窗體上的右擊被桌面攔截了下來。

  第三Edit裡表顯不出TEdit本身對消息的響應。如點選時,拖動時,按鍵時右擊時,Edit缺少相應的閃爍輸入光标,抹黑所選字元,文字處理,顯示上下文菜單等。這是因為窗體得不到焦點,而得不到焦點對于TEdit控件來說,一切都是無效的。

  動态取得第一層控件句柄的方法是:

  TmpHandle:=FindWindow(''''Progman'''',Nil);

  TmpHandle:=GetWindow(TmpHandle,GW_CHILD);

  此時TmpHandle即是桌面的句柄了。

  依照此方法,我們可以将窗體嵌入第二層''''SHELLDLL_DefView''''了。當嵌入第二層時,你會發現。所嵌入的程式視窗不見了。當我們把第一層最小化時,可以看到我們所嵌入的視窗是存在的。隻是被第一層所遮住了。是以說,第一層并不是透明的!

  第一層最小化之後,可以看到,桌面上的圖示都不見了。再看一下第一層的類名“SysListView32”,可以确定,第一層這個控件的作用主要就是列出系統桌面上的圖示。我們在目前第二層中點選一下右鍵。桌面菜單出來了吧?原來一切的消息及處理都是在這一層接收和處理的。這時可以用“建立”指令建立一個文檔,之後再恢複第一層桌面,可以看到,建立的文檔出現了。

  可以這樣了解,第一層是“顯示層”,第二層是“功能層”。我們的窗體在這裡是顯示不出來的。而且同樣得不到焦點。

  現在,将我們的程式嵌入到第三層''''Progman'''',嵌入之後,出現了和第二層相同的結果,按功能來說這一層應該沒有什麼實際的用途,可能隻是給上面兩層提供一個容器。現在''''Progman''''中有了兩個窗體,一個是原有的''''SHELLDLL_DefView'''',另一個便是這個嵌入窗體。但是前者用盡了所有的可視區域,是以才使得嵌入的窗體顯示不出來。這種情況似乎平時也會遇到,那我們在嵌入時加入一句:BringWindowToTop(FrmMain.Handle);試試;

  呵呵,看到了什麼?是不是嵌入的視窗出現了?按一下"Win+D"看一下。如何?還在吧?如果桌面上有圖示的話,此時這個窗體應該是擋遮住了一部分圖示的。

處理的辦法就是将上一層窗體縮小。如:

  MoveWindow(TmpHandle,0,20,1024,740,False);

  這樣,在窗體頂部留出了二十象素的高度。可以放一個工作列式的窗體了。

  現在隻剩下最後一層"#32769"了。隻要在系統登陸前的啟動程式不變,此視窗的句柄應該是不變化的(有可能系統登陸前啟動的程式有變化此句柄也不變,具體情況沒試過)。

  按前面的方法将窗體嵌入到此視窗中。

  窗體又是得不到焦點的狀态了。可以看出來這和嵌入到第一層差不多。但是我們拖動一下窗體看一下。此時窗體并不是實時跟随滑鼠的。再仔細看一下,工作列上出現了兩個此程式的按鈕。一個是程式的名稱,一個是視窗的名稱。這是一種奇怪的現象,從來沒有見過的。或許我們可以這樣解釋它。Explorer會将符合要求的窗體顯示在工作列上(非ToolsWindows,并且可見)。本窗體就符合,而且Explorer又會将視窗"#32769"裡的所有視窗放到工作列上而不管它是否複。是以才會得到此結果。

  總結一下:

  Windows的桌面是分四層的。嵌入的窗體如果嵌入到第三層,并将Z軸順序移到最上的話,程式就會一個正常的嵌入桌面的程式。這符合我們的要求。而且可以通過調整第二層的大小來使窗體不遮住桌面圖示。是以,将窗體嵌入到此是很理想的。

  第一層的嵌入也是可以的。但是在這裡窗體會得不到焦點和使用不了右鍵。是以這裡的窗體受很多限制。

  第二層是一個根本不考慮嵌入窗體的地方,因為這裡的窗體根本顯示不出來。而且與第一層相同的得不到焦點。

  第四層是個意外的層。嵌在這裡的窗體會表顯出異樣的情況。唯一值得我們嵌入的理由是:它不會随Explorer.exe程序的結束而關閉。

用mfc實作就是這樣的代碼

HWND hDesktop = ::FindWindow("Progman", NULL);

hDesktop = ::GetWindow(hDesktop, GW_CHILD);

CWnd* pWndDesktop = CWnd::FromHandle(hDesktop);

this->SetParent(pWndDesktop);

繼續閱讀