5

I am trying to implement CreateProcessW within a dll so that the user can launch an application in a separate process.

For starters I am hardcoding the commands in the code until I figure it out..

I've got

STARTUPINFO si = {sizeof(STARTUPINFO), 0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
LPCTSTR AppName=L"c:\\utilities\\depends.exe";
LPTSTR Command = L"c:\\utilities\\tee.exe";
if (CreateProcessW(AppName, Command, 0, 0, 0, CREATE_DEFAULT_ERROR_MODE, 0, 0, &si, &pi)) {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        return GX_RESULT_OK;
    } else {
        .. show error msg
    }

This will launch Depends but won't open Tee.exe. There's no error, it just ignores the command line parameter. The parameters are correct and I can run it at the run prompt and it works fine. If I leave AppName blank and specify Depends.exe as the Command parameter it also works, but if I specify

LPTSTR Command = L"c:\\utilities\\depends.exe c:\\utilities\\tee.exe";

I get Error 3: "The system cannot find the path specified".

Also, by specifying the lpCurrentDirectory parameter it is likewise ignored.

Dan Korn
  • 1,274
  • 9
  • 14
marcp
  • 1,179
  • 2
  • 15
  • 36
  • 2
    My crystal ball says that you obfuscated your code and the real path is not c:\utilities. But one that has spaces in the path name. Which requires you to put double-quotes around the path so it doesn't get parsed into multiple command line arguments. – Hans Passant Jan 27 '15 at 17:55
  • @HansPassant sorry but your ball has fogged up! I specifically chose a path without spaces to eliminate that potential problem. – marcp Jan 27 '15 at 17:58
  • Okay, next problem is the 2nd argument. Note how it is LPTSTR, not LPCTSTR. In other words, CreateProcess needs to be able to write into that string buffer. It can't with yours, you passed a literal. It is definitely one of the most flaky winapi functions around, you should favor ShellExecuteEx() instead. – Hans Passant Jan 27 '15 at 18:01
  • 5
    @Mgetz That is not correct. From MSDN: If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line. – Benilda Key Jan 27 '15 at 18:02
  • 2
    @BenKey and there's the answer - the command line string needs the name of the program first, followed by the first parameter. In this case it looks like the command doesn't have *any* parameters. – Mark Ransom Jan 27 '15 at 18:07
  • @BenKey, that seems to be it. If I specify Depends as the appname and Depends AND Tee as the commandline it works! – marcp Jan 27 '15 at 18:07
  • @marcp Did you mean to achieve something like piping `depends.exe`'s output to `tee.exe`? – πάντα ῥεῖ Jan 27 '15 at 18:12
  • @πάνταῥεῖ no, I just used those as a test case, Depends (the dependency checker) takes a dll or exe as input. – marcp Jan 27 '15 at 18:14
  • @HansPassant I don't think your second comment is correct. CommandLine is a variable, it was just initialized with a string literal, there's no other way to initialize a string variable that I can think of offhand.If I used "L"c:\\utilities\\tee.exe" as the second parameter in the CreateProcessW call then I would be passing a string literal. – marcp Jan 27 '15 at 18:18
  • @πάνταῥεῖ and Raymon - I agree! I'm not sure of the protocol, should I delete my question? – marcp Jan 27 '15 at 19:19
  • @marcp _"should I delete my question?"_ No, leave it as is. It's upvoted and also has an accepted answer. Such kind of duplicates are well received, to tighten the available Q&A network on SO. Such is considered useful for future researchers on the topic. – πάντα ῥεῖ Jan 27 '15 at 19:23
  • 1
    @marcp: it's a pointer variable, though, which points to a string literal, and that's not allowed in CreateProcessW. You need to say `wchar_t Command[] = L"string";` (or equivalent) instead. – Harry Johnston Jan 28 '15 at 00:04
  • Thanks @HarryJohnston, I'm beginning to understand that. In my question I explained that the hardcoded values were temporary, so I absolve myself that way;). The million dollar question is whether it is guaranteed that the potential changes will never 'increase' the length of the variable. – marcp Jan 28 '15 at 16:11

1 Answers1

3

You must to supply the executable in the command

  • Appname should contain the full path to the executable
  • Command should contain also the argv[0]

if you want to open file t.txt with notepad than you can give as follows:

  • Appname = "c:/windows/notepad.exe";
  • command = "notepad c:/temp/t.txt";

You doesn't even must to supply the real program name, even fake name will do the job, since it is only a place holder.

like this: command = "fake c:/temp/t.txt";

now in notepad.exe:

  • argv[0] = "notepad";
  • argv[1] = "c:/temp/t.txt";

See this full example:

#include <Windows.h>
#include <iostream>

using namespace std;

int main(){
    STARTUPINFO si = {sizeof(STARTUPINFO), 0};
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = {0};
    LPTSTR AppName=L"C:/Windows/notepad.exe";
    wchar_t Command[] = L"notepad C:/Temp/t.txt"; 
    DWORD res = CreateProcess(AppName, Command, 0, 0, 0, CREATE_DEFAULT_ERROR_MODE, 0, 0, &si, &pi);
    if (res) {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        return 0;
    } else {
        cerr<<"error..."<<GetLastError()<<endl;
    }; 
    return 0;
}
marcp
  • 1,179
  • 2
  • 15
  • 36
SHR
  • 7,940
  • 9
  • 38
  • 57
  • The second parameter must be editable memory. Use `wchar_t Command[] = L"notepad C:/Temp/t.txt";` – David Heffernan Jan 27 '15 at 19:14
  • @DavidHeffernan, according to MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx) LPTSTR is the correct variable type. – marcp Jan 27 '15 at 20:33
  • @marcp Yes, but a literal is only `LPTSTR` out of backwards compatibility with K&R C. You aren't allowed to modify them. I'm the second person to tell you this. A cursory web search will find countless more people saying the same thing. We do actually know what we are talking about. – David Heffernan Jan 27 '15 at 20:37
  • 2
    @marcp Heck, disagree with me if you like, but do you really think that MSDN gets this wrong. It's writ large: *The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.* – David Heffernan Jan 27 '15 at 20:39
  • 1
    It's also a bad habit to use forward slashes in Windows. *Most* of the time the Windows API will convert them to backslashes for you, but not all applications accept them. – Harry Johnston Jan 28 '15 at 00:13