wallpaper是一款優(yōu)秀的動(dòng)態(tài)壁紙軟件,除了播放動(dòng)畫以外,還可以執(zhí)行程序,甚至可以實(shí)時(shí)響應(yīng)鼠標(biāo)移動(dòng)。 原理分析windows的桌面是由不同的二窗體構(gòu)成,包括圖標(biāo)層,背景層,背景層顯示桌面壁紙,圖標(biāo)層放置圖標(biāo),且圖標(biāo)層背景透明,因此可以直接看到后面的背景層,鼠標(biāo)右鍵彈出菜單也是在圖標(biāo)層完成。wallpaper在圖標(biāo)層和背景層之間插入了自己的窗口,因此可以顯示動(dòng)畫,執(zhí)行代碼。前面已經(jīng)提到圖標(biāo)層是一個(gè)透明的覆蓋全屏的大窗口,因此鼠標(biāo)事件只會(huì)在圖標(biāo)層響應(yīng),而wallpaper可以實(shí)時(shí)響應(yīng)鼠標(biāo)可能是利用了Hook攔截了鼠標(biāo)事件,并加入自己代碼。 既然知道了原理就可以自己實(shí)現(xiàn)。 C#實(shí)現(xiàn)界面繪制首先創(chuàng)建兩個(gè)窗體,一個(gè)用來(lái)播放視頻,一個(gè)用來(lái)控制 上圖是控制窗口,也是主窗口。 另一個(gè)視頻窗口較為簡(jiǎn)單,直接用MediaPlayer覆蓋全屏就行,注意需要設(shè)置WindowState為Maximized,即啟動(dòng)時(shí)立即最大化,同時(shí)播放器要隱藏ui,即設(shè)置uiMode為none。 在主窗體的load事件里新建VideoForm。為了讓VideoForm能夠夾在圖標(biāo)層和背景層中間,需要將VideoForm的父窗體設(shè)置為背景窗體。 查找句柄現(xiàn)在需要查找背景窗體的句柄,使用窗口查看器發(fā)現(xiàn)背景窗體沒(méi)有窗體名稱,因此無(wú)法直接定位,但是我們知道它的類名是WorkW,它的父窗體是Program Manager,所以我們可以遍歷所有WorkW窗體,如果其中一個(gè)窗體的父窗體是Program Manager,那么這個(gè)窗體就是背景窗體。 C#不支持直接這種接近底層的操作,因此需要調(diào)用user32.dll實(shí)現(xiàn) 代碼語(yǔ)言:javascript 復(fù)制
其中GetBackground函數(shù)負(fù)責(zé)查找背景層窗體,SetFather負(fù)責(zé)把一個(gè)窗體設(shè)置成另一個(gè)窗體的子窗體。為了使用指針功能,需要先開啟不安全的代碼功能 :項(xiàng)目—??屬性(??是你的項(xiàng)目名稱)—允許不安全代碼。 這個(gè)方法在Windows 10 21H1 19043.1110上測(cè)試有效,但是不保證在其他系統(tǒng)有效,例如,在vista系統(tǒng)上就會(huì)返回空指針,這可能是因?yàn)関ista系統(tǒng)上的背景窗體不滿足上面所講的關(guān)系。一旦返回空指針,會(huì)導(dǎo)致設(shè)置父窗體失敗,最后視頻會(huì)在圖標(biāo)層上方播放,此時(shí)的動(dòng)態(tài)壁紙軟件就徹底變成了一個(gè)全屏播放器。 如果遇到上面這種情況,可以使用MicrosoftSpy來(lái)查找背景窗體,并根據(jù)具體情況改寫上面的代碼。 這里利用了windows窗口的一個(gè)特性:如果A窗體在B窗體上面,那么A窗體也會(huì)在B窗體的子窗體上面。 按鈕事件給控制窗體的四個(gè)按鈕寫上事件 代碼語(yǔ)言:javascript 復(fù)制
其中main是視頻播放窗體,player是播放器 運(yùn)行 點(diǎn)擊退出 刷新背景雖然程序退出了,但是桌面變成了一張白紙,極其難看,目前暫不知道為什么會(huì)發(fā)生這種情況,個(gè)人猜測(cè)是windows考慮到背景是一張靜態(tài)圖,所以不會(huì)實(shí)時(shí)刷新,而剛剛被覆蓋掉的地方就會(huì)保持最后一次刷新的顏色,剛才點(diǎn)擊“退出”時(shí),由于先dispose了視頻播放窗體,導(dǎo)致背景變成白板,如果不點(diǎn)擊“退出”,直接結(jié)束進(jìn)程,那么背景就會(huì)變成黑板,因?yàn)镸ediaPlayer就是黑色的 既然如此,我們只需要讓背景刷新一下就可以,顯然在切換壁紙的時(shí)候,windows不得不刷新背景,所以我們可以先獲取當(dāng)前壁紙,然后把壁紙切換成當(dāng)前壁紙,這樣實(shí)際效果看起來(lái)沒(méi)有任何變化,但是讓windows為我們刷新了一次背景。 代碼語(yǔ)言:javascript 復(fù)制
改寫“退出”按鈕事件 代碼語(yǔ)言:javascript 復(fù)制
之所以先隱藏,是因?yàn)樵赿ispose和refresh執(zhí)行的空隙里會(huì)有一瞬間的白屏,如果先隱藏就可以避免這種情況。 因?yàn)橐曨l壁紙需要常駐后臺(tái),而控制窗口不可能常駐桌面,所以我們需要改寫它的Formclosing,取消窗體關(guān)閉事件,并隱藏窗體 代碼語(yǔ)言:javascript 復(fù)制
給窗體加上NotifyIcon控件,該控件可以顯示任務(wù)欄角標(biāo),改寫雙擊事件,雙擊角標(biāo)時(shí)顯示控制窗體 代碼語(yǔ)言:javascript 復(fù)制
到現(xiàn)在完整的Wallpaper已經(jīng)制作完成,但是目前僅能播放視頻。當(dāng)然也包括圖片,但是你需要設(shè)置MediaPlayer的循環(huán)播放,否則圖片顯示幾秒后就會(huì)變成純黑壁紙。 資源占用看看GPU占用情況 以上數(shù)據(jù)是我在播放電影《龍之谷精靈王座》時(shí)的資源占用情況,該電影共1.83GB,可以看到內(nèi)存占用不到100MB,GPU0是核顯,核顯占用也才2%,比起wallpaper已經(jīng)非常優(yōu)秀了,但同時(shí)功能也非常單一,不過(guò)如果僅僅用來(lái)播放視頻,完全可以用來(lái)替代wallpaper。 如果你想要實(shí)現(xiàn)更多好玩的功能,也可以往視頻播放窗體里加別的東西,但是需要注意一點(diǎn),所有需要交互的事件都不會(huì)響應(yīng),比如鼠標(biāo)點(diǎn)擊,你只能通過(guò)控制窗體來(lái)修改視頻播放窗體的內(nèi)容。 EXE文件鏈接打開后是一個(gè)壓縮包,里面包含兩個(gè)dll和一個(gè)exe,這三個(gè)文件需要放在同一目錄下才可以運(yùn)行 |
|