65.9K
CodeProject 正在变化。 阅读更多。
Home

初学者创建进程的入门指南

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (57投票s)

2002年2月18日

Ms-PL

5分钟阅读

viewsIcon

609637

对 ShellExecute/ShellExecuteEx、CreateProcess、WaitForSingleObject 的基本用法进行介绍。如何打开“查找”窗口、“属性”窗口。

引言

我们经常看到新手提问,如何启动一个新进程,如何用关联的程序打开一个特定的文档,如何启动一个网站,如何弹出邮件客户端的撰写窗口,如何等待一个程序完成,如何打印一个文件等等。其中很多都可以通过调用 CreateProcess 来实现。但不幸的是,新手们常常发现 CreateProcess 并不是那么容易理解。但对新手来说,好消息是你可以使用 shell32.dll 提供的一些 shell 函数来完成上述任务。

本文可以看作一个微型 FAQ,提供一些简单的代码片段,详细说明如何完成特定任务。你必须在 MSDN 上查阅此处讨论的函数。如果大家能发送更多类似的问题,我将非常高兴。文章最后我还对 CreateProcess 进行了极简的介绍。这种方法非常不全面,但我认为它能为新手提供一个简单的入门基础。

ShellExecute 及其作用

此函数声明在 Shellapi.h 中,你需要包含它。你还必须链接 Shell32.libShellExecute 是一个非常省力的 API 调用。我们将通过微型 FAQ 的形式介绍它的用法。在 MSDN 上查阅该函数,以了解每个参数的含义。

问:如何在 Windows 中启动一个应用程序?

答:很简单。只需调用 ShellExecute,传入要执行的文件名的完整路径。

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

你还可以像下面这样为应用程序传递参数:

ShellExecute(this->m_hWnd,"open","notepad.exe",
    "c:\\MyLog.log","",SW_SHOW );

如你所见,我没有传递程序的完整路径。如果你不指定完整路径,ShellExecute 会在 PATH 环境变量中查找。

问:我想使用与文档类型关联的程序来打开一个特定的文档。

答:你可以将文档的路径传递给 ShellExecuteShellExecute 会用其关联的程序打开文件。很简单吧?

ShellExecute(this->m_hWnd,"open",
    "c:\\abc.txt","","",SW_SHOW );

问:我想以编程方式在浏览器窗口中启动我的主页。

答:天哪!如果你知道这有多容易。只需将 URL 传递给 ShellExecute。我敢打赌你现在嘴边露出一丝苦笑,好像在说:“该死!我不相信。”

ShellExecute(this->m_hWnd,"open",
    "http://www.google.com","","", SW_SHOW );

问:如何启动默认电子邮件客户端的撰写窗口,并将我指定的地址作为收件人地址?

答:这次我们将一个 mailto 链接传递给 ShellExecute

ShellExecute(this->m_hWnd,"open",
    "mailto:nishinapp@yahoo.com","","", SW_SHOW );

问:老板要求我从程序中打印那个许可协议文本文件。

答:别担心,ShellExecute 在这里。[我希望我没有过度推销]

ShellExecute(this->m_hWnd,"print",
    "c:\\abc.txt","","", SW_HIDE);

问:我想在特定文件夹上打开 Windows 的“查找”窗口。

答:我们使用 find 动词作为操作参数,然后 Windows 的“查找”窗口就会以我们指定的目录作为根目录打开。如果你想让用户在某个文件夹中查找文件,这会非常方便。只需让他们指定文件夹,然后弹出一个以他们的文件夹为根目录的“查找”窗口。

ShellExecute(m_hWnd,"find","d:\\nish",
    NULL,NULL,SW_SHOW);

更强大的工具 - ShellExecuteEx

ShellExecuteEx 是一个更灵活的调用,它允许我们检索刚刚启动的程序的信息。你需要填充 SHELLEXECUTEINFO 结构,并将它的地址传递给 ShellExecuteEx。请在你的 MSDN 副本中查找这两个函数。

问:如何启动一个程序,并暂停当前程序的执行,直到该程序退出?

答:使用 ShellExecuteEx 启动程序,然后对进程句柄调用 WaitForSingleObject

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);

问:我想显示一个文件或文件夹的“文件/文件夹属性”窗口。

答:这次我们将 properties 作为操作动词传递。我们还需要在 SHELLEXECUTEINFO 结构的 fMask 参数中指定 SEE_MASK_INVOKEIDLIST。这就是为什么这里必须使用 ShellExecuteEx 而不是 ShellExecute。这个技巧要归功于 Microsoft MVP David Lowndes,因为正是 David 提供了关于 SEE_MASK_INVOKEIDLIST 标志的提示。

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 - 极简介绍

CreateProcess 函数属于 Kernel32.dll。Windows 使用此调用来创建一个新进程和新进程的主线程。主线程然后开始执行指定的程序。通常,如果这是一个 C++ 程序,执行将从 WinMain 开始 [实际上在此之前,CRT 库已被加载和初始化]。有关 CreateProcess 用法的更全面教程,我推荐阅读 Joseph M Newcomer 的文章 进程简介:异步进程通知

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...");
}

正如你所见,我正在对进程句柄使用 WaitForSingleObject。但由于 CreateProcess 除了创建进程对象外,还会创建线程对象,我也可以像这样等待线程句柄:

WaitForSingleObject(ProcessInfo.hThread,INFINITE);

但这可能会引起问题,如果由于某些原因,你的一个或多个辅助线程在主线程完成后仍然处于活动状态。MSDN 说,只有当所有线程都停止执行时,进程才会被完全终止。所以我建议你等待进程句柄而不是线程句柄。

最后更新

  • 2002 年 8 月 3 日 - 添加了打开“查找”窗口的技巧
  • 2002 年 8 月 3 日 - 添加了打开“属性”窗口的技巧
© . All rights reserved.