diff --git a/doc/api/cluster.md b/doc/api/cluster.md index 2c42c9e701adb4..0a3676cb26f71b 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -756,6 +756,8 @@ changes: This can be a number, or a function that takes no arguments and returns a number. By default each worker gets its own port, incremented from the master's `process.debugPort`. + * `windowsHide` {boolean} Hide the forked processes console window that would + normally be created on Windows systems. **Default:** `false` After calling `.setupMaster()` (or `.fork()`) this settings object will contain the settings, including the default values. diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 4836ede540a9cf..aa349f8e1afbd7 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -131,6 +131,7 @@ function createWorkerProcess(id, env) { return fork(cluster.settings.exec, cluster.settings.args, { env: workerEnv, silent: cluster.settings.silent, + windowsHide: cluster.settings.windowsHide, execArgv: execArgv, stdio: cluster.settings.stdio, gid: cluster.settings.gid, diff --git a/test/parallel/test-cluster-fork-windowsHide.js b/test/parallel/test-cluster-fork-windowsHide.js new file mode 100644 index 00000000000000..a7476983f9e39d --- /dev/null +++ b/test/parallel/test-cluster-fork-windowsHide.js @@ -0,0 +1,76 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const cluster = require('cluster'); + +if (!process.argv[2]) { + /* It seems Windows only allocate new console window for + * attaching processes spawned by detached processes. i.e. + * - If process D is spawned by process C with `detached: true`, + * and process W is spawned by process D with `detached: false`, + * W will get a new black console window popped up. + * - If D is spawned by C with `detached: false` or W is spawned + * by D with `detached: true`, no console window will pop up for W. + * + * So, we have to spawn a detached process first to run the actual test. + */ + const master = child_process.spawn( + process.argv[0], + [process.argv[1], '--cluster'], + { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] }); + + const messageHandlers = { + workerOnline: common.mustCall((msg) => { + }), + mainWindowHandle: common.mustCall((msg) => { + assert.ok(/0\s*/.test(msg.value)); + }), + workerExit: common.mustCall((msg) => { + assert.strictEqual(msg.code, 0); + assert.strictEqual(msg.signal, null); + }) + }; + + master.on('message', (msg) => { + const handler = messageHandlers[msg.type]; + assert.ok(handler); + handler(msg); + }); + + master.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + +} else if (cluster.isMaster) { + cluster.setupMaster({ + silient: true, + windowsHide: true + }); + + const worker = cluster.fork(); + worker.on('exit', (code, signal) => { + process.send({ type: 'workerExit', code: code, signal: signal }); + }); + + worker.on('online', (msg) => { + process.send({ type: 'workerOnline' }); + + let output = '0'; + if (process.platform === 'win32') { + output = child_process.execSync( + 'powershell -NoProfile -c ' + + `"(Get-Process -Id ${worker.process.pid}).MainWindowHandle"`, + { windowsHide: true, encoding: 'utf8' }); + } + + process.send({ type: 'mainWindowHandle', value: output }); + worker.send('shutdown'); + }); + +} else { + cluster.worker.on('message', (msg) => { + cluster.worker.disconnect(); + }); +}