Skip to content

Commit 6fb8d9a

Browse files
committed
fix: use spawn instead of utilityProcess.fork() on Windows
utilityProcess.fork() causes "listen UNKNOWN" errors on Windows. Use spawn() with proper env/stdio handling instead. Also fix killPortProcess to use PowerShell on Windows.
1 parent 4d09268 commit 6fb8d9a

File tree

1 file changed

+67
-29
lines changed

1 file changed

+67
-29
lines changed

apps/desktop/electron/process-manager.ts

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,26 @@ export class ProcessManager {
5656
/** 杀掉占用指定端口的进程 */
5757
private killPortProcess(port: number): void {
5858
try {
59-
const result = execSync(`lsof -ti tcp:${port}`, { encoding: 'utf-8' }).trim();
59+
let cmd: string;
60+
if (process.platform === 'win32') {
61+
cmd = `powershell -Command "Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { $_.OwningProcess }"`;
62+
} else {
63+
cmd = `lsof -ti tcp:${port}`;
64+
}
65+
const result = execSync(cmd, { encoding: 'utf-8' }).trim();
6066
if (result) {
6167
for (const pid of result.split('\n')) {
62-
try {
63-
process.kill(parseInt(pid), 'SIGKILL');
64-
this.logger.info(`Killed stale process ${pid} on port ${port}`);
65-
} catch { /* already dead */ }
68+
const p = parseInt(pid.trim());
69+
if (p > 0) {
70+
try {
71+
if (process.platform === 'win32') {
72+
execSync(`taskkill /PID ${p} /F`, { encoding: 'utf-8' });
73+
} else {
74+
process.kill(p, 'SIGKILL');
75+
}
76+
this.logger.info(`Killed stale process ${p} on port ${port}`);
77+
} catch { /* already dead */ }
78+
}
6679
}
6780
}
6881
} catch { /* no process on port */ }
@@ -172,30 +185,55 @@ export class ProcessManager {
172185
};
173186

174187
if (usePacked) {
175-
// utilityProcess 不会在 Dock 多出图标
176-
this.logger.info(`Starting frontend (utilityProcess): ${serverJs}`);
177-
178-
const up = utilityProcess.fork(serverJs, [], {
179-
cwd: nextDir,
180-
env,
181-
stdio: 'pipe',
182-
});
183-
184-
up.stdout?.on('data', (data: Buffer) => {
185-
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
186-
});
187-
188-
up.stderr?.on('data', (data: Buffer) => {
189-
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
190-
});
191-
192-
up.on('exit', (code) => {
193-
if (code !== 0) {
194-
this.logger.warn(`Frontend exited with code ${code}`);
195-
}
196-
});
197-
198-
this.frontendProcess = up as unknown as ChildProcess;
188+
// macOS: utilityProcess 不在 Dock 显示图标; Windows: spawn 更稳定
189+
if (process.platform === 'win32') {
190+
this.logger.info(`Starting frontend (spawn): node ${serverJs}`);
191+
const procEnv = {
192+
...process.env,
193+
...env,
194+
NODE_PATH: path.join(nextDir, 'node_modules'),
195+
};
196+
this.frontendProcess = spawn('node', [serverJs], {
197+
cwd: nextDir,
198+
env: procEnv,
199+
stdio: ['ignore', 'pipe', 'pipe'],
200+
detached: false,
201+
shell: false,
202+
});
203+
this.frontendProcess.stdout?.on('data', (data) => {
204+
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
205+
});
206+
this.frontendProcess.stderr?.on('data', (data) => {
207+
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
208+
});
209+
this.frontendProcess.on('error', (error) => {
210+
this.logger.error('Frontend process error', error);
211+
});
212+
this.frontendProcess.on('exit', (code) => {
213+
if (code !== 0) {
214+
this.logger.warn(`Frontend exited with code ${code}`);
215+
}
216+
});
217+
} else {
218+
this.logger.info(`Starting frontend (utilityProcess): ${serverJs}`);
219+
const up = utilityProcess.fork(serverJs, [], {
220+
cwd: nextDir,
221+
env,
222+
stdio: 'pipe',
223+
});
224+
up.stdout?.on('data', (data: Buffer) => {
225+
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
226+
});
227+
up.stderr?.on('data', (data: Buffer) => {
228+
this.logger.debug(`[Frontend] ${data.toString().trim()}`);
229+
});
230+
up.on('exit', (code) => {
231+
if (code !== 0) {
232+
this.logger.warn(`Frontend exited with code ${code}`);
233+
}
234+
});
235+
this.frontendProcess = up as unknown as ChildProcess;
236+
}
199237
} else {
200238
// 开发模式:用 next start
201239
this.logger.info('Starting frontend (dev mode): next start');

0 commit comments

Comments
 (0)