該程式需支援兩台螢幕,並能夠逐一更換每台螢幕的桌布。這意味著程式需要能夠識別並控制多個顯示器的設置,確保每個螢幕上顯示不同的圖片且不卡頓。
圖片來源設定為 E:\wallpaper
,且圖片數量龐大(千張以上),因此必須採取隨機讀取且不一次載入所有圖片的方法。程式需記錄已播放過的圖片,並在所有圖片播放完畢後才能重覆播放,防止重複顯示。
已播放過的圖片需記錄在Windows的臨時目錄(%Temp%)中的 rollings.txt
文件中。程式重啟時需讀取此文件,以避免已播放圖片的重覆。
圖片需根據螢幕解析度自動調整大小,確保圖像不變形。這需要使用圖像處理庫來計算縮放比例,並保持圖片的寬高比。
桌布需每分鐘更換一次,每次更換時逐一更換兩台螢幕的桌布,確保每台螢幕在不同時間點更新,避免同時更換導致的性能衝擊。
桌布更換時,需在螢幕頂部顯示當前圖片的全檔名。這可以通過在圖片上覆蓋文字,或創建一個透明的頂層視窗來實現。
C++ 是實現此桌布輪換程式的理想語言,因為它能夠高效地處理系統API調用和圖像處理。以下是主要的功能模組:
#include <windows.h>
#include <shlobj.h>
#include <gdiplus.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <filesystem>
namespace fs = std::filesystem;
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")
std::wstring GetTempFolder()
{
wchar_t szPath[MAX_PATH];
GetTempPathW(MAX_PATH, szPath);
return std::wstring(szPath);
}
std::vector<std::wstring> LoadRollings()
{
std::vector<std::wstring> used;
std::wstring rollFile = GetTempFolder() + L"rollings.txt";
std::wifstream ifs(rollFile);
std::wstring line;
while(getline(ifs, line))
{
if(!line.empty())
used.push_back(line);
}
return used;
}
void AppendRolling(const std::wstring& fullpath)
{
std::wstring rollFile = GetTempFolder() + L"rollings.txt";
std::wofstream ofs(rollFile, std::ios::app);
ofs << fullpath << std::endl;
}
void ClearRollings()
{
std::wstring rollFile = GetTempFolder() + L"rollings.txt";
std::wofstream ofs(rollFile, std::ios::trunc);
}
std::wstring GetRandomImage(const std::wstring& folder)
{
std::vector<std::wstring> allImages;
for (auto& entry : fs::directory_iterator(folder))
{
if(entry.is_regular_file())
{
std::wstring ext = entry.path().extension().wstring();
if(_wcsicmp(ext.c_str(), L".jpg")==0 ||
_wcsicmp(ext.c_str(), L".jpeg")==0 ||
_wcsicmp(ext.c_str(), L".png")==0 ||
_wcsicmp(ext.c_str(), L".bmp")==0)
{
allImages.push_back(entry.path().wstring());
}
}
}
if(allImages.empty())
return L"";
std::vector<std::wstring> used = LoadRollings();
std::vector<std::wstring> available;
for(auto& path : allImages)
{
if(std::find(used.begin(), used.end(), path) == used.end())
available.push_back(path);
}
if(available.empty())
{
ClearRollings();
available = allImages;
}
int idx = rand() % available.size();
return available[idx];
}
bool PrepareWallpaper(const std::wstring& imgPath, const RECT& screenRect, const std::wstring& outPath)
{
Bitmap* pBmp = new Bitmap(imgPath.c_str());
if(pBmp->GetLastStatus() != Ok)
{
delete pBmp;
return false;
}
int screenW = screenRect.right - screenRect.left;
int screenH = screenRect.bottom - screenRect.top;
UINT imgW = pBmp->GetWidth();
UINT imgH = pBmp->GetHeight();
double ratioW = (double)screenW / imgW;
double ratioH = (double)screenH / imgH;
double scale = min(ratioW, ratioH);
int newW = (int)(imgW * scale);
int newH = (int)(imgH * scale);
Bitmap bmpNew(screenW, screenH, PixelFormat24bppRGB);
Graphics g(&bmpNew);
g.Clear(Color::Black);
int x = (screenW - newW) / 2;
int y = (screenH - newH) / 2;
g.DrawImage(pBmp, x, y, newW, newH);
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, 20, FontStyleBold, UnitPixel);
SolidBrush brush(Color(255,255,255,255));
RectF layoutRect(0, 0, (REAL)screenW, 30);
std::wstring displayText = imgPath;
g.DrawString(displayText.c_str(), -1, &font, layoutRect, NULL, &brush);
CLSID clsidEncoder;
UINT num = 0, size = 0;
GetImageEncodersSize(&num, &size);
if(size == 0)
return false;
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if(wcscmp(pImageCodecInfo[j].MimeType, L"image/bmp") == 0)
{
clsidEncoder = pImageCodecInfo[j].Clsid;
break;
}
}
free(pImageCodecInfo);
Status stat = bmpNew.Save(outPath.c_str(), &clsidEncoder, NULL);
delete pBmp;
return stat == Ok;
}
bool SetWallpaper(const std::wstring& filePath)
{
return SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (void*)filePath.c_str(), SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
}
std::vector<RECT> GetAllMonitorRect()
{
std::vector<RECT> rects;
auto MonitorEnumProc = [](HMONITOR hMon, HDC hdcMon, LPRECT lprcMon, LPARAM dwData) -> BOOL
{
std::vector<RECT>* pRects = reinterpret_cast<std::vector<RECT>*>(dwData);
pRects->push_back(*lprcMon);
return TRUE;
};
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&rects);
return rects;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
srand((unsigned)time(NULL));
const std::wstring wallpaperFolder = L"E:\\wallpaper";
while(true)
{
std::vector<RECT> monitors = GetAllMonitorRect();
for(size_t i = 0; i < monitors.size(); i++)
{
std::wstring imgPath = GetRandomImage(wallpaperFolder);
if(imgPath.empty())
continue;
AppendRolling(imgPath);
std::wstring tempFolder = GetTempFolder();
std::wstring tempFile = tempFolder + L"wallpaper_" + std::to_wstring(i) + L".bmp";
if(!PrepareWallpaper(imgPath, monitors[i], tempFile))
continue;
if(i==0)
{
SetWallpaper(tempFile);
}
else
{
// 多螢幕設定需使用第三方API或修改登錄檔
}
}
Sleep(60000);
}
GdiplusShutdown(gdiplusToken);
return 0;
}
上述C++程式碼展示了如何實現桌布輪換的核心功能,包括隨機選取圖片、記錄播放過的圖片、調整圖片大小以適應螢幕,以及設置桌布。主要步驟如下:
std::filesystem
遍歷圖片目錄,並隨機選取未播放過的圖片。rollings.txt
中,以避免重複播放。Lua語言可以通過調用外部程式來實現桌布輪換功能。雖然Lua本身不擅長處理系統級操作,但與C++或批次檔結合使用,能夠有效完成需求。
math.randomseed(os.time())
local function read_rollings(file_path)
local used = {}
local file = io.open(file_path, "r")
if file then
for line in file:lines() do
table.insert(used, line)
end
file:close()
end
return used
end
local function write_rollings(file_path, img_path)
local file = io.open(file_path, "a")
if file then
file:write(img_path .. "\n")
file:close()
end
end
local function get_random_image(folder, used)
local all_files = {}
for file in io.popen('dir "'..folder..'" /b /a-d'):lines() do
table.insert(all_files, file)
end
local available = {}
for _, file in ipairs(all_files) do
local exists = false
for _, used_file in ipairs(used) do
if file == used_file then
exists = true
break
end
end
if not exists then
table.insert(available, file)
end
end
if #available == 0 then
-- Reset rollings
os.execute('del "'..folder..'\\rollings.txt"')
available = all_files
end
if #available == 0 then return nil end
local idx = math.random(#available)
return available[idx]
end
local wallpaper_dir = "E:\\wallpaper"
local temp_dir = os.getenv("TEMP")
local rollings_file = temp_dir .. "\\rollings.txt"
while true do
local used = read_rollings(rollings_file)
local img = get_random_image(wallpaper_dir, used)
if img then
-- Set wallpaper using external tool
os.execute('Rundll32.exe user32.dll,UpdatePerUserSystemParameters')
-- Record the used image
write_rollings(rollings_file, img)
print("Set wallpaper: " .. img)
end
os.execute("timeout /t 60")
end
此Lua腳本通過調用外部命令來更換桌布,並管理已播放的圖片。具體步驟包括:
rollings.txt
文件,得到已播放的圖片列表。E:\wallpaper
目錄下所有圖片,並隨機選擇未播放過的圖片。rollings.txt
中。批次檔雖然功能相對簡單,但可以通過調用其他工具或腳本來實現桌布輪換的需求。以下是一個基本的批次檔範例:
@echo off
setlocal enabledelayedexpansion
set "wallpaperDir=E:\wallpaper"
set "recordFile=%TEMP%\rollings.txt"
:loop
if exist "%recordFile%" (
for /f "tokens=*" %%a in (%recordFile%) do (
set "played=%%a !played!"
)
)
set "allFiles="
for /f "delims=" %%a in ('dir /b /a-d "%wallpaperDir%"') do (
set "allFiles=!allFiles! %%a"
)
set "unplayed="
for %%a in (!allFiles!) do (
set "found=false"
for %%b in (!played!) do (
if "%%a"=="%%b" set "found=true"
)
if "!found!"=="false" set "unplayed=!unplayed! %%a"
)
if "!unplayed!"=="" (
del "%recordFile%"
set "unplayed=!allFiles!"
)
for %%a in (!unplayed!) do (
set "selected=%%a"
goto setwallpaper
)
:setwallpaper
echo Set wallpaper: %selected%
:: 使用外部程式設置桌布,例如使用 PowerShell
powershell.exe -Command "Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class Wallpaper { [DllImport(\"user32.dll\", SetLastError=true)] public static extern bool SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); }'; [Wallpaper]::SystemParametersInfo(20, 0, '%wallpaperDir%\%selected%', 3)"
echo %selected% >> "%recordFile%"
timeout /t 60 >nul
goto loop
此批次檔通過以下步驟實現桌布輪換:
rollings.txt
,獲取已播放的圖片列表。rollings.txt
中。功能 | C++ 實現 | Lua 實現 | BAT 實現 |
---|---|---|---|
雙螢幕支持 | 原生支持,可直接控制多螢幕 | 需調用外部工具或API | 有限,通常依賴外部命令 |
圖片管理 | 高效,支持大規模圖片 | 中等,依賴外部腳本 | 基礎,適合簡單需求 |
自適應縮放 | 使用GDI+實現精確調整 | 依賴外部工具處理 | 基本,通常無法精確控制 |
顯示檔名 | 可在圖片上覆蓋或創建透明視窗 | 需使用外部工具或腳本 | 難以實現,需額外工具 |
可擴展性 | 高,易於添加更多功能 | 中等,受限於語言特性 | 低,功能擴展受限 |
在雙螢幕環境中,確保每個螢幕設置不同的桌布是關鍵。C++實現可以利用IDesktopWallpaper
API實現精確設置,而Lua和BAT則需借助外部工具或腳本來達成。
為確保圖片在不同解析度下不失真,建議使用高品質縮放算法,如雙線性插值或立方插值,以提高圖片縮放的清晰度。
在桌布上顯示檔名時,可以考慮創建一個透明的頂層視窗,使用Windows API繪製文字,或將文字直接嵌入到圖像中。
針對大量圖片的處理,需確保程式具備高效的文件讀取和寫入能力,並在處理異常時有健全的錯誤處理機制,以避免程式崩潰或出現卡頓現象。
通過上述C++、Lua及BAT腳本的實現方式,可以滿足多螢幕、自適應圖片、高效管理及自動化更換桌布的需求。C++提供了最高的性能和靈活性,適合需要精細控制的用戶;Lua腳本則展現了更高的可編程性,適合與其他系統工具結合;而BAT腳本則適合簡單需求的快速部署。根據具體需求和開發環境的不同,用戶可以選擇最合適的實現方式,並根據進階功能進行優化和擴展。