koffi 枚举所有屏幕并显示坐标
koffi EnumDisplayMonitors
GitHub 代码:https://github.com/xieerduos/koffi-example/blob/main/EnumDisplayMonitors.js
通过 user32 EnumDisplayMonitors
EnumDisplayMonitors.js
js
// EnumDisplayMonitors
// 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
const koffi = require("koffi");
// 加载 user32.dll
const user32 = koffi.load("user32.dll");
// 定义数据类型
const HANDLE = koffi.pointer("HANDLE", koffi.opaque());
const HDC = koffi.pointer("HDC", koffi.opaque());
const RECT = koffi.struct("RECT", {
left: "long",
top: "long",
right: "long",
bottom: "long",
});
const LPARAM = koffi.alias("LPARAM", "long");
// 定义 MONITORENUMPROC 回调函数的签名
const MonitorEnumProc = koffi.proto(
// "bool __stdcall MonitorEnumProc(HANDLE HMONITOR, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
"bool __stdcall MonitorEnumProc(int32, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
);
// 声明 EnumWindows 函数,这是 user32.dll 中的一个函数,用于枚举所有顶层窗口
const EnumDisplayMonitors = user32.func(
"__stdcall",
"EnumDisplayMonitors",
"bool",
[
HANDLE,
HANDLE,
koffi.pointer(MonitorEnumProc), // 回调函数的指针
LPARAM, // 附加参数,传递给回调函数
]
);
// const GetMonitorInfoW = user32.func(
// "bool __stdcall GetMonitorInfoW(int32, HANDLE lpmi)"
// );
// 定义 MONITORINFO 结构
const MONITORINFO = Buffer.alloc(40);
MONITORINFO.writeInt32LE(40, 0); // cbSize
// 枚举窗口的回调函数
const cb1 = koffi.register((hMonitor, hdcMonitor, lprcMonitor, dwData) => {
const rect = koffi.decode(lprcMonitor, RECT);
console.log("rect1", convertToXYWH({ rect: rect }));
// if (GetMonitorInfoW(hMonitor, MONITORINFO)) {
// const left = MONITORINFO.readInt32LE(4);
// const top = MONITORINFO.readInt32LE(8);
// const right = MONITORINFO.readInt32LE(12);
// const bottom = MONITORINFO.readInt32LE(16);
// const rect = convertToXYWH({ rect: { left, top, right, bottom } });
// console.log("rect2", rect);
// }
console.log("Monitor handle:", hMonitor);
console.log("Monitor lprcMonitor:", lprcMonitor);
return true; // 返回 true 继续枚举,返回 false 停止枚举
}, koffi.pointer(MonitorEnumProc)); // 传递回调签名
console.time("EnumDisplayMonitors");
EnumDisplayMonitors(0, 0, cb1, 0);
koffi.unregister(cb1);
console.timeEnd("EnumDisplayMonitors");
function convertToXYWH({ rect, className }) {
const newItem = {
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
className,
};
if (!className) {
delete newItem.className;
}
return newItem;
}
代码输出结果,我有两个屏幕,所以输出了两个
powershell
PS C:\Users\Administrator\Desktop\koffi-example> node .\EnumDisplayMonitors.js
rect1 { x: 0, y: 0, width: 2048, height: 1152 }
Monitor handle: 131073
Monitor lprcMonitor: [External: 229b726ffb8]
rect1 { x: 2560, y: 0, width: 1920, height: 1080 }
Monitor handle: 65592
Monitor lprcMonitor: [External: 229b726ffb8]
EnumDisplayMonitors: 2.917ms
PS C:\Users\Administrator\Desktop\koffi-example>
通过 user32 GetMonitorInfoW 根据句柄 id 获取屏幕信息
- EnumDisplayMonitors 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
- GetMonitorInfoW 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfow
js
// EnumDisplayMonitors 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
// GetMonitorInfoW 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfow
const koffi = require("koffi");
// 加载 user32.dll
const user32 = koffi.load("user32.dll");
// 定义数据类型
const HANDLE = koffi.pointer("HANDLE", koffi.opaque());
const HDC = koffi.pointer("HDC", koffi.opaque());
const RECT = koffi.struct("RECT", {
left: "long",
top: "long",
right: "long",
bottom: "long",
});
const LPARAM = koffi.alias("LPARAM", "long");
// 定义 MONITORENUMPROC 回调函数的签名
const MonitorEnumProc = koffi.proto(
// "bool __stdcall MonitorEnumProc(HANDLE HMONITOR, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
"bool __stdcall MonitorEnumProc(int32, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
);
// 声明 EnumWindows 函数,这是 user32.dll 中的一个函数,用于枚举所有顶层窗口
const EnumDisplayMonitors = user32.func(
"__stdcall",
"EnumDisplayMonitors",
"bool",
[
HANDLE,
HANDLE,
koffi.pointer(MonitorEnumProc), // 回调函数的指针
LPARAM, // 附加参数,传递给回调函数
]
);
const GetMonitorInfoW = user32.func(
"bool __stdcall GetMonitorInfoW(int32, HANDLE lpmi)"
);
// 定义 MONITORINFO 结构
const MONITORINFO = Buffer.alloc(40);
MONITORINFO.writeInt32LE(40, 0); // cbSize
// 枚举窗口的回调函数
const cb1 = koffi.register((hMonitor, hdcMonitor, lprcMonitor, dwData) => {
// const rect = koffi.decode(lprcMonitor, RECT);
// console.log("rect1", convertToXYWH({ rect: rect }));
if (GetMonitorInfoW(hMonitor, MONITORINFO)) {
const left = MONITORINFO.readInt32LE(4);
const top = MONITORINFO.readInt32LE(8);
const right = MONITORINFO.readInt32LE(12);
const bottom = MONITORINFO.readInt32LE(16);
const rect = convertToXYWH({ rect: { left, top, right, bottom } });
console.log("rect2", rect);
}
console.log("Monitor handle:", hMonitor);
console.log("Monitor lprcMonitor:", lprcMonitor);
return true; // 返回 true 继续枚举,返回 false 停止枚举
}, koffi.pointer(MonitorEnumProc)); // 传递回调签名
console.time("EnumDisplayMonitors");
EnumDisplayMonitors(0, 0, cb1, 0);
koffi.unregister(cb1);
console.timeEnd("EnumDisplayMonitors");
function convertToXYWH({ rect, className }) {
const newItem = {
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
className,
};
if (!className) {
delete newItem.className;
}
return newItem;
}
输出结果
powershell
PS C:\Users\Administrator\Desktop\koffi-example> node EnumDisplayMonitors.js
rect2 { x: 0, y: 0, width: 2048, height: 1152 }
Monitor handle: 131073
Monitor lprcMonitor: [External: 1bda348ffb8]
rect2 { x: 2560, y: 0, width: 1920, height: 1080 }
Monitor handle: 65592
Monitor lprcMonitor: [External: 1bda348ffb8]
EnumDisplayMonitors: 3.291ms
PS C:\Users\Administrator\Desktop\koffi-example>
综合例子
增加判断是否为主显示器、获取工作区
js
// EnumDisplayMonitors
// 官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
const koffi = require("koffi");
// 加载 user32.dll
const user32 = koffi.load("user32.dll");
// 定义数据类型
const HANDLE = koffi.pointer("HANDLE", koffi.opaque());
const HDC = koffi.pointer("HDC", koffi.opaque());
const RECT = koffi.struct("RECT", {
left: "long",
top: "long",
right: "long",
bottom: "long",
});
const LPARAM = koffi.alias("LPARAM", "long");
// 定义 MONITORINFO 结构
const MONITORINFO = Buffer.alloc(40);
MONITORINFO.writeInt32LE(40, 0); // cbSize
const MONITORINFOF_PRIMARY = 0x00000001;
// 定义 MONITORENUMPROC 回调函数的签名
const MonitorEnumProc = koffi.proto(
// "bool __stdcall MonitorEnumProc(HANDLE HMONITOR, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
"bool __stdcall MonitorEnumProc(int32, HDC hdcMonitor, RECT* lprcMonitor, LPARAM dwData)"
);
// 声明 EnumWindows 函数,这是 user32.dll 中的一个函数,用于枚举所有顶层窗口
const EnumDisplayMonitors = user32.func(
"__stdcall",
"EnumDisplayMonitors",
"bool",
[
HANDLE,
HANDLE,
koffi.pointer(MonitorEnumProc), // 回调函数的指针
LPARAM, // 附加参数,传递给回调函数
]
);
const GetMonitorInfoW = user32.func(
"bool __stdcall GetMonitorInfoW(int32, HANDLE lpmi)"
);
const monitors = [];
// 枚举窗口的回调函数
const cb1 = koffi.register((hMonitor, hdcMonitor, lprcMonitor, dwData) => {
// const rect = koffi.decode(lprcMonitor, RECT);
// console.log("rect1", convertToXYWH({ rect: rect }));
if (GetMonitorInfoW(hMonitor, MONITORINFO)) {
const left = MONITORINFO.readInt32LE(4);
const top = MONITORINFO.readInt32LE(8);
const right = MONITORINFO.readInt32LE(12);
const bottom = MONITORINFO.readInt32LE(16);
const rect = convertToXYWH({ rect: { left, top, right, bottom } });
console.log("rect2", rect);
// 判断是否为主显示器
// #region isPrimaryMonitor
const dwFlags = MONITORINFO.readInt32LE(36);
const isPrimaryMonitor = (dwFlags & MONITORINFOF_PRIMARY) !== 0;
console.log("isPrimaryMonitor", isPrimaryMonitor);
// #endregion isPrimaryMonitor
// 获取工作区
// #region workArea
const workArea = {};
workArea.left = MONITORINFO.readInt32LE(20);
workArea.top = MONITORINFO.readInt32LE(24);
workArea.right = MONITORINFO.readInt32LE(28);
workArea.bottom = MONITORINFO.readInt32LE(32);
console.log("workArea", convertToXYWH({ rect: workArea }));
// #endregion workArea
monitors.push({
...rect,
isPrimaryMonitor,
workArea,
});
}
console.log("Monitor handle:", hMonitor);
console.log("Monitor lprcMonitor:", lprcMonitor);
return true; // 返回 true 继续枚举,返回 false 停止枚举
}, koffi.pointer(MonitorEnumProc)); // 传递回调签名
function convertToXYWH({ rect, className }) {
const newItem = {
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
className,
};
if (!className) {
delete newItem.className;
}
return newItem;
}
console.time("EnumDisplayMonitors");
EnumDisplayMonitors(0, 0, cb1, 0);
koffi.unregister(cb1);
console.timeEnd("EnumDisplayMonitors");
console.log("monitors", monitors);
运行结果
powershell
PS C:\Users\Administrator\Desktop\koffi-example> node EnumDisplayMonitors.js
rect2 { x: 0, y: 0, width: 2048, height: 1152 }
isPrimaryMonitor true
workArea { x: 0, y: 0, width: 2048, height: 1104 }
Monitor handle: 131073
Monitor lprcMonitor: [External: 2b6cfa9ffb8]
rect2 { x: 2560, y: 0, width: 1920, height: 1080 }
isPrimaryMonitor false
workArea { x: 2560, y: 0, width: 1920, height: 1032 }
Monitor handle: 65592
Monitor lprcMonitor: [External: 2b6cfa9ffb8]
EnumDisplayMonitors: 3.43ms
monitors [
{
x: 0,
y: 0,
width: 2048,
height: 1152,
isPrimaryMonitor: true,
workArea: { left: 0, top: 0, right: 2048, bottom: 1104 }
},
{
x: 2560,
y: 0,
width: 1920,
height: 1080,
isPrimaryMonitor: false,
workArea: { left: 2560, top: 0, right: 4480, bottom: 1032 }
}
]
PS C:\Users\Administrator\Desktop\koffi-example>