tl;dr
Your immediate problem is that, due to a PowerShell bug,[0] PowerShell omits the "" argument from the cmd /c start /WAIT call, which means that "C:\Program Files\VeraCrypt\Veracrypt.exe" becomes the title of the window created by start, and /v is interpreted as yet another start switch - which doesn't exist, hence the Invalid switch error. However, because start need not be used in your use case, as explained below, this problem isn't worth fixing.
Your question suggests that you want to execute a GUI application synchronously (in a blocking fashion) from PowerShell.
For console applications, direct invocation is the right solution (no need for cmd.exe), as shown in Bill Stewart's answer, because PowerShell (invariably) executes them synchronously.
Synchronous execution of GUI application can be achieved in of three ways (leaving direct use of .NET APIs aside), all of which are detailed in the next sections:
Delegating to cmd /c " ... ", without additional use of start /WAIT
Using direct invocation in combination with piping to a dummy command,
& ... | Write-Output
Using the Start-Process cmdlet with its -Wait switch.
Caveat:
These techniques only work for GUI applications that do not delegate to a different process on startup and then exit right away.
Start-Process -Wait, unlike the other two techniques, is more robust in that it also waits for any child processes launched by the application (startup) process to terminate.
However, even Start-Process -Wait isn't sufficient for GUI applications that delegate to a non-child process, which appears to be the case at least with some modern UWP / Windows App SDK applications such as the built-in calculator application, calc.exe, as well as for the Microsoft Edge browser.
Your use of start /WAIT suggests that the target executable is a GUI application whose termination you want to wait for, i.e. you want to invoke it synchronously (in a blocking fashion).
However, you do not need start /WAIT, because cmd /c itself ensures synchronous execution.[1]
Additionally, to avoid problems with cmd.exe's parsing of multiple "..."-enclosed arguments, enclose the executable command line overall in "..." too, i.e. pass a single double-quoted argument to the /c parameter, with embedded double-quoted arguments.[2]
Therefore:
# Synchronously executes Veracrypt.exe and reflects its exit code in $LASTEXITCODE
# Note the overall "..." enclosure of what follows /c
cmd /c " "C:\Program Files\VeraCrypt\Veracrypt.exe" /v "C:\Location_of_Veracrypt_Container" /l P: /p Password /q /hash SHA-512 /nowaitdlg "
Note:
The above should execute Veracrypt.exe synchronously and report its exit code back to PowerShell, which the latter reflects in the automatic $LASTEXITCODE variable.
While use of cmd.exe involves an extra child process (compared to the PowerShell-native solutions discussed in the next section), that extra overhead is unlikely to matter in practice.
Conversely, the cmd /c approach offers one advantage over the PowerShell-native solutions: it gives you full control over how the arguments are quoted, which notably matters with GUI-subsystem applications such as msiexec.exe that situationally require arguments to be partially double-quoted (e.g. INSTALLLOCATION="C:\foo bar"; see this answer for more.
Taking a step back:
You do not need cmd.exe for synchronous execution of GUI applications in PowerShell:
PowerShell's own Start-Process cmdlet is roughly equivalent to cmd.exe's internal start command and also has a -Wait switch to ensure synchronous execution.
- While its use - demonstrated further below - is more cumbersome than the simpler direct-invocation alternative discussed next, there may still be a reason to prefer it:
Unlike the simpler alternative and the cmd /c technique, Start-Process also waits for child processes of the immediately launched process to terminate, which also makes it work for applications with short-lived start-up processes that delegate to a child process; however, note that even that isn't enough for some modern UWP / Windows App SDK applications such as the built-in calculator application, calc.exe; it does, however, work for the Windows 11 version of notepad.exe, for instance, but not (reliably) for applications that delegate to preexisting and therefore (non-child) processes, such as Microsoft Edge.
However, a simpler alternative is to use direct invocation and pipe (|) the invocation to a dummy command, which not only makes the call synchronous, but also directly populates $LASTEXITCODE, as with the cmd /c technique (by contrast, with Start-Process you'd have to also use -PassThru, and examine the returned process-information object's .ExitCode property).
This approach is shown next.
Using direct invocation with |:
# Synchronously executes Veracrypt.exe and reflects its exit code in $LASTEXITCODE
# Note the pipe operator (|) at the end of the first line, and the use of
# dummy command Write-Output as the receiving command.
& "C:\Program Files\VeraCrypt\Veracrypt.exe" /v "C:\Location_of_Veracrypt_Container" /l P: /p Password /q /hash SHA-512 /nowaitdlg |
Write-Output
Note:
It is use of |, the pipeline operator (at the end of the Veracrypt.exe call, scrolled out of view) that makes direct invocation of a GUI application synchronous,[3] which would otherwise be asynchronous (invariably so, unlike in cmd.exe)[4] Additionally, as noted, it records the application process' exit code in the automatic $LASTEXITCODE variable.
Note that this contrasts with console application, which in direct invocation are invariably launched synchronously.
Typically, GUI applications produce no output to the parent process' console, so the use of Write-Output as the receiving command will have no effect.
However, some GUI applications explicitly attach to their parent process' console in order to print diagnostic output to it, which Write-Output would surface; if that is undesired, pipe to Out-Null instead.
Because the executable path is quoted, PowerShell requires use of &, the call operator, to invoke it, purely for syntactic reasons, as explained in this answer.
Its use is optional for literal, unquoted executable names/paths (whereas variable-based names/paths require & too).
Using Start-Process:
# Synchronously executes Veracrypt.exe and returns a process-information object,
# due to use of -Wait and -PassThru.
# $processInfo.ExitCode will contain the exit code.
$processInfo =
Start-Process -Wait -PassThru "C:\Program Files\VeraCrypt\Veracrypt.exe" @"
/v "C:\Location_of_Veracrypt_Container" /l P: /p Password /q /hash SHA-512 /nowaitdlg
"@
Note:
Even though Start-Process technically accepts an array of arguments (separated with ,), it is ultimately easier to pass the list of arguments encoded in a single string, as shown, due to a long-standing bug - see this answer for more.
A here-string (@"<newline>...<newline>"@) is used to allow use of embedded " chars. without escaping.
[0] See this answer for background information. The short of it is that Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and final version is 5.1) has bugs with respect to passing the empty string as an argument as well as arguments with embedded double quotes. While this was mostly fixed in PowerShell (Core) 7 v7.3+, the old, broken behavior is by default selectively retained for certain applications that exhibit nonstandard behavior, notably including cmd.exe and batch files.
[1] Note: Only a directly launched process is waited for, so this technique won't work for applications that ultimately launch via a child process or delegate to a preexisting process. By contrast, the -Wait switch of PowerShell's Start-Process also waits for child processes; see later in this post.
[2] Note that, due to cmd.exe's idiosyncratic parsing, embedded " chars. do not require escaping.
[3] Note that alternatively using >, the redirection operator, does not ensure synchronous execution. This surprising discrepancy has been reported in GitHub issue #26455.
[4] cmd.exe's behavior depends on whether the invocation occurs interactively in a session, in which case it is asynchronous, vs. in a batch file / via cmd /c (or cmd /k), in which case it is synchronous
cmdas opposed to justVeracrypt.exe /v "C:\Location_of_Veracrypt_Container" /l P: /p Password /q /hash SHA-512 /nowaitdlg? If the reason is it doesn't wait for the process to finish you should useStart-Process -Waitifmakes a difference: yourcmd /c ...call is broken in itself, as explained at the top of my answer - omitting the unnecessarystart /WAIT ""part bypasses the problem, as long as you enclose the exe call in"..."overall. While aStart-Processsolution is also possible (but more cumbersome), note that your attempt in the previous comment mistakenly passes the pass-through arguments individually; instead, pass them as an array (,-separated) or, preferably, as a single string; again, see my answer.