From 45134fbc169b5c549ccd886fdd4101f618604920 Mon Sep 17 00:00:00 2001 From: Todd Wong Date: Sat, 2 Dec 2017 00:08:01 +0800 Subject: [PATCH 1/3] cluster: support windowsHide option for workers Fixes: https://github.com/nodejs/node/issues/17370 --- doc/api/cluster.md | 2 + lib/internal/cluster/master.js | 1 + .../parallel/test-cluster-fork-windowsHide.js | 77 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 test/parallel/test-cluster-fork-windowsHide.js diff --git a/doc/api/cluster.md b/doc/api/cluster.md index 7ab4d33dbd9ca8..016fdf6c185c68 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -722,6 +722,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 f3d2e30a5b73f0..408b31c2b77805 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -127,6 +127,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..eb6166858d4582 --- /dev/null +++ b/test/parallel/test-cluster-fork-windowsHide.js @@ -0,0 +1,77 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); + +let patchedFork = child_process.fork; +child_process.fork = (...args) => patchedFork.apply(this, args); +const cluster = require('cluster'); + +if (!process.argv[2]) { + const master = child_process.spawn( + process.argv[0], + [process.argv[1], '--cluster'], + { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] }); + + const messageHandlers = { + windowsHide: common.mustCall((msg) => { + assert.strictEqual(msg.value, true); + }), + 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) { + const originalFork = patchedFork; + patchedFork = common.mustCall((...args) => { + process.send({ type: 'windowsHide', value: args[2].windowsHide }); + return originalFork.apply(this, args); + }); + + 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(); + }); +} From e8d752ee121584b2d4d622fefcee82b823a55c8f Mon Sep 17 00:00:00 2001 From: Todd Wong Date: Sun, 3 Dec 2017 10:33:28 +0800 Subject: [PATCH 2/3] fixup! cluster: support windowsHide option for workers --- test/parallel/test-cluster-fork-windowsHide.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-cluster-fork-windowsHide.js b/test/parallel/test-cluster-fork-windowsHide.js index eb6166858d4582..175f7170849047 100644 --- a/test/parallel/test-cluster-fork-windowsHide.js +++ b/test/parallel/test-cluster-fork-windowsHide.js @@ -60,11 +60,12 @@ if (!process.argv[2]) { process.send({ type: 'workerOnline' }); let output = '0'; - if (process.platform === 'win32') + 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'); From cc4081a758c308fb1e5b2c245a9b23c39f2d3e56 Mon Sep 17 00:00:00 2001 From: Todd Wong Date: Sun, 10 Dec 2017 15:15:04 +0800 Subject: [PATCH 3/3] fixup! cluster: support windowsHide option for workers --- .../parallel/test-cluster-fork-windowsHide.js | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/parallel/test-cluster-fork-windowsHide.js b/test/parallel/test-cluster-fork-windowsHide.js index 175f7170849047..a7476983f9e39d 100644 --- a/test/parallel/test-cluster-fork-windowsHide.js +++ b/test/parallel/test-cluster-fork-windowsHide.js @@ -2,21 +2,25 @@ const common = require('../common'); const assert = require('assert'); const child_process = require('child_process'); - -let patchedFork = child_process.fork; -child_process.fork = (...args) => patchedFork.apply(this, args); 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 = { - windowsHide: common.mustCall((msg) => { - assert.strictEqual(msg.value, true); - }), workerOnline: common.mustCall((msg) => { }), mainWindowHandle: common.mustCall((msg) => { @@ -40,12 +44,6 @@ if (!process.argv[2]) { })); } else if (cluster.isMaster) { - const originalFork = patchedFork; - patchedFork = common.mustCall((...args) => { - process.send({ type: 'windowsHide', value: args[2].windowsHide }); - return originalFork.apply(this, args); - }); - cluster.setupMaster({ silient: true, windowsHide: true