4

I'm creating a PowerShell that runs a simple Java program using:

Start-Process -filepath .\java.exe -ArgumentList "Input" -NoNewWindow

The Input class in this case simply accepts and prints a string.

I'm expecting strings with length greater than the cmd.exe 8191 char limit, so I need to run it in PS.

Current scenario:

  • Running the above command in the PS directly works and Input accepts string greaters than 8191 char.
  • Saving the command in a ps1 file and run it, a PS windows appears but cmd runs behind the scene as it hits the 8191 character limit.

I tried adding -Wait as well, but not luck.

Any idea?

Sled
  • 18,541
  • 27
  • 119
  • 168
  • Both right-clicking/run with powershell on the ps1 script and from another ps1 file using `Invoke-Expression .\javaRun.ps1`. If I invoke java.exe directly, without `Start-Process` I still hit the 8191 char limit. –  May 17 '20 at 10:33
  • I tried the `Invoke-Expression` only in order to find a workaround, I'd like to stick to a single script. Why the only wait is to run the script or the actual command within the PS? I don't know much about Windows but it sounds a silly behaviour. How come running a PS script it uses the cmd underneath? –  May 17 '20 at 16:22

1 Answers1

3

As you state, the max. length of an interactive command line in cmd.exe (at the interactive Command Prompt, but not in batch files) is officially 8,191 characters - see the documentation. If you try to submit a command line via the Run dialog (WinKey-R), the limit appears to be even lower.

However, this does not apply to command lines in PowerShell, - neither at the interactive prompt nor in .ps1 files - where the limit is much higher - 32,764 characters.[1]

In other words: commands launched from within PowerShell are not subject to the 8,191-character limit (unless they themselves impose their own limit, which a call to cmd.exe /c does, for instance) - irrespective of whether you use Start-Process or Invoke-Expression (neither of which you should use for invoking console applications / scripts - see this answer and this answer) or by direct invocation (by file path, possibly preceded by &, the call operator - e.g., .\javaRun.ps1 or & .\javaRun.ps1) - none of these methods involve cmd.exe

As for calling external programs from PowerShell:

  • .NET-based executables and most natively compiled executables are capable of receiving command lines up to PowerShell's 32,764-character limit.

  • By contrast, if the target executable happens to be cmd.exe (a command line executed via cmd /c), it again imposes the overall 8,191-character limit. (However, in batch files - .cmd, .bat - that limit only applies to cmd.exe's internal (built-in) commands, such as echo; calls to external programs are actually allowed to be 32,766 chars. long).

  • I don't know about executables using different runtimes, such as java.exe; but if they turn out to be the bottleneck, you won't be able to overcome this problem from PowerShell, and will instead have to find a different way to pass input to the target program, such as via standard input or an aux. file.


[1] I don't have an official reference, but you can verify the limit experimentally as follows: C:\Windows\system32\choice.exe /d y /t 0 /m ('x' * (32764-44)) 2>$null. -44 subtracts the length of the static prefix of the command, and creates as many x chars. as needed to amount to an overall command-line length of line is 32,764. The call succeeds (in the sense that it is successfully submitted and choice.exe is actually invoked, irrespective of whether it then reports an error or not), and produces no output (there's incidental stderr output, which 2>$null discards). If you increase the length by only 1 character (change -44 to -43), the invocation breaks fundamentally with "Program 'choice.exe' failed to run: The filename or extension is too long".
Presumably, the limit is ultimately imposed by the CreateProcess WinAPI function, whose docs state: "The maximum length of this string is 32,767 characters, including the Unicode terminating null character". However, even taking the extra terminating null character that is used behind the scenes into account, 37,764 + 1 is 32,765, and I don't know what accounts for the remaining 2 characters.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thank you, I tried other ways to understand how this works. It seems that running `Start-Process Powershell` in a ps1 file, does not hit the char limit. It's been hit when from the same opened new shell, java is called. But how can Java could be the bottleneck if running it with -NoNewWindow works? Windows/PS design is doing something that I can't fully understand. –  May 18 '20 at 15:04
  • 1
    @user219378 If you don't hit the limit with `Start-Process -NoNewWindow`, then you won't hit it with direct invocation either (`.\java.exe ...`), which you should use instead. I was only _speculating_ about `java.exe` being the limitation, as the only plausible explanation for why it wouldn't work from PowerShell as described in the answer. From `cmd.exe` / a batch file you _will_ hit the limit, irrespective of what executable you call. I'm unclear on what you're still unclear on. – mklement0 May 18 '20 at 15:33
  • @user219378: Are you perhaps trying to do something like `Start-Process .\javaRun.ps1 ...`? That will perform the default GUI shell verb, Open, on the _document_ that the `.ps1` file is from the system's perspective - and then whatever action is defined via the registry will be performed - which _could_ involve `cmd.exe` - depending on your machine's configuration. Instead, use `Start-Process powershell.exe -Args '-NoExit -File .\javaRun.ps1 ...'` for predictable invocation, _if_ you need the script to run in a new window (if not, use direct invocation). – mklement0 May 18 '20 at 15:54
  • To recap: if I use `Start-Process Powershell` I don't hit the limit, from the same shell if I run `.\java.exe Input` I hit the limit. Insted, from the same shell using `Start-Process .\java.exe Input -NoNewWindows` works. I'd simply like to make the latter work from a ps1, but I I use the same command in a ps1, I hit the limit. And this it doesn't make any sense. –  May 18 '20 at 17:05
  • Probably a better question now it would be, how I can ensure that `.\java.exe` is not the bottleneck and/or how it gets called from Windows? –  May 18 '20 at 17:24
  • I've just noticed that the only one that worked with the `-NoNewWindows` is just giving me the impressiont that it does. But the java application is not accepting the input correctly when pasting a long string. If I call Java without the `Start-Process` it opens a new conhost.exe and java in a cmd windows. I hate it. –  May 18 '20 at 17:35
  • The sysinternal Process Explorer tells me that using both 'Start-Process' with `-NoNewWindows` and calling `.\java.exe` directly, it opens a process within the PS without opening a new conhost.exe. If I don't include `-NoNewWindows` it does open a new conhost.exe under java.exe. Still it looks all under the powershell.exe tree. So @mklement0 you might be right, java could be the limitation here. No idea how to prove it yet. –  May 18 '20 at 17:46
  • @user219378. Yes, `Start-Process -NoNewWindow` and direct execution run in the caller's console window, by design. `Start-Process` without `-NoNewWindow` indeed creates a new console window, but it's still a _child process_ of the caller's process - and it does _not_ involve `cmd.exe` - it is `java.exe` that directly runs in that window. I don't have Java, but I can tell you that a .NET-based executable works fine in all these situations. – mklement0 May 18 '20 at 19:20
  • Do you have a source for this PowerShell command limit? Searching for that information is what got me here. – Sled Jan 18 '23 at 18:18
  • 1
    @Sled, please see the footnote I've just added. – mklement0 Jan 18 '23 at 18:50