在工作上遇到nodejs呼叫執行檔執行失敗問題,最後發現是由於nodejs專案本身有用nssm包成服務,在服務環境的nodejs呼叫的執行檔也執行在服務中,造成程式不會跳出視窗而導致失敗。將程式用nssm包成服務詳細操作可以看這篇。
原因是在 Windows Vista / Windows Server 2008 及之後的版本,為了安全性,系統服務預設執行在一個獨立的、非互動的 Session 0 中。而使用者登入後所看到的桌面和視窗是執行在其他 Session (例如 Session 1, Session 2...)。Session 0 中的程序無法直接與使用者的桌面互動,也就是說,它無法顯示圖形化使用者介面 (GUI) 或接收使用者的滑鼠、鍵盤輸入。
簡單範例
正常環境呼叫開啟計算器:

在nssm服務環境中呼叫,無法看到計算器:

透過psexec將執行檔執行到指定session,讓服務內程式能開啟並顯示powershell視窗

準備psExec工具:
- 下載鏈接
- 下載完成後解壓到喜歡的資料夾
獲得session id:

上面下載鏈接頁面的下方有這個工具的參數說明,其中需要我們提供-i參數,這就是我們上面提到的Session id參數;下面我們在nodejs中,通過呼叫window的powershell,獲得圖形界面進程explorer.exe所在的Session id:
const { exec } = require("child_process");
const { promisify } = require("util");
const execPromise = promisify(exec);
/**
* 異步獲取當前活動的互動式 Session ID (使用 PowerShell Get-Process 和 Base64 編碼)
* @returns {Promise<number>} 解析成功則返回 Session ID
*/
const getActiveSessionIdByPs = async () => {
const psCommandOriginal = `
$foundSessionId = $null
try {
$explorer = Get-Process -Name explorer -ErrorAction SilentlyContinue | Select-Object -First 1;
if ($explorer) {
$foundSessionId = $explorer.SessionId;
} else {
$interactiveProcess = Get-Process | Where-Object { $_.SessionId -ne 0 -and $_.SessionId -ne 65536 } | Select-Object -First 1;
if ($interactiveProcess) {
$foundSessionId = $interactiveProcess.SessionId;
} else {
Write-Error "No interactive session process found.";
}
}
} catch {
Write-Error "Error querying processes: $($_.Exception.Message)";
}
Write-Output $foundSessionId
`;
const psScriptBase64 = Buffer.from(psCommandOriginal, "utf16le").toString(
"base64"
);
const command = `powershell.exe -NoProfile -EncodedCommand ${psScriptBase64}`;
try {
const { stdout, stderr } = await execPromise(command);
const outputTrimmed = stdout.trim();
if (outputTrimmed === "")
throw new Error("PowerShell script did not find an active session ID.");
const activeSessionId = parseInt(outputTrimmed, 10);
if (isNaN(activeSessionId))
throw new Error(
`PowerShell script returned non-numeric output: "${outputTrimmed}"`
);
sessionId = activeSessionId;
return sessionId;
} catch (error) {
console.error("Error executing PowerShell command:", error.message);
if (error.stderr) console.error("Stderr:", error.stderr.trim());
if (error.stdout) console.error("Stdout:", error.stdout.trim());
throw new Error(
`Failed to get active session ID via PowerShell (Base64): ${error.message}`
);
}
};
(async () => {
const sessionId = await getActiveSessionIdByPs();
console.log(sessionId);
})();
透過psexec開啟在指定session開啟程式:
最後在nodejs中用以下指令呼叫psexec,就能在服務中開啟powershell並看到窗口:
await execPromise(`${psexecPath} -accepteula -nobanner -i ${await getActiveSessionIdByPs()} -s powershell.exe`)
提示:要在服務中才能使用-i參數,否則會報錯
