nodejs 在windows10中設置動態(視頻)壁紙


node版本

λ node -v
v12.16.2

main.js

const ffi = require("@saleae/ffi");
const child_process = require("child_process");
const W32 = require("./w32");

const argv = process.argv.slice(2);

if (!argv || !argv.length) process.exit(1);

const play = child_process.fork("./play.js");

// ffplay -noborder -loop 0 -fs -vf scale=w=1920:h=-1 "${argv[0]}"
// -noborder 無邊框
// -loop 0 循環次數,0無限循環
// -vf scale=w=1920:h=-1 使用scale濾鏡, See also: https://trac.ffmpeg.org/wiki/Scaling
// ffplay -noborder -x 1920 -y 1080  無邊框,強制設置寬高
play.send(`ffplay -noborder -x 1920 -y 1080 -loop 0 "${argv[0]}" `);
play.on("message", playCallback);

function playCallback() {
  let ffplayw = 0; // ffplay句柄
  let t;
  t = setInterval(() => {
    ffplayw = getFFplayHandle();
    if (ffplayw !== 0) {
      clearInterval(t);
      setDynamicWallpaper(ffplayw);
    }
  }, 1000);
}

function setDynamicWallpaper(ffplayw) {
  const progman = W32.FindWindowW(TEXT("Progman"), null);

  // 要觸發在桌面圖標和牆紙之間創建WorkerW窗口,我們必須向程序管理器發送一條消息。
  // 該消息是未記錄的消息,因此沒有專用的Windows API名稱,除了0x052C
  W32.SendMessageTimeoutW(
    progman,
    0x052c, // 在程序管理器上生成牆紙工作程序的未記錄消息
    0,
    0,
    0x0000,
    1000,
    0
  );

  // 我們枚舉所有Windows
  W32.EnumWindows(
    ffi.Callback("bool", ["int32", "int32"], (tophandle, topparamhandle) => {
      // 找到一個具有SHELLDLL_DefView的Windows
      const SHELLDLL_DefView = W32.FindWindowExW(
        tophandle,
        0,
        TEXT("SHELLDLL_DefView"),
        0
      );
      if (SHELLDLL_DefView !== 0) {
        // 將其下一個同級分配給workerw。
        const workerw = W32.FindWindowExW(0, tophandle, TEXT("WorkerW"), 0);
        W32.SetParent(ffplayw, workerw);
      }
      return true;
    }),
    0
  );
}
function TEXT(text) {
  return Buffer.from(`${text}\0`, "ucs2");
}

// 獲取ffplay句柄
function getFFplayHandle() {
  return W32.FindWindowW(TEXT("SDL_app"), null);
}

play.js

const child_process = require("child_process");

process.on("message", (runFFplayCommand) => {
  process.send(true);
  child_process.execSync(runFFplayCommand);
});

w32.js

const ffi = require("@saleae/ffi");

// Import user32
const W32 = new ffi.Library("user32", {
  // 檢索頂級窗口的句柄,該頂級窗口的類名和窗口名與指定的字符串匹配。此功能不搜索子窗口。此功能不執行區分大小寫的搜索。
  FindWindowW: ["int32", ["string", "string"]],

  // 將指定的消息發送到一個或多個窗口
  SendMessageTimeoutW: [
    "int32",
    ["int32", "int32", "int32", "int32", "int32", "int32", "int32"],
  ],

  // 通過將句柄傳遞給每個窗口,依次傳遞到應用程序定義的回調函數,可以枚舉屏幕上所有的頂級窗口
  EnumWindows: ["bool", ["pointer", "int32"]],

  // 檢索其類名和窗口名與指定字符串匹配的窗口的句柄。該功能搜索子窗口,從指定子窗口之后的子窗口開始。此功能不執行區分大小寫的搜索。
  FindWindowExW: ["int32", ["int32", "int32", "string", "int32"]],

  // 更改指定子窗口的父窗口。
  // HWND SetParent(HWND hWndChild, HWND hWndNewParent);
  SetParent: ["int32", ["int32", "int32"]],

  // int MessageBox(
  //   HWND    hWnd, 要創建的消息框的所有者窗口的句柄。如果此參數為NULL,則消息框沒有所有者窗口
  //   LPCTSTR lpText, 要顯示的消息
  //   LPCTSTR lpCaption, 對話框標題
  //   UINT    uType 對話框的內容和行為
  // );
  MessageBoxW: ["int32", ["int32", "string", "string", "int32"]],

  // 最小化(但不破壞)指定的窗口。
  CloseWindow: ["bool", ["int32"]],

  // 銷毀指定的窗口
  DestroyWindow: ["bool", ["int32"]],

  // 打開指定的桌面對象
  OpenDesktopW: ["int32", ["string", "int32", "bool", "int32"]],

  // 確定指定窗口的可見性狀態。
  IsWindowVisible: ["bool", ["int32"]],

  // 設置指定窗口的顯示狀態。
  ShowWindow: ["bool", ["int32", "int32"]],
});

module.exports = W32;

運行

>node main.js "D:\dynamic wallpaper\Nightcore-We-Wish-You-A-Merry-Christmas-Live-Wallpaper.mp4"

運行前的窗口:

運行后的窗口:

將視頻放在Progman下面

現在測試來看,這種方法要好些,關閉進程后不會顯示殘留壁紙

function setDynamicWallpaper(ffplayw) {
  const progman = W32.FindWindowW(TEXT("Progman"), null);

  // 要觸發在桌面圖標和牆紙之間創建WorkerW窗口,我們必須向程序管理器發送一條消息。
  // 該消息是未記錄的消息,因此沒有專用的Windows API名稱,除了0x052C
  W32.SendMessageTimeoutW(
    progman,
    0x052c, // 在程序管理器上生成牆紙工作程序的未記錄消息
    0,
    0,
    0x0000,
    1000,
    0
  );

  // 我們枚舉所有Windows
  W32.EnumWindows(
    ffi.Callback("bool", ["int32", "int32"], (tophandle, topparamhandle) => {
      // 找到一個具有SHELLDLL_DefView的Windows
      const SHELLDLL_DefView = W32.FindWindowExW(
        tophandle,
        0,
        TEXT("SHELLDLL_DefView"),
        0
      );
      if (SHELLDLL_DefView !== 0) {
        // 將其下一個同級分配給workerw。
        const workerw = W32.FindWindowExW(0, tophandle, TEXT("WorkerW"), 0);
        const isVisible = W32.IsWindowVisible(workerw);
        if (isVisible) {
          // 設置窗口為未激活狀態,否則這個窗口會遮擋視頻
          W32.ShowWindow(workerw, 0);
        }
        W32.SetParent(ffplayw, progman);
      }
      return true;
    }),
    0
  );
}

現在按下Ctrl+c指令后,node進程結束,ffplay關閉,壁紙將顯示之前的壁紙

使用ffplay你可以解析本地視頻,圖片,http視頻,直播流,ts視頻段,m3u8,等等...

參考連接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM