Skip to content

Commit a6a78f7

Browse files
committed
feat(window-recognition): native 扩展窗口几何/置顶/前台HWND枚举
1 parent 66808c0 commit a6a78f7

4 files changed

Lines changed: 49 additions & 0 deletions

File tree

‎client/electron/audio/processAudioNative.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ export interface NativeWindowInfo {
2020
title: string;
2121
processName: string;
2222
rootProcessName: string;
23+
/** 窗口矩形(像素,native 扩展字段) */
24+
left: number;
25+
top: number;
26+
width: number;
27+
height: number;
28+
/** 置顶窗口 */
29+
alwaysOnTop: boolean;
2330
}
2431

2532
/** 原生模块流式回调的负载:音频块或错误 */
@@ -35,6 +42,8 @@ export interface NativeStreamPayload {
3542
export interface ProcessAudioNative {
3643
listAudioWindows(): NativeWindowInfo[];
3744
resolveRootPid(pid: number): number;
45+
/** 当前前台窗口 hwnd 字符串;无前台窗口返回空串 */
46+
getForegroundHwnd(): string;
3847
isProcessLoopbackSupported(): boolean;
3948
startCapture(
4049
options: { pid: number; sampleRate: number; channels: number; chunkDurationMs: number },

‎client/native/process-audio/src/addon.cc‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Napi::Value ListAudioWindows(const Napi::CallbackInfo& info) {
3939
obj.Set("title", ToNapiString(env, w.title));
4040
obj.Set("processName", ToNapiString(env, w.process_name));
4141
obj.Set("rootProcessName", ToNapiString(env, w.root_process_name));
42+
obj.Set("left", Napi::Number::New(env, w.left));
43+
obj.Set("top", Napi::Number::New(env, w.top));
44+
obj.Set("width", Napi::Number::New(env, w.width));
45+
obj.Set("height", Napi::Number::New(env, w.height));
46+
obj.Set("alwaysOnTop", Napi::Boolean::New(env, w.always_on_top));
4247
arr.Set(i, obj);
4348
}
4449
return arr;
@@ -116,6 +121,13 @@ Napi::Value IsSupported(const Napi::CallbackInfo& info) {
116121
return Napi::Boolean::New(info.Env(), process_audio::IsProcessLoopbackSupported());
117122
}
118123

124+
/** getForegroundHwnd(): 返回当前前台窗口 hwnd 字符串;无则返回空串 */
125+
Napi::Value GetForegroundHwnd(const Napi::CallbackInfo& info) {
126+
const uint64_t hwnd = process_audio::GetForegroundWindowHwnd();
127+
if (hwnd == 0) return Napi::String::New(info.Env(), "");
128+
return Napi::String::New(info.Env(), std::to_string(hwnd));
129+
}
130+
119131
// ================================================================
120132
// 流式采集(生产入口)
121133
// ================================================================
@@ -239,6 +251,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
239251
exports.Set("captureToWav", Napi::Function::New(env, CaptureToWav));
240252
exports.Set("isProcessLoopbackSupported",
241253
Napi::Function::New(env, IsSupported));
254+
exports.Set("getForegroundHwnd", Napi::Function::New(env, GetForegroundHwnd));
242255
exports.Set("startCapture", Napi::Function::New(env, StartCapture));
243256
exports.Set("stopCapture", Napi::Function::New(env, StopCapture));
244257
return exports;

‎client/native/process-audio/src/window_finder.cc‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lparam) {
106106
info.title = title;
107107
info.process_name = GetProcessImageName(pid);
108108
info.root_process_name = GetProcessImageName(info.root_pid);
109+
110+
RECT rect = {0, 0, 0, 0};
111+
if (GetWindowRect(hwnd, &rect) != 0) {
112+
info.left = rect.left;
113+
info.top = rect.top;
114+
info.width = rect.right - rect.left;
115+
info.height = rect.bottom - rect.top;
116+
}
117+
info.always_on_top = (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
118+
109119
ctx->out->push_back(std::move(info));
110120
return TRUE;
111121
}
@@ -125,4 +135,10 @@ uint32_t ResolveRootPidForPid(uint32_t pid) {
125135
return ResolveRootPid(static_cast<DWORD>(pid), parents);
126136
}
127137

138+
uint64_t GetForegroundWindowHwnd() {
139+
HWND hwnd = ::GetForegroundWindow();
140+
if (hwnd == nullptr) return 0;
141+
return reinterpret_cast<uint64_t>(hwnd);
142+
}
143+
128144
} // namespace process_audio

‎client/native/process-audio/src/window_finder.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ struct WindowInfo {
2222
std::wstring title;
2323
std::wstring process_name;
2424
std::wstring root_process_name;
25+
26+
/** 窗口矩形(像素,GetWindowRect) */
27+
int32_t left = 0;
28+
int32_t top = 0;
29+
int32_t width = 0;
30+
int32_t height = 0;
31+
/** 置顶窗口(WS_EX_TOPMOST) */
32+
bool always_on_top = false;
2533
};
2634

2735
/** 枚举所有可见且有标题的顶层窗口 */
@@ -30,6 +38,9 @@ std::vector<WindowInfo> ListAudioWindows();
3038
/** 单独解析某个 PID 的应用根进程 */
3139
uint32_t ResolveRootPidForPid(uint32_t pid);
3240

41+
/** 取当前前台窗口 HWND;无前台窗口时返回 0 */
42+
uint64_t GetForegroundWindowHwnd();
43+
3344
} // namespace process_audio
3445

3546
#endif // PROCESS_AUDIO_WINDOW_FINDER_H_

0 commit comments

Comments
 (0)