一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Wallpaper的原理和C#實(shí)現(xiàn)(含源文件)

 心有樹 2024-07-24

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)控制

DearXuan
DearXuan

上圖是控制窗口,也是主窗口。

另一個(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ù)制
[DllImport("user32.dll", EntryPoint = "SetParent")]
private static extern int SetParent(int hWndChild,int hWndNewParent);
[DllImport("user32.dll", EntryPoint = "FindWindowA")]
private static extern IntPtr FindWindowA(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindowExA")]
private static extern IntPtr FindWindowExA(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "GetClassNameA")]
private static extern IntPtr GetClassNameA(IntPtr hWnd, IntPtr lpClassName, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "GetParent")]
private static extern IntPtr GetParent(IntPtr hWnd);
 
public static void SetFather(Form form)
{
    SetParent((int)form.Handle, GetBackground());
}
 
private static int GetBackground()
{
    unsafe
    {
        IntPtr background = IntPtr.Zero;
        IntPtr father = FindWindowA("progman", "Program Manager");
        IntPtr workerW = IntPtr.Zero;
        do
        {
            workerW = FindWindowExA(IntPtr.Zero, workerW, "workerW", null);
            if (workerW != IntPtr.Zero)
            {
                char[] buff = new char[200];
                IntPtr b = Marshal.UnsafeAddrOfPinnedArrayElement(buff, 0);
                int ret = (int)GetClassNameA(workerW, b, 400);
                if (ret == 0) throw new Exception("出錯(cuò)");
            }
            if (GetParent(workerW) == father)
            {
                background = workerW;
            }
        } while (workerW != IntPtr.Zero);
        return (int)background;
    }
}

其中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ù)制
private void Form1_Load(object sender, EventArgs e)
{
    main = new VideoForm();
    player = main.player;
    Window.SetFather(main);
    main.Show();
}
private void button1_Click(object sender, EventArgs e)//打開
{
    OpenFileDialog open = new OpenFileDialog();
    open.Filter = "媒體文件(所有類型)|*.mp4;*.mpeg;*.wma;*.wmv;*.wav;*.avi|所有文件|*.*";
    if (open.ShowDialog() == DialogResult.OK)
    {
        player.URL = open.FileName;
    }
}
 
private void button2_Click(object sender, EventArgs e)//播放
{
    player.Ctlcontrols.play();
}
 
private void button3_Click(object sender, EventArgs e)//暫停
{
    player.Ctlcontrols.pause();
}
 
private void button4_Click(object sender, EventArgs e)//退出
{
    main.Dispose();
    System.Environment.Exit(0);
}

其中main是視頻播放窗體,player是播放器

運(yùn)行

點(diǎn)擊退出

DearXuan
DearXuan

刷新背景

雖然程序退出了,但是桌面變成了一張白紙,極其難看,目前暫不知道為什么會(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就是黑色的

DearXuan
DearXuan

既然如此,我們只需要讓背景刷新一下就可以,顯然在切換壁紙的時(shí)候,windows不得不刷新背景,所以我們可以先獲取當(dāng)前壁紙,然后把壁紙切換成當(dāng)前壁紙,這樣實(shí)際效果看起來(lái)沒(méi)有任何變化,但是讓windows為我們刷新了一次背景。

代碼語(yǔ)言:javascript
復(fù)制
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern int SystemParametersInfo(int uAction, int uParam, StringBuilder lpvParam, int fuWinIni);
 
public static bool Refresh()
{
    StringBuilder wallpaper = new StringBuilder(200);
    SystemParametersInfo(0x73, 200, wallpaper, 0);
    int ret = SystemParametersInfo(20, 1, wallpaper, 3);
    if(ret != 0)
    {
        RegistryKey hk = Registry.CurrentUser;
        RegistryKey run = hk.CreateSubKey(@"Control Panel\Desktop\");
        run.SetValue("Wallpaper", wallpaper.ToString());
        return true;
    }
    return false;
}

改寫“退出”按鈕事件

代碼語(yǔ)言:javascript
復(fù)制
private void button4_Click(object sender, EventArgs e)//退出
{
    main.Hide();
    this.Hide();
    Window.Refresh();
    main.Dispose();
    System.Environment.Exit(0);
}

之所以先隱藏,是因?yàn)樵赿ispose和refresh執(zhí)行的空隙里會(huì)有一瞬間的白屏,如果先隱藏就可以避免這種情況。

因?yàn)橐曨l壁紙需要常駐后臺(tái),而控制窗口不可能常駐桌面,所以我們需要改寫它的Formclosing,取消窗體關(guān)閉事件,并隱藏窗體

代碼語(yǔ)言:javascript
復(fù)制
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    e.Cancel = true;
    this.Hide();
}

給窗體加上NotifyIcon控件,該控件可以顯示任務(wù)欄角標(biāo),改寫雙擊事件,雙擊角標(biāo)時(shí)顯示控制窗體

代碼語(yǔ)言:javascript
復(fù)制
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    this.Show();
}

到現(xiàn)在完整的Wallpaper已經(jīng)制作完成,但是目前僅能播放視頻。當(dāng)然也包括圖片,但是你需要設(shè)置MediaPlayer的循環(huán)播放,否則圖片顯示幾秒后就會(huì)變成純黑壁紙。

資源占用

看看GPU占用情況

DearXuan
DearXuan

以上數(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)容。

源代碼

https://dearx./iiP4frxcm4d

EXE文件

https://dearx./iIPmWrxcn6b

EXE文件鏈接打開后是一個(gè)壓縮包,里面包含兩個(gè)dll和一個(gè)exe,這三個(gè)文件需要放在同一目錄下才可以運(yùn)行

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美一级特黄特色大色大片| 亚洲中文字幕剧情在线播放| 国产午夜免费在线视频| 亚洲女同一区二区另类| 91精品国产av一区二区| 亚洲综合激情另类专区老铁性| 麻豆精品视频一二三区| 91欧美一区二区三区| 亚洲国产精品av在线观看| 黄色污污在线免费观看| 亚洲妇女黄色三级视频| 亚洲精品日韩欧美精品| 国产成人精品资源在线观看| 久久亚洲国产视频三级黄| 日韩一区二区三区在线欧洲| 激情三级在线观看视频| 开心激情网 激情五月天| 男人和女人草逼免费视频 | 国产目拍亚洲精品区一区| 少妇特黄av一区二区三区| 日韩欧美综合中文字幕| 天海翼精品久久中文字幕| 欧美在线观看视频免费不卡| 精品人妻一区二区三区免费看| 午夜视频成人在线免费| 亚洲熟女精品一区二区成人| 2019年国产最新视频| 老司机精品线观看86| 国产精品伦一区二区三区在线| 亚洲精品熟女国产多毛| 99国产一区在线播放| 欧美一二三区高清不卡| 午夜直播免费福利平台| 91欧美日韩精品在线| 日本特黄特色大片免费观看| 成人区人妻精品一区二区三区| 亚洲国产精品无遮挡羞羞| 国产精品成人一区二区在线| 丰满人妻一二三区av| 亚洲国产91精品视频| 中文字幕日韩欧美亚洲午夜|