Introduction to CreateProcess()

Hi all,

I'm having a few problems understanding the syntax of using the CreateProcess() function. I searched the forums, and came up with this page:

http://msdn.microsoft.com/en-us/library/ms682425(v=vs.85).aspx

which has begun to clear up this question for me. I see it repeatedly on this forum, and it has been praised more than once as a great source for understanding the createProcess function, but I still have much to understand, even with the explanations given for the individual lines of the syntax code. I have been trying that syntax code out as a sort of "stencil" I guess, attempting to place the correct directory in the applicationname section, but not getting much of anywhere. I was hoping to find a sample program of this used somewhere in its most basic form to open up, say, the windows calculator.

The reason I have become so adamant on using the CreateProcess function is because as I understand it has the ability to close an application which it opens, a feature which ( also if I have gathered correctly) cannot be done using system(), which is ultimately what I am trying to do. (I have gotten system() to open up the program, but not close it)

I'm not an absolute beginner ( well, relative to the forum members, I am) but many key words I have seen while attempting to understand this function (handles, commandline console, etc) have sent me into a Wikipedia'ing frenzy, trying to understand each word while putting together the big picture. A little difficult, I think.

So, If I could fond the simplest running program which is able to use createProcess to open a program (and maybe a short explanation?) I may be a little closer. I can worry about the closing program issue after.

Oh, I'm running Visual C++ 2005 Express edition. Upon finding out I needed a software pack to include the windows.h library, I instead switched to VC++ 2008 Express.

Hopefully I'm not forgetting anything. Thank you tons for any help!
"A newbie's elementary guide to spawning processes"
http://www.codeproject.com/KB/system/newbiespawn.aspx

See the section "CreateProcess - an ultra brief introduction", it shows how to launch notepad.exe.

Note that using CreateProcess makes it easier to close an app, as you already have the process handle. But just calling TerminateProcess with the handle is not a nice thing to do to a process. It's something you use to force a misbehaving process to exit.
Hey Andy, thanks for the quick reply. That page looks promising, but I am still having issues with even getting the code to work.

If I attempt to compile:

int main(void)
{
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

STARTUPINFO StartupInfo; //This is an [in] parameter

ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field

if(CreateProcess("c:\\winnt\\notepad.exe", NULL,
NULL,NULL,FALSE,0,NULL,
NULL,&StartupInfo,&ProcessInfo))
{
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
}
else
{
MessageBox("The process could not be started...");
}

}

I get two errors:
error C2664: 'CreateProcessW' : cannot convert parameter 1 from 'const char [21]' to 'LPCWSTR'

and

error C2660: 'MessageBoxW' : function does not take 1 arguments


I have included windows.h, as well as winbase.h. The author did not include a not on that bit of code saying it was completely ready for compiling, but I took my chances, seeing as it was a beginner's guide, and the code seemed to have a path (notepad.exe).




Can that ShellExecute he talks about on the same page close a program as well? I saw something about "halting a program" until it closes, however I am having too many issue implementing the code to get and idea of what it can do. I placed his first line:

ShellExecute(this->m_hWnd,"open","calc.exe","","", SW_SHOW );

Along with #include <Shellapi.h>( I couldnt include shell32.lib, not sure why), but it kept returning an error about global functions not having "this" pointers, as well as an error about: left of '->m_hWnd' must point to class/struct/union/generic type. Not sure about those. Sorry if I got off topic with the last question there.

I forgot to add that that 'LPCWSTR' is a constant problem for me when I attempt to compile a createProcess function. Probably something very simple I'm missing about it, just thought I'd add that.
Last edited on
I am not sure if this will work, but try adding
TEXT(""c:\\winnt\\notepad.exe"") in place of "c:\\winnt\\notepad.exe"

I tried to understand it as well. Here is how I had done it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main ()
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	
	ZeroMemory( &si, sizeof(si) );
	si.cb = sizeof(si);
	ZeroMemory( &pi, sizeof(pi) );
	if (!
	CreateProcess
			(
			TEXT("F:\\C++\\C++ Programs\\Hangman\\Debug\\Hangman.exe"),
			NULL,NULL,NULL,FALSE,
			CREATE_NEW_CONSOLE,
			NULL,NULL,
			&si,
			&pi
			)
			)
		{
			cout << "Unable to execute.";
		}


This worked in Visual C++ Express.

[code]And Please use Code tags[/code]
Last edited on
Hmm... that puts me a little closer I think.... at least now I dont get build errors haha. But it wont open up the file I chose. since I didn't have an F: drive, I picked a file from c:

TEXT("c:\mozilla_firefox\firefox")

(I also tried double back slashes)

and put that in the application name area of your code. It ran, but always came back "unable to execute."

Either way, thank you. So far that has been the shortest example I have found, which I was able to run. Still don't have a clue what the things beforehand do (STARTUPINFO,PROCESSINFO, and the ZeroMemory stuff under it) but I can see where the syntax structure came into play and that the '&si' and '&pi' have something to do with it obviously, though it seems like you were able to skip a few parameters in the syntax statement, so that lost me a little.

Any idea why I may be running into that hiccup? It is a valid extension and application.

You could have fixed you're "LPCWSTR" problem by changing the "Character Set" in the "General" section of your project's properties from Unicode to Multi-Byte. The TEXT() or _T() macro allow you switch between UNICODE and ANSI builds, by #defining (or not) _UNICODE. BUT the TEXT() solution is probably better in this case.

(The new project wizard in older versions of VC++ gave you the choice, but they've dropped it at UNICODE is Windows preferrred character set. I have to admit that for small console, utility apps, I just switch the char set and code in ANSI.)

And yes, you always need to use \\ for a \

ZeroMemory does what it says. Here it's being used as a short hand way of setting all member of the structures to zero. In the case of STARTUPINFO, the cb member (count bytes) is then being set to its size (in bytes).

(The Windows API uses the cb trick a lot. It's how they spot which version of a structure you're using. New versions of a structure can have more members.)

Executable have a .exe extension in Windows. CreateProcess might be too stupid to realize you mean "firefox.exe" when you type just "firefox".

(At the command line, when you type just "firefox" then the system will try to run firefox.com, then firefox.exe, then firefox.bat, ... in the order of the extensions defined by the PATHEXT environment variable)

To get the error code when CreateProcess fails, call GetLastError() -- this is used by most of the WIN32 API which return a handle, pointer, BOOL, ...

You should have a good read of the comments for the CreateProcess in its MSDN entry!!

Andy


Last edited on
@IanD:
I hadn't skipped any Parameters. I just put NULL where-ever It was available as optional.
The error you are making is probably, that you missed the .exe extension (I did that too when I tried it out!).

And you will have to use \\ for slashes.

EDIT:
Ahh! Too late!!
Last edited on
And here's a helpful (?) code fragment. It gets the system's error message for a code. It's nicked from a piece of test code, so it's rather basic (doesn't even attempt to handle inserts). See MSDN entry for FormatMessage for further info.

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void DisplayError(LPCTSTR errorDesc, DWORD errorCode)
{
    TCHAR errorMessage[1024] = TEXT("");

    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM
                | FORMAT_MESSAGE_IGNORE_INSERTS
                | FORMAT_MESSAGE_MAX_WIDTH_MASK;

    FormatMessage( flags,
                   NULL,
                   errorCode,
                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                   errorMessage,
                   sizeof(errorMessage)/sizeof(TCHAR),
                   NULL );

#ifdef _UNICODE
    wcerr << L"Error : "   << errorDesc    << endl;
    wcerr << L"Code    = " << errorCode    << endl;
    wcerr << L"Message = " << errorMessage << endl;
#else
    cerr << "Error : "   << errorDesc    << endl;
    cerr << "Code    = " << errorCode    << endl;
    cerr << "Message = " << errorMessage << endl;
#endif
}


e.g.

1
2
3
4
5
6
7
8
    ...

    {
        DWORD errorCode = GetLastError();
        DisplayError(TEXT("Unable to execute."), errorCode);
    }

    ...

Last edited on
Thank you very much Andy, Nisheeth! The .exe extension fixed it!

Nisheeth, I see now where you put the optional parameters as null.

Andy, the '&' is a separate place in memory reserved for a pointer that points to 'si', correct? Why can you not just name the place in memory to be "zeroed out" itself?

And if the STARTUPINFO variable is nothing more than a pointer to STARTUPINFO structure , then why do you not have to include the STARTUPINFO structure? Is the STARTUPINFO structure "assumed" to be default, and does not have to be included? Some of its (many) variables seem frivolous, like the appearance of the window, but others, like window station or the handles (what ARE handles, exactly?32 bit unsigned values ? haha. I can see they have something to do with windows and that I believe every window is assigned a unique one to identify it) sound a little more important. Same issue with the PROCESSINFO structure.

Back to handles, this seems like a very important topic when creating (or exiting) an outside window, and the key that separates CreateProcess from System(). Is this correct?

oh, and this line:

si.cb = sizeof(si);

is use to determine which STARTUPINFO structure version you are using? I still have trouble understanding why you would need that, seeing as the 'si' variable is nothing more than a pointer, and the whole STARTUPINFO structure was never included.

Sorry I havent even gotten to the code you included for me about getting error messages, I really do appreciate it, and once I get this down I will take a longer look at it.

Already this forum has become invaluable. MSDN is great, and I have tried a few times to make sense of the comments section below createprocess, but many of the comments that might help me out have gone unanswered themselves.

I hope I'm not forgetting any of the questions I have, once again thank you VERY much.
#1 address-of operator &

& is the address-of operator. It's the reverse of the pointer dereference operator.

1
2
3
int i = 12;
int* p = &i;
cout << "value = " << *p << endl;


Has an output of:
value = 12


#2 STARTUPINFO

Yes, for most purpose you can leave the struct values as 0 for default behaviour.

#3 Handles

A handle is an opaque identifer to a whatever you want. In WIN32, these identifiers are 32 bit, but whether they are signed on not is irrelevant (though they are normally display in hex format (0 - 0xFFFFFFFF). Not only windows and files, but consoles, events, mutexes, named pipes, ... are identified by handles.

In C, the alternative would be to pass the address of a structure about, but that would mean making public the internal of the file system. Handles allow the information to stay hidden.

#4 system

I know very little about the system call, but I believe it communicates with cmd.exe, the command console. So when you use (e.g.) system("notepad.exe"); it's the same as if you have typed the same thing at the console. Then the console parses the string and then eventually calls CreateProcess. So it's better to call CreateProcess directly.

#5 sizeof and struct version

sizeof() returns the size of the data type or variable. A number of Windows API calls check the cb param to find out what size it is and hence which version of the Windows SDK it corresponds to.
Last edited on
Now I understand almost all of it! Thank you so much for the help!

I have one more question pertaining to the terminateProcess function, but I can mark this thread as answered immediately if it is better to open up another thread with this question.

so here's what I have so far. It compiles:


void wait ( int seconds )
{
clock_t endwait;
endwait = clock () + seconds * CLOCKS_PER_SEC ;
while (clock() < endwait) {}
}


int main ()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
HWND themozilla;
UINT change;
int n = 0;


ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (!
CreateProcess
(
TEXT("c:\\WINDOWS\\system32\\calc.exe"),
NULL,NULL,NULL,FALSE,
CREATE_NEW_CONSOLE,
NULL,NULL,
&si,
&pi
)
)
{
printf("Unable to execute.");
}
for (n= 150; n>0; n--)
{
printf ("%d\n",n);
wait (1);
}


themozilla = FindWindow(
NULL, TEXT("Calculator"));
if(themozilla == NULL)
printf("bad");

DWORD exitCode;
change = GetExitCodeProcess(themozilla,&exitCode);

ExitProcess(
change
);

return 0;
}



But does not close the window I have placed in the GetHandle function. I tried TerminateProcess as well, but haven't had much luck. I think it has something to do with the handle? Or exit code.


Hmm. Looks like it is the exitcode causing the issue. I just added a check to see if the exit code variable was returning a zero, and it was. What might be the cause?
Last edited on
You have to enable the right privilages before TerminateProcess will work. If you check the error code, it should access denied.

I would enum the windows to find the window, then post in a WM_CLOSE message.

- GetProcessId -> to get the process id from it's handle
- EnumWindows -> to enum the top level windows
- GetWindowThreadProcessId -> to get thread id from hwnd
- SendMessage

This basic sequence should work for notepad, calc, etc

But there's more work to handle more complicated apps. And it can't handle the popup that Notepad will display if it text is dirty.

Andy
closed account (DSLq5Di1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <Windows.h>
#include <tchar.h>

#include <iostream>
using namespace std;

int main ()
{
    STARTUPINFO si = {};
    si.cb = sizeof si;

    PROCESS_INFORMATION pi = {};
    const TCHAR* target = _T("c:\\WINDOWS\\system32\\calc.exe");

    if ( !CreateProcess(target, 0, 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        cout << "Waiting on process for 5 seconds.." << endl;
        WaitForSingleObject(pi.hProcess, 5 * 1000);
        /*
        if ( TerminateProcess(pi.hProcess, 0) ) // Evil
            cout << "Process terminated!";
        */
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    cin.sync();
    cin.ignore();

    return 0;
}
Oops! sloppy9's is a much nicer solution for the standard, basic case!

But it is kinder to send WM_CLOSE rather than WM_QUIT, as that allows apps to exit cleanly. WM_QUIT usually causes the message loop to exit immediately, so the app has no chance to process WM_DESTROY.

Andy

P.S. Note that the standard case will not work for a few evil apps:
- those that have multiple GUI threads (this is app dependent, but in some cases you need to send WM_CLOSE to all the threads. Each GUI thread will be running a message pump.)
- an app whose primary thread is not a GUI thread (I have encountered this only once in real life; a legacy console app which has a GUI added on a separate (GUI) thread, rather than using the usual primary GUI thread + worker thread).

P.P.S. I generally use WM_CLOSE and then wait on the process handle. Then, if the app doesn't close in a timely manner, I use TerminateProcess. As WM_QUIT (usually) terminates the loop immediately (if coded in the usual way) it's not so much different to just terminating the process (I am assuming here that the convention of setting up in WM_CREATE and tearing down in WM_DESTORY is followed, and that a lot of spurious stuff has not been added to WinMain.).
Last edited on
closed account (DSLq5Di1)
I see your point Andy and completely agree with you. ^^
Topic archived. No new replies allowed.