Electron界面嵌入其他exe程序

Published on
44
import { app, BrowserWindow } from 'electron';
import path from 'path';
import { exec, spawn } from 'child_process';
import koffi from 'koffi';
import { endianness } from 'os';

const user32 = koffi.load('user32.dll');
const DWORD = koffi.alias('DWORD', 'uint32_t');
const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
const HWND = koffi.alias('HWND', HANDLE);
const BOOL = koffi.alias('BOOL', 'int');
const LONG = koffi.alias('LONG', 'int');

const SetParent = user32.func('__stdcall', 'SetParent', 'int', ['int', 'int']);
const MoveWindow = user32.func('__stdcall', 'MoveWindow', 'bool', ['int', 'int', 'int', 'int', 'int', 'bool']);
const SetWindowPos = user32.func('__stdcall', 'SetWindowPos', 'bool', ['int', 'int', 'int', 'int', 'int', 'int', 'int']);
const GetWindowThreadProcessId = user32.func('DWORD __stdcall GetWindowThreadProcessId(HWND hWnd, _Out_ DWORD *lpdwProcessId)');
const SetWindowLong = user32.func('LONG __stdcall SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong)');

const EnumWindowsProc = koffi.proto('bool __stdcall EnumWindowsProc(int, long lParam)');
const EnumWindows = user32.func('__stdcall', 'EnumWindows', 'bool', [koffi.pointer(EnumWindowsProc), 'int']);

async function GetHwndFromPid(window_pid, retry = 0) {
    const hwnds = [];
    const cb = koffi.register((hwnd, lParam) => {
        hwnds.push(hwnd);
        return true;
    }, koffi.pointer(EnumWindowsProc));

    EnumWindows(cb, 0);

    koffi.unregister(cb);

    const data = hwnds
        .map((hwnd) => {
            const buffer = Buffer.alloc(4);
            GetWindowThreadProcessId(hwnd, buffer);
            return {
                hwnd,
                pid: buffer.readInt32LE()
            };
        })
        .find((item) => item.pid === window_pid);

    if (data) {
        return data.hwnd;
    } else {
        if (retry < 100) {
            return await new Promise((resolve) => {
                setTimeout(() => {
                    resolve(GetHwndFromPid(window_pid, retry + 1));
                }, 100);
            });
        } else {
            return null;
        }
    }
}

async function StartProcess(child_window, main_hwnd) {
    const handler = endianness() == 'LE' ? main_hwnd.readInt32LE() : main_hwnd.readInt32BE();

    // --window-borderless
    // --fullscreen
    // --keyboard=uhid
    const child = spawn('F:\\Soft\\scrcpy-win64-v2.6.1\\scrcpy.exe', ['--fullscreen', '--video-codec=h265', '--video-bit-rate=16M', '--max-fps=60'], {
        windowsVerbatimArguments: true
    });

    console.log('child.pid:', child.pid);
    const hwnd = await GetHwndFromPid(child.pid);
    console.log('hwnd:', hwnd);

    if (hwnd) {
        SetParent(hwnd, handler);
        MoveWindow(hwnd, 0, 0, 300 - 10, 660 - 32 - 10, true);
        SetWindowPos(hwnd, -1, 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0040);

        child_window.on('resize', () => {
            const bounds = child_window.getBounds();
            MoveWindow(hwnd, 0, 0, bounds.width - 10, bounds.height - 32 - 10, true);

            // 如果窗口渲染了html,移动的时候需要重新设置一下,不然无法点击
            setTimeout(() => {
                SetParent(hwnd, handler);
            }, 1000);
        });
    } else {
        console.log('hwnd is null');
    }
}

function createWindow() {
    const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        center: true,
        backgroundColor: '#00FFFFFF'
    });

    mainWindow.loadURL(`${process.env.ELECTRON_RENDERER_URL}/index.html`);

    // StartProcess(mainWindow, mainWindow.getNativeWindowHandle());

    const child_window = new BrowserWindow({
        parent: mainWindow,
        modal: true,
        autoHideMenuBar: true,
        width: 300,
        height: 660,
        center: true,
        transparent: true,
        backgroundColor: '#00FFFFFF' //00FFFFFF
    });

    StartProcess(child_window, child_window.getNativeWindowHandle());
}

app.whenReady().then(() => {
    createWindow();

    app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
});

app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') app.quit();
});


Prev Post electron、nest的权限管理
Next Post :empty 选择器