0

So, I have a tiny fragment of C code running on a windows box, that reads:

/* invoke command */
impl->procHandle = _spawnve(_P_NOWAIT, command, vargs, env);
if (impl->procHandle == -1) {
  printf("Failed to invoke command: %s\n", strerror(errno));
  impl->busy = false;
}
printf("VICTORY\n");

I wrote some unit tests around this where my "command" was C:\windows\system32\ipconfig.exe and it works, no problem.

Tried to use it for an application launcher... doo doo. Failed with the helpful error:

The application failed to initialize properly (0xc0150004). 
Click on OK to terminate the application.

Ok... searching around I discovered that the error code is STATUS_SXS_ASSEMBLY_NOT_FOUND, and it happens when I try to launch notepad.exe as well. Missing assemblies?

Why is this happening?

How can I work around it?

I'm just guessing here, but I suspect it has something to do with needing the PATH variable to be set in the _spawnve(), but I dont know what it should be. I tried passing in the path, but that doesn't seem to help. Running this code:

int offset = 0;
while (vargs[offset] != NULL) {
  printf("vargs %d: %s\n", offset, vargs[offset]);
  ++offset;
}
offset = 0;
while (env[offset] != NULL) {
  printf("env %d: %s\n", offset, env[offset]);
  ++offset;
}

Yeilds:

vargs 0: C:\windows\system32\notepad.exe
env 0: PATH=c:\WINDOWS\system32

ie. I am passing in argv[0], and a path value; not other env variables or arguments.

Any ideas?

--

Edit:

So, it seems this error is occurring because the PATH is not correctly set when I invoke the command using _spawnve().

This is made obvious by invoking either _spawnv() or _spawnvpe(), both of which seem to work correctly.

However, that doesn't really help me, because I need to specify an additional PATH component for the application when it runs. Passing PATH=... into _spawnvpe() causes the same error, and obviously _spawnv is no used because it doesn't allow you to specify the PATH.

So really, the answer to this question is: Because the PATH variable is wrong.

...but I still have no idea what it should be. There seem to be no working examples of this that I can find anywhere. I'll accept any answer that links to an example of coding using _spawnve() or _spawnvpe() and passing the PATH variable into it (and working).

Edit #2:

Really. No, actually, this doesn't work. Here's an example of it not working. Forget linking to an example that works; just modify my example and post a diff that 1) passes in PATH and 2) runs without an error.

Nb. Want to see it work? change to _spawnv() or make the env value NULL and it runs just fine.

#include <stdio.h>
#include <windows.h>
#include <process.h>
#include <errno.h>

int main(int argc, char *argv[]) {

  char *path_value;
  char buffer[4000];
  const char *env[2];
  const char *args[1];
  char *command;
  int result;
  intptr_t procHandle;

  path_value = getenv("PATH");
  sprintf(buffer, "PATH=%s", path_value);
  env[0] = buffer;
  env[1] = NULL;

  args[0] = NULL;

  int offset = 0;
  while (env[offset] != NULL) {
    printf("env %d: %s\n", offset, env[offset]);
    ++offset;
  }

  offset = 0;
  while (args[offset] != NULL) {
    printf("arg %d: %s\n", offset, args[offset]);
    ++offset;
  }

  command = "C:\\windows\\system32\\notepad.exe";

  procHandle = _spawnvpe(_P_NOWAIT, command, args, NULL);
  if (procHandle == -1) {
    printf("Failed to invoke command: %s\n", strerror(errno));
    exit(1);
  }

  _cwait(&result, procHandle, 0);
  if (result != 0)
    printf("Command exited with error code %d\n", result);
}

Output:

env 0: PATH=.;c:\Program Files\Common Files\Microsoft Shared\Windows Live;c:\WINDOWS\system32;c:\WINDOWS;c:\WINDOWS\System32\Wbem;c:\Program Files\Microsoft SQL Server\100\Tools\Binn\;c:\Program Files\Microsoft SQL Server\100\DTS\Binn\;c:\Program Files\CMake 2.8\bin;c:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\;c:\Program Files\Common Files\Microsoft Shared\Windows Live
Command exited with error code -1072365564
Doug
  • 32,844
  • 38
  • 166
  • 222
  • Document what you find back in the Windows Application event log. – Hans Passant Jun 10 '12 at 14:59
  • 1
    If you're talking about the "Application" log in the event viewer, then there's nothing there. I ran the failing code a dozen times and no new log entries turned up. If you're talking about something else, be more specific. – Doug Jun 10 '12 at 15:05
  • It may simply means your app needs some DLL that are not there. See this on SO: http://stackoverflow.com/questions/1582844/vcredist-x86-dll-and-version-8-0-50727-4053 – Simon Mourier Jun 15 '12 at 09:42
  • What's with the windows.0 in vargs[0]? Do you have a c:\windows and a c:\windows.0? If so, which one is the real Windows directory? Also, what version of Windows are you testing this on? – Harry Johnston Jun 16 '12 at 04:09
  • Was running on a machine with windows installed into windows.0; I've moved the code onto a new fresh machine running XP with a default C:\Windows\ path and it still has the same issue; I've removed the odd '.0' from the question. This is on XP SP 3 – Doug Jun 17 '12 at 04:52
  • @SimonMourier I've clarified the question to make it more obvious that I know that, but I don't know how to work around it. – Doug Jun 17 '12 at 04:54
  • You need to pass the PATH variable that your program was given into the subprocess. In fact, for reliable operation, you should pass all the environment variables your program was given into the subprocess. You can of course include additional ones as well. – Harry Johnston Jun 17 '12 at 10:34
  • @HarryJohnston great. Please provide an example for how to do that. – Doug Jun 17 '12 at 12:08

3 Answers3

2

This is wrong:

const char *args[1];
args[0] = NULL;

you need

const char *args[2];
args[0] = "notepad";
args[1] = NULL;

Other than that, your example code works, at least when compiled with Visual Studio 2010. I've tested it on both Windows 7 and Windows XP, and it works. Notepad runs.

What compiler are you using?

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • Your example code wasn't passing in the env array, but it still works for me when this is changed. – Harry Johnston Jun 19 '12 at 01:21
  • It might be worth your while to build a virtual machine with a vanilla install of Windows XP, to determine whether it is your compiler or your test machine that is behaving differently to mine. I do have access to various different versions of Visual Studio so if you are using one of these I could try that too. – Harry Johnston Jun 19 '12 at 01:36
  • Looks like it was the args[] that was causing the strange error (funny, on windows 7 the error is different and comes out as invalid argument; only xp throws STATUS_SXS_ASSEMBLY_NOT_FOUND for me); the example works with your changes! thanks very much! – Doug Jun 19 '12 at 06:14
0

You are right, the second parameter to _spawnev() takes the name of the app to be executed inlcuding its full path.

To get around to know the path you could call the command processer cmd.exe and pass it along the name of the app to execute as a parameter to it using cmd.exe's option /C.

This works in all the cases where you could have started the application out of one of cmd.exe's command line windows.

cmd.exe knows the value of the environment variable PATH and used it to search through it for the app's path to start.

The path to cmd.exe itself could be read from the environment variable COMSPEC.

Update: For more on this issue (including examples) please read here.

alk
  • 69,737
  • 10
  • 105
  • 255
  • There's nothing wrong with argument 2 of my command. If I swap to _spawnv() instead of _spawnve() it works perfectly. ...but obviously then I can't pass in any other env values I want. (Ie. 'command' is identical to the value of vargs[0]) – Doug Jun 10 '12 at 15:07
  • Also, when I try using "cmd.exe /C blah" in _spawnve I get a no-such-command error. Could you provide an example of how to make that work? – Doug Jun 10 '12 at 15:12
  • @doug you need to prefix `"cmd.exe"` with what you have read as value of the environment variable `COMSPEC` by using the `getenv()` function. – alk Jun 10 '12 at 15:29
  • @doug Regaring passing a set of env/ var/s you might like to try taking the third value (usualy ommited) of the programs `int main( int argc, char *argv[], char *env[] )` function which receives a reference to the environment of the program itself. – alk Jun 10 '12 at 15:32
  • yes, astonishingly, I have actually read the msdn documentation before blindly asking this question. example helpfully doesn't include the path variable or launch anything that depends on win dlls -> ie. doesn't solve anything. I'll try using getenv() and passing the value in, but I have to say, it doesn't look promising. (getenv() returns a path value that's pretty much identical to what I'm passing in in env[0]) – Doug Jun 10 '12 at 15:40
  • Did you try prefixing the COMSPEC's value to the actual programs name "cmd.exe"? @doug – alk Jun 10 '12 at 15:43
  • @doug Reading the MSDN documentation would also have pointed you to use a 'p'-member of the spawn() family of functions to have the function use the PATH env/ var/ autmagically to resolve the path to the program to start. Thanks. – alk Jun 10 '12 at 15:48
  • If you have an example of this _actually working_ I'll accept the answer; otherwise I guess I'll wait for someone else to provide a better solution. – Doug Jun 11 '12 at 00:45
-1

As specified here _spawn, _wspawn Functions, only the functions with a 'p' letter in the name implicitely use the PATH environment variable. The others don't.

So you need to do this:

char *args[] =  {"notepad.exe", NULL };
_spawnvpe(_P_NOWAIT, args[0], args, NULL);
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Not true. _spawn inherits the PATH variable too. _spawnvpe inherits the PATH *and* allows you to specify environment variables. However, if you try to specify PATH as one of those env variables, it messes up. – Doug Jun 18 '12 at 14:31
  • Well, the official docs is quite explicit about it *PATH environment variable is used to find file to execute when _spawn function suffix is p*, and it does not specify it supports the PATH as an env variable. – Simon Mourier Jun 18 '12 at 15:40