0

Platform Qt 5.15.2 Windows.

I have a function that uses this code:

QString commstr = "c:/temp/cpath.bat";

QProcess::startDetached("cmd.exe", QStringList() << "/c" << "start" << commstr);

bat file:

echo off
set PATH=%PATH%; more paths added here ...

It opens a console and runs the .bat file contained in the QString commstr. The bat file makes a temporary change to the path then finishes, leaving the console open for further user interaction. Works great, just as intended.

Here's the weird problem: If the userid has embedded spaces i.e. "joe user" the .bat file is not run. The console opens, but commstr is never executed. I can't for the life of me figure out why the userid matters here.

I can't imagine what part the userid plays in this. The user's home directory (e.g. "C:\Users\joe user") isn't referenced in the code, nor is the program installed there. The program works w/o issues other embedded spaces directories anyway (e.g "C:\Program Files").

Alan
  • 1,265
  • 4
  • 22
  • 44
  • Unrelated, shorter: `QProcess::startDetached("cmd.exe", { "/c", "start", commstr});` – hyde Aug 10 '23 at 18:05
  • Why are you runnng `cmd.exe` to start a `.bat` file? What is wrong with just running the `.bat` file directly? – Compo Aug 10 '23 at 18:36
  • QString commstr = "\"" + "c:/temp/cpath.bat" + "\""; – Ripi2 Aug 10 '23 at 18:51
  • I need an open console with a specialized path. – Alan Aug 10 '23 at 19:17
  • QString commstr = "\"" + "c:/temp/cpath.bat" + "\""; fails to compile. Invalid operands to binary expression. – Alan Aug 10 '23 at 19:29
  • I did try QDir::setCurrent(QStringLiteral("c:/temp/")); to change the working directory, but no joy – Alan Aug 10 '23 at 19:55
  • FYI, there is absolutely no reason to run a batch file with that content. As soon as it has redefined the variable named `%PATH%` it closes, and that change is immediately reverted to its previous value. If, as you say, you want the 'console' to stay open, you would use the `/k` option to `cmd.exe`, instead of its `/c` option. – Compo Aug 10 '23 at 20:07
  • Maybe that is not a space character issue. Maybe joe user does not have the access to c:\temp? Did you try to execute that command from the Command prompt? – Dmytro Ovdiienko Aug 10 '23 at 21:26
  • The internal command __START__ of `cmd.exe` interprets the first argument string enclosed in `"` as optional window title string on calling the Windows kernel library function [CreateProcess](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw) with an appropriate filled out [STARTUPINFO](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow) structure. The batch file name enclosed in `"` is passed via `lpTitle` in the `STARTUPINFO` structure to `CreateProcess` instead of `lpCommandLine`. – Mofi Aug 11 '23 at 05:32
  • The batch file programming experts use for that reason __START__ always with a windows title string as first argument. An empty title string specified with `""` is used on program to start as separate, parallel executed process is a Windows GUI application on which no console window is opened at all by `CreateProcess` for which `lpTitle` in the `STARTUPINFO` structure is used for. For console applications like `%SystemRoot%\System32\cmd.exe` is used by batch file programming experts a meaningful console window title string. – Mofi Aug 11 '23 at 05:36
  • In your C++ code should be used first `QByteArray systemRoot = qgetenv("SystemRoot");` The next line should be `QString commandProcess = (!systemRoot.isEmpty()) ? QString(systemRoot) + QStringLiteral("\\System32\\cmd.exe") : QStringLiteral("cmd.exe");` to get the fully qualified file name of the *Windows Command Processor* or very unlikely but not impossible just the file name with extension without path. Then `commandProcess` can be used twice. First as `program` argument for `QProcess::startDetached`. Second for the arguments list stored in the `QStringList`. – Mofi Aug 11 '23 at 05:51
  • `QProcess` has to call `CreateProcess` with expanded version of `%SystemRoot%\System32\cmd.exe` (or very unlikely just `cmd.exe`) for `lpApplicationName` and `/D /C start "meaningful title" %SystemRoot%\System32\cmd.exe /D /C "C:\temp\cpath.bat"` or very unlikely `/D /C start "meaningful title" cmd.exe /D /C "C:\temp\cpath.bat"` for `lpCommandLine`. There must be carefully read in [QProcess](https://doc.qt.io/qt-5/qprocess.html) documentation how `QProcess` interprets the argument strings in `QStringList`. It changes all `/` to ``\`` which is very bad here for `/D` and `/C`. – Mofi Aug 11 '23 at 05:58
  • `QProcess` also encloses each argument string automatically in `"` on containing a space. The modifications of `QProcess` on the argument strings are counterproductive in this case. The options `/D` and `/C` must be begin with `/` and not with ``\``. The window title string `meaningful title` must be always enclosed in `"` independent on containing a space or not. The batch file name should be best always enclosed in `"` as it could contain not only a space, but also one of these characters: ``&()[]{}^=;!'+,`~`` which require also enclosing the fully qualified batch file name in `"`. – Mofi Aug 11 '23 at 06:20
  • That means for your C++ program that the __static__ method `QProcess::startDetached()` cannot be used here. There must be used `QProcess batchProcess;` and `batchProcess.setProgram(commandProcess);` and `batchProcess.setNativeArguments(QStringLiteral("/D /C start \"meaningful title\" ") + commandProcess + QStringLiteral("/D /C \"") + QDir::toNativeSeparators(commstr) + QChar('\"'));` and last `batchProcess.startDetached(nullptr);` This code should work for any batch file name (which I have not tested). Run in a command prompt window `cmd /?` and `start /?` for output of usage help. – Mofi Aug 11 '23 at 06:20
  • BTW: The program written in C++ is designed for Windows. It is therefore also possible to forget `QProcess` here and call directly `CreateProcess` with an appropriate filled out `STARTUPINFO` structure to run just one (in expanded form) `C:\Windows\System32\cmd.exe` passed with `lpApplicationName` and `/D /C "C:\temp\cpath.bat"` passed with `lpCommandLine` to `CreateProcess`. The advantage would be one `cmd.exe` process less used and you can define in your code where the console window is opened on screen and the size of the console window. – Mofi Aug 11 '23 at 06:27
  • PS: You hopefully know that `set PATH=%PATH%; more paths added here ...` which should be better `set "PATH=%PATH%; more paths added here ..."` affects only the __local__ environment variable `PATH` of `cmd.exe` processing the batch file and not the environment variable `PATH` of your C++ coded program or any other process already running or executed next using `QProcess` or `CreateProcess` from within your C++ coded program. Read [What is the reason for "X is not recognized as an internal or external command, operable program or batch file"?](https://stackoverflow.com/a/41461002/3074564) – Mofi Aug 11 '23 at 06:30
  • @mofi adding a title string to my code fixed my issue. If you add this as an answer I'll gladly accept it. Thanks! – Alan Aug 11 '23 at 18:02

1 Answers1

0
 QProcess::startDetached("cmd.exe", QStringList() << "/c" << "start" << ""  << "/path/to/file.bat");

Adding a empty title string as the first argument following start fixed my issue.

Syntax
      START "title" [/D path] [options] "command" [parameters]

Key:
   title       Text for the CMD window title bar (required.)
   path        Starting directory.
   command     The command, batch file or executable program to run.
   parameters  The parameters passed to the command.```

Alan
  • 1,265
  • 4
  • 22
  • 44