1

My goal is to run a command line batch script (from a .bat file) without displaying the console window, and wait for it to finish running before continuing. I'm using the example from here. So I came up with the following code:

//NOTE: Error checks are omitted for brevity

//Get path to cmd.exe
WCHAR buffCmd[1024];
::GetEnvironmentVariable(L"ComSpec", buffCmd, 1024);

std::wstring runPath = buffCmd;
runPath = runPath + L" /C \"path-to\\test.bat\"";

LPWCH pEnvStrs = ::GetEnvironmentStrings();

STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);

::CreateProcess(NULL, &runPath[0], NULL, NULL, FALSE, 
    /*CREATE_NO_WINDOW | */     //Will uncomment it when I make it work
    CREATE_UNICODE_ENVIRONMENT,
    pEnvStrs, NULL, &si, &pi);

HANDLE hProc = pi.hProcess;

::WaitForSingleObject(hProc, INFINITE);

DWORD dwProcExitCode = 0xCCCCCCCC;
::GetExitCodeProcess(hProc, &dwProcExitCode);

//Clean up
::FreeEnvironmentStrings(pEnvStrs);
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);

And the batch test.bat file is just this:

notepad

But when I run it, and use /K option instead of /C to keep the console open, I get the following error in the console window that CreateProcess opens up:

The filename, directory name, or volume label syntax is incorrect.

enter image description here

and cmd.exe returns error code 1.

So what am I missing here?

EDIT: Sorry, forgot to mention, I'm calling it from a GUI process.

Community
  • 1
  • 1
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • 1
    Perhaps you got the path wrong. Not checking for errors is troubling. Use of SIZEOF looks wrong. Why are you using TCHAR? Are you targeting Win98? Why pass environment strings? Just pass NULL to inherit environment. – David Heffernan Feb 03 '17 at 22:50
  • @DavidHeffernan: I'm not checking for errors only in this code sample to make it more easy to read. Also adjusted for your gripes. – c00000fd Feb 03 '17 at 23:12
  • In other words you asking about code that we can't see. – David Heffernan Feb 03 '17 at 23:13
  • @DavidHeffernan: OK. Fixed it up. Sorry. Also I'm not sure if it matters, I run it on Windows 10. – c00000fd Feb 03 '17 at 23:14
  • My guess is that there is something wrong with whatever `" /C \"path-to\\test.bat\""` really is -- sometimes Windows chokes on Unicode characters which look like standard ASCII, but aren't. For example, this could happen if a quoted string is copied from a word processing app which automatically replaces typed quote characters with fancy Unicode open- and close-quote characters. – Christopher Oicles Feb 03 '17 at 23:45
  • 1
    If your path contains international characters, try replacing `L" /C \"path-to\\test.bat\"";` with `L" /C echo \"path-to\\test.bat\"";` and run this to check the output and see if `cmd` is actually interpreting the string as it appears in your IDE. I'm not really familiar with Dev C++, but there could be some kind of code-page vs. Unicode mismatch. – Christopher Oicles Feb 04 '17 at 00:01
  • @ChristopherOicles: Thanks. And, no. There's no Unicode characters in the path. It's clear as day. The one I'm testing it with doesn't even have spaces. Can you guys try it on your end? Does it work there? My suspicion now is that it has something to do with 64-bit vs. 32-bit process. I compile this executable as 32-bit but test it on 64-bit Windows 10, so there must be some "bitness" barrier or some similar quirk. I just can't find what exactly it doesn't like. – c00000fd Feb 04 '17 at 00:39
  • Can you show the value of `runPath` just after the assignment `runPath =`? Please. – AlexP Feb 04 '17 at 01:08
  • Ok, I built a 32-bit console app, with your code in `main()`, copied the exe to a new folder on a 64-bit Windows 10 system, made a subdirectory here called "path-to", copied a batch file named "test.bat", whose contents are just "notepad" (with a carriage return/newline combo at the end) and ran it -- and it just launched notepad. Lol. – Christopher Oicles Feb 04 '17 at 01:38
  • @ChristopherOicles: Aha. I think you hit onto something. The difference between our tests is that you were running it in a console app, and I run it in a GUI app. That is why it fails. Sorry, I didn't think about this "little nuance." The question is what difference does it make? Do I need to provide it with STDIN, STDOUT, STDERR pipes? – c00000fd Feb 04 '17 at 03:44
  • @AlexP: Sure, here's the actual value of `runPath`=`C:\WINDOWS\system32\cmd.exe /K "‪C:\Users\Dev\Desktop\test.bat"` – c00000fd Feb 04 '17 at 03:45
  • Well, I built another version, this time a GUI app (32 bit MFC), and again, it correctly opens a console window and launches notepad without any error message (I used a relative path, though -- I just noticed your the last comment with an absolute path). – Christopher Oicles Feb 04 '17 at 10:21
  • 1
    The only difference between an executable image running in the `CONSOLE` and `WINDOWS` subsystems is, that the system allocates a console for the former, and does not for the latter. The call to `CreateProcess` doesn't care either way. Since you are launching a command processor to run the batch file, that batch file has standard IO devices set up in case it needs them anyway. At any rate, the error seems to be caused by the contents of the batch file, not the code that invokes it. – IInspectable Feb 04 '17 at 12:51
  • I got it. See my separate answer w/the info. "Thanks" for downvoting, everyone. @ChristopherOicles: dude, you were correct in your initial assessment of the Unicode characters. – c00000fd Feb 04 '17 at 18:26

1 Answers1

2

Fellas, I think I got it. This is so stupid too. The answer has nothing to do with C++, CreateProcess or cmd.exe. It's Windows 10. I just recently switched to it from Windows 7 on my laptop. So it's an "un-learning curve".

I'm posting it here in case someone else runs into this idiotic thing that they added to Windows Explorer (I'm not sure if it was in Windows 10 or 8.1) so that you don't waste a day trying to fix something so stupid!

Anyway, the way I would get a path for a file in Windows 7, I'd right-click it in Windows Explorer and then go to Properties, then Security and copy it from the Object Name field:

enter image description here

Well, guess what? There's now a LEFT-TO-RIGHT EMBEDDING Unicode character 202A) sitting at the beginning, that Explorer is more than willing to copy along with the rest of the path. Moreover, when you paste it into your source file, it becomes invisible. At that point good luck figuring out why your code doesn't work.

Here's the hex view of the string (after I saved it in a Unicode text file, so first FEFF is a BOM marker):

enter image description here

I also found this article where Raymond Chen, as always, justifies its use.

So the bottom line, DO NOT COPY PATHS from the Security tab in Properties in Windows 10 (or 8.1)!

So Christopher Oicles had it right all along in the comments to my OP.

PS. Interestingly enough, if you copy just the folder path from the General tab, it does NOT have that Unicode character in the beginning. So go figure, Microsoft!

Community
  • 1
  • 1
c00000fd
  • 20,994
  • 29
  • 177
  • 400