VirtualDesktop
VirtualDesktop copied to clipboard
This program always throws an error in programming
This project seems to always throw an error when used in programming.
This is my simple code in nodejs:
import { exec } from 'child_process';
import util from 'util';
const execAsync = util.promisify(exec);
const { stdout, stderr } = await execAsync(`VirtualDesktop11.exe`);
console.log('result:', stdout, stderr);
which produces the following error:
PS C:\Nabi> node .\test.mjs
node:internal/errors:983
const err = new Error(message);
^
Error: Command failed: VirtualDesktop11.exe
at genericNodeError (node:internal/errors:983:15)
at wrappedFn (node:internal/errors:537:14)
at ChildProcess.exithandler (node:child_process:414:12)
at ChildProcess.emit (node:events:518:28)
at maybeClose (node:internal/child_process:1101:16)
at ChildProcess._handle.onexit (node:internal/child_process:304:5) {
code: 4294967294,
killed: false,
signal: null,
cmd: 'VirtualDesktop11.exe',
stdout: 'VirtualDesktop.exe\t\t\t\tMarkus Scholtes, 2025, v1.20\n' +
'\r\n' +
'Command line tool to manage the virtual desktops of Windows 11.\r\n' +
'Parameters can be given as a sequence of commands. The result - most of the\r\n' +
'times the number of the processed desktop - can be used as input for the next\r\n' +
'parameter. The result of the last command is returned as error level.\r\n' +
'Virtual desktop numbers start with 0.\n' +
'\r\n' +
'Parameters (leading / can be omitted or - can be used instead):\n' +
'\r\n' +
'/Help /h /? this help screen.\r\n' +
'/Verbose /Quiet enable verbose (default) or quiet mode (short: /v and /q).\r\n' +
'/Break /Continue break (default) or continue on error (short: /b and /co).\r\n' +
"/Animation:<s> Enable switch animations (default) with 'On' or '1' and\r\n" +
" disable switch animations with 'Off' or '0' (short: /anim).\r\n" +
'/List list all virtual desktops (short: /li).\r\n' +
'/Count get count of virtual desktops to pipeline (short: /c).\r\n' +
'/GetDesktop:<n|s> get number of virtual desktop <n> or desktop with text <s> in\r\n' +
' name to pipeline (short: /gd).\r\n' +
'/GetCurrentDesktop get number of current desktop to pipeline (short: /gcd).\r\n' +
'/Name[:<s>] set name of desktop with number in pipeline (short: /na).\r\n' +
'/Wallpaper[:<s>] set wallpaper path of desktop with number in pipeline (short:\r\n' +
' /wp).\r\n' +
'/AllWallpapers:<s> set wallpaper path of all desktops (short: /awp).\r\n' +
'/IsVisible[:<n|s>] is desktop number <n>, desktop with text <s> in name or with\r\n' +
' number in pipeline visible (short: /iv)? Returns 0 for\r\n' +
' visible and 1 for invisible.\r\n' +
'/Switch[:<n|s>] switch to desktop with number <n>, desktop with text <s> in\r\n' +
' name or with number in pipeline (short: /s).\r\n' +
'/Left switch to virtual desktop to the left of the active desktop\r\n' +
' (short: /l).\r\n' +
'/Right switch to virtual desktop to the right of the active desktop\r\n' +
' (short: /ri).\r\n' +
'/Wrap /NoWrap /Left or /Right switch over or generate an error when the edge\r\n' +
' is reached (default)(short /w and /nw).\r\n' +
'/New create new desktop (short: /n). Number is stored in pipeline.\r\n' +
'/Remove[:<n|s>] remove desktop number <n>, desktop with text <s> in name or\r\n' +
' desktop with number in pipeline (short: /r).\r\n' +
'/RemoveAll remove all desktops but visible (short: /ra).\r\n' +
'/SwapDesktop:<n|s> swap desktop in pipeline with desktop number <n> or desktop\r\n' +
' with text <s> in name (short: /sd).\r\n' +
'/MoveDesktop:<n|s> move desktop in pipeline to desktop number <n> or desktop\r\n' +
' with text <s> in name (short: /md).\r\n' +
'/MoveWindowsToDesktop:<n|s> move windows on desktop in pipeline to desktop\r\n' +
' number <n> or desktop with text <s> in name (short: /mwtd).\r\n' +
'/MoveWindow:<s|n> move process with name <s> or id <n> to desktop with number\r\n' +
' in pipeline (short: /mw).\r\n' +
'/MoveWindowHandle:<s|n> move window with text <s> in title or handle <n> to\r\n' +
' desktop with number in pipeline (short: /mwh).\r\n' +
'/MoveActiveWindow move active window to desktop with number in pipeline\r\n' +
' (short: /maw).\r\n' +
'/GetDesktopFromWindow:<s|n> get desktop number where process with name <s> or\r\n' +
' id <n> is displayed (short: /gdfw).\r\n' +
'/GetDesktopFromWindowHandle:<s|n> get desktop number where window with text\r\n' +
' <s> in title or handle <n> is displayed (short: /gdfwh).\r\n' +
'/IsWindowOnDesktop:<s|n> check if process with name <s> or id <n> is on\r\n' +
' desktop with number in pipeline (short: /iwod). Returns 0\r\n' +
' for yes, 1 for no.\r\n' +
'/IsWindowHandleOnDesktop:<s|n> check if window with text <s> in title or\r\n' +
' handle <n> is on desktop with number in pipeline\r\n' +
' (short: /iwhod). Returns 0 for yes, 1 for no.\r\n' +
'/ListWindowsOnDesktop[:<n|s>] list handles of windows on desktop number <n>,\r\n' +
' desktop with text <s> in name or desktop with number in\r\n' +
' pipeline (short: /lwod).\r\n' +
'/CloseWindowsOnDesktop[:<n|s>] close windows on desktop number <n>, desktop\r\n' +
' with text <s> in name or desktop with number in pipeline\r\n' +
' (short: /cwod).\r\n' +
'/PinWindow:<s|n> pin process with name <s> or id <n> to all desktops\r\n' +
' (short: /pw).\r\n' +
'/PinActiveWindow pin active window to all desktops (short: /paw).\r\n' +
'/PinWindowHandle:<s|n> pin window with text <s> in title or handle <n> to all\r\n' +
' desktops (short: /pwh).\r\n' +
'/UnPinWindow:<s|n> unpin process with name <s> or id <n> from all desktops\r\n' +
' (short: /upw).\r\n' +
'/UnPinActiveWindow unpin active window from all desktops (short: /upaw).\r\n' +
'/UnPinWindowHandle:<s|n> unpin window with text <s> in title or handle <n>\r\n' +
' from all desktops (short: /upwh).\r\n' +
'/IsWindowPinned:<s|n> check if process with name <s> or id <n> is pinned to\r\n' +
' all desktops (short: /iwp). Returns 0 for yes, 1 for no.\r\n' +
'/IsWindowHandlePinned:<s|n> check if window with text <s> in title or handle\r\n' +
' <n> is pinned to all desktops (short: /iwhp). Returns 0 for\r\n' +
' yes, 1 for no.\r\n' +
'/PinApplication:<s|n> pin application with name <s> or id <n> to all desktops\r\n' +
' (short: /pa).\r\n' +
'/UnPinApplication:<s|n> unpin application with name <s> or id <n> from all\r\n' +
' desktops (short: /upa).\r\n' +
'/IsApplicationPinned:<s|n> check if application with name <s> or id <n> is\r\n' +
' pinned to all desktops (short: /iap). Returns 0 for yes, 1\r\n' +
' for no.\r\n' +
'/Calc:<n> add <n> to result, negative values are allowed (short: /ca).\r\n' +
'/WaitKey wait for key press (short: /wk).\r\n' +
'/Sleep:<n> wait for <n> milliseconds (short: /sl).\n' +
'\r\n' +
'Hint: Instead of a desktop name you can use LAST or *LAST* to select the last\r\n' +
'virtual desktop.\r\n' +
'Hint: Insert ^^ somewhere in window title parameters to prevent finding the own\r\n' +
'window. ^ is removed before searching window titles.\n' +
'\r\n' +
'Examples:\r\n' +
'Virtualdesktop.exe /LIST\r\n' +
'Virtualdesktop.exe "-Switch:Desktop 2"\r\n' +
'Virtualdesktop.exe -New -Switch -GetCurrentDesktop\r\n' +
'Virtualdesktop.exe Q N /MOVEACTIVEWINDOW /SWITCH\r\n' +
'Virtualdesktop.exe sleep:200 gd:1 mw:notepad s\r\n' +
'Virtualdesktop.exe /Count /continue /Remove /Remove /Count\r\n' +
'Virtualdesktop.exe /Count /Calc:-1 /Switch\r\n' +
'VirtualDesktop.exe -IsWindowPinned:cmd\r\n' +
'if ERRORLEVEL 1 VirtualDesktop.exe PinWindow:cmd\r\n' +
'Virtualdesktop.exe -GetDesktop:*last* "-MoveWindowHandle:note^^pad"\r\n',
stderr: ''
}
Node.js v22.14.0
While the point is that the code is executed correctly and this is clear from the output before!
Anyway I tried to handle it with try/catch:
try {
const { stdout, stderr } = await execAsync(`VirtualDesktop11.exe`);
console.log('result:', stdout, stderr);
} catch (error) {
console.error('ERROR:', error.message);
}
But this always returns the following error even when the command is executed correctly:
ERROR: Command failed: VirtualDesktop11.exe
I noticed that the error object in the previous code also contains stdout. So I have to do it in a catch block, which makes the code dirty and unprincipled. Something like this:
try {
await execAsync(`VirtualDesktop11.exe`);
} catch (error) {
if (error.stderr) {
console.log("stderr from execAsync:", error.stderr);
}
if (error.stdout) {
console.log("stdout from execAsync:", error.stdout);
}
}
It seems to me that my code should not encounter stderr when we have no errors and only stdout. Maybe this is a bug in the code structure. For example, maybe stderr: "" should not be generated or at some other level it is causing the library I am using to fail and generate the error.
Hello @NabiKAZ,
calling Virtualdesktop.exe without parameter is detected as an error and returns ERRORLEVEL 2. If you want to show the help text please use Virtualdesktop.exe /?, Virtualdesktop.exe /h or Virtualdesktop.exe /help as indicated from the help text, then no error is generated.
Greetings
Markus
Thanks. Ok, it worked fine for /help or /list. But when I set /MoveWindowHandle it still gives an error. While this command works correctly on the command line. Even more interesting is that even though the code throws an error, it executes correctly. That is, despite the stderr error, my window is moved correctly! That is why I said in the beginning that it always throws an error.
Code example 1 and its output:
import { exec } from 'child_process';
import util from 'util';
const execAsync = util.promisify(exec);
try {
const title = 'Calculator';
const titleModified = title[0] + "^^" + title.slice(1);
const result = await execAsync(`C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"${titleModified}"`);
console.log('result:', result);
} catch (error) {
console.log('ERROR:', error);
}
ERROR: Error: Command failed: C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"C^^alculator"
at genericNodeError (node:internal/errors:983:15)
at wrappedFn (node:internal/errors:537:14)
at ChildProcess.exithandler (node:child_process:414:12)
at ChildProcess.emit (node:events:518:28)
at maybeClose (node:internal/child_process:1101:16)
at ChildProcess._handle.onexit (node:internal/child_process:304:5) {
code: 1,
killed: false,
signal: null,
cmd: 'C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"C^^alculator"',
stdout: "Virtual desktop number 1 (desktop 'Desktop 2') selected\r\n" +
"Window 'Calculator' moved to desktop number 1 (desktop 'Desktop 2')\r\n",
stderr: ''
}
I thought it might be related to ^^ but no: Code example 2 and its output:
import { exec } from 'child_process';
import util from 'util';
const execAsync = util.promisify(exec);
try {
const title = 'Calculator';
const result = await execAsync(`C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"${title}"`);
console.log('result:', result);
} catch (error) {
console.log('ERROR:', error);
}
ERROR: Error: Command failed: C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"Calculator"
at genericNodeError (node:internal/errors:983:15)
at wrappedFn (node:internal/errors:537:14)
at ChildProcess.exithandler (node:child_process:414:12)
at ChildProcess.emit (node:events:518:28)
at maybeClose (node:internal/child_process:1101:16)
at ChildProcess._handle.onexit (node:internal/child_process:304:5) {
code: 1,
killed: false,
signal: null,
cmd: 'C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"Calculator"',
stdout: "Virtual desktop number 1 (desktop 'Desktop 2') selected\r\n" +
"Window 'Calculator' moved to desktop number 1 (desktop 'Desktop 2')\r\n",
stderr: ''
}
Hello @NabiKAZ,
I think I understand the issue now. But the real challenge here is that I intentionally set the last result as error level so that it can be reused in scripts, and this conflicts with your usage. I think only a trick can help here: always set /GetDesktop:0 as last parameter for VirtualDesktop.exe.
This selects desktop 0 and so 0 ist returned by the program. And 0 means no error. And since no action is provided, the parameter will do nothing.
Hope this helps,
greetings
Markus
Thanks, this seems to be working fine:
const result = await execAsync(`C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"${titleModified}" /GetDesktop:0`);
result: {
stdout: "Virtual desktop number 1 (desktop 'Desktop 2') selected\r\n" +
"Window 'Calculator' moved to desktop number 1 (desktop 'Desktop 2')\r\n" +
"Virtual desktop number 0 (desktop 'Desktop 1') selected\r\n",
stderr: ''
}
But this is a question for me why the command line does not produce an error but the script sees it as an error (even stderr is empty)
PS C:\> C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"C^^alculator"
Virtual desktop number 1 (desktop 'Desktop 2') selected
Window 'Calculator' moved to desktop number 1 (desktop 'Desktop 2')
PS C:\> C:/Users/Nabi/Downloads/VirtualDesktop11.exe /GetDesktop:*last* /MoveWindowHandle:"C^^alculator" /GetDesktop:0
Virtual desktop number 1 (desktop 'Desktop 2') selected
Window 'Calculator' moved to desktop number 1 (desktop 'Desktop 2')
Virtual desktop number 0 (desktop 'Desktop 1') selected
Hello @NabiKAZ,
there's an very old convention on Unix, CP/M, DOS, Z/OS etc. that an exit code or return code of 0 for command line tools means success, other values mean error. The internet has a lot to say to it.
Greetings
Markus
Closed for no reaction - think issue is solved