Introduction
Often, we find questions coming from newbies on how to spawn a new process,
how to open a particular document with the program associated with it, how to
launch a web site, how to bring up the mail client's compose window, how to wait
for a program to finish, how to print a file etc. Quite a few of these can be
accomplished using the CreateProcess
call. Unfortunately newbies often find that
CreateProcess
is not exactly the easiest of calls to understand. But the very
good news for newbies is that you can use some of the shell functions available
through shell32.dll to accomplish the above jobs.
This article is a sort of miniature FAQ that gives you short code snippets
that detail how a particular task can be done. You must look up the functions
discussed here on MSDN. And I'd be glad if people can send in more questions on
similar lines that might fit in here. I also give an ultra brief introduction to
CreateProcess
at the end of the article. It is highly insufficient an approach,
but I do think it will serve a newbie as an easy stepping block.
ShellExecute and what it does for you
This function is declared in Shellapi.h which you will need to include. You
will also have to link with Shell32.lib. ShellExecute
is one of the API calls
that really saves you a lot of effort. We'll go through the usage of the
function through the mini FAQ format. Look up the function on MSDN to get an
idea of what each of the parameters mean.
Q: How do I start an application under windows?
A: Simple. Just call ShellExecute
, passing the full path of the file name you
want to execute.
ShellExecute(this->m_hWnd,"open","calc.exe","","", SW_SHOW );
You can even pass parameters to the application as shown below :-
ShellExecute(this->m_hWnd,"open","notepad.exe",
"c:\\MyLog.log","",SW_SHOW );
As you can see, I haven't passed the full path of the programs. ShellExecute
will look into the PATH environment variable if you don't specify the full path.
Q: I want to open a particular document using the program associated with
that document type.
A: You can pass the path of a document to ShellExecute
and ShellExecute
will
open the file in it's associated program. Easy, huh?
ShellExecute(this->m_hWnd,"open",
"c:\\abc.txt","","",SW_SHOW );
Q: I want to launch my home page in a browser window programmatically.
A: Boy! If you only knew how easy this is. Simply pass the URL to ShellExecute
.
I bet you have a caustic smile on your lips now as if to say, "Damn! I
don't believe this."
ShellExecute(this->m_hWnd,"open",
"http://www.google.com","","", SW_SHOW );
Q: How do I launch the compose window of the default email client with an
address I specify as the To: address?
A: This time we pass a mailto link to ShellExecute
.
ShellExecute(this->m_hWnd,"open",
"mailto:nishinapp@yahoo.com","","", SW_SHOW );
Q: My boss asked me to print that license-agreement text file from the
program.
A: Have no fear, ShellExecute
is here. [I hope I am not over-doing this]
ShellExecute(this->m_hWnd,"print",
"c:\\abc.txt","","", SW_HIDE);
Q: I want to bring up the Windows Find window on a particular folder.
A: We use the find
verb as the operation parameter and we have
the Windows Find window open up with the directory we have specified. This can
be rather handy if you want to allow users to find some file within some folder.
Just ask them for their folder and pop up a Find Window which has their folder
as the root folder.
ShellExecute(m_hWnd,"find","d:\\nish",
NULL,NULL,SW_SHOW);
Big Brother - ShellExecuteEx
ShellExecuteEx
is a more flexible call, in that it allows us to retrieve
information about the program we just spawned. You'll need to fill up the
SHELLEXECUTEINFO
structure and pass it's address to ShellExecuteEx
. Please
lookup both on your copy of MSDN.
Q: How do I start a program, and halt execution of my current program, till
that program exits?
A: You start the program using ShellExecuteEx
and use WaitForSingleObject
on
the process handle.
SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = "c:\\MyProgram.exe";
ShExecInfo.lpParameters = "";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess,INFINITE);
Q: I want to show the File or Folder properties window for a file or a
folder.
A: This time what we do is to pass properties
as the operation
verb. We also need to specify SEE_MASK_INVOKEIDLIST
as the
fmask
parameter of the SHELLEXECUTEINFO
structure. That's
why we have to use ShellExecuteEx
here instead of
ShellExecute
. I owe this tip to David Lowndes, Microsoft MVP because it
was David who helped me with that tip about the SEE_MASK_INVOKEIDLIST
flag.
SHELLEXECUTEINFO ShExecInfo ={0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_INVOKEIDLIST ;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = "properties";
ShExecInfo.lpFile = "c:\\"; //can be a file as well
ShExecInfo.lpParameters = "";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
CreateProcess - an ultra brief introduction
The CreateProcess
function is part of Kernel32.dll. Windows uses this call to
create a new process and a primary thread for the new process. The primary
thread then starts executing the specified executable. Normally, if this is a
C++ program, execution starts with your WinMain
[actually prior to this
the CRT library is loaded and initialized]. For a more comprehensive tutorial on
the use of CreateProcess
, I recommend that you read Joseph M Newcomer's article,
An Introduction to Processes: Asynchronous Process Notification.
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ;
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...");
}
As you can observe, I am using WaitForSingleObject
on the process handle. But
since CreateProcess
creates a thread object in addition to the process object, I
might as well have waited on the thread handle as in :-
WaitForSingleObject(ProcessInfo.hThread,INFINITE);
This might cause problems though if, for some reasons, one or more of your
secondary threads are still active even after the main thread finishes. MSDN
says that a process is fully terminated only when all it's threads have ceased
execution. So I recommend that you wait on the process handle rather than on the
thread handle.
Last Updates
- Aug 03 2002 - Added tip to bring up the Find window
- Aug 03 2002 - Added tip to bring up the Properties window