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();
});