CreateProcess使用注意事项

  1. BOOL CreateProcessCreateProcessA(
  2.     __in_opt    LPCSTR lpApplicationName,
  3.     __inout_opt LPSTR lpCommandLine,
  4.     __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,
  5.     __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,
  6.     __in        BOOL bInheritHandles,
  7.     __in        DWORD dwCreationFlags,
  8.     __in_opt    LPVOID lpEnvironment,
  9.     __in_opt    LPCSTR lpCurrentDirectory,
  10.     __in        LPSTARTUPINFOA lpStartupInfo,
  11.     __out       LPPROCESS_INFORMATION lpProcessInformation
  12.     );
复制代码
CreateProcess在进程完全初始化好之前就返回TRUE。这意味着操作系统加载器(loader)尚未尝试定位所有必要的DLL。 如果一个DLL找不到或者不能正确初始化,进程就会终止。因为CreateProcess返回TRUE,所以父进程不会注意到任何初始化问题。

pszApplicationName和pszCommandLine参数分别指定新进程要使用的执行体文件的名称,以及要传给新进程的命令行字符串。先来谈谈pszCommandLine参数。注意,pszCommandLine参数被原型化为一个PTSTR。这意味着CreateProces期望你传入的是一个非“常量字符串”的地址。在内部,CreateProcess实际上会修改你传给它的命令行字符串。但在CreateProcess返回之前,它会将这个字符串还原为原来的形式。这是很重要的, 因为如果命令行字符串包含在你的文件映像的只读部分,就会引起访问冲突(违例)。例如,以下代码就会导致冲突,因为Microsoft的C/C++编译器把"NOTEPAD"字符串放在只读内存中
  1. STARTUPINFO si = { sizeof(si) };
  2. PROCESS_INFORMATION pi;
  3. CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL,FALSE, 0, NULL, NULL, &si, &pi);
复制代码
CreateProcess试图修改字符串时,会引起一个访问冲突(Microsoft C/C++编译器的早期版本把字符串放在可读/写内存中。所以对CreateProcess函数的调用不会引起访问冲突)。
解决这个问题的最佳方式是:在调用CreateProcess之前,把常量字符串复制到一个临时缓冲区,如下所示:
  1. STARTUPINFO si = { sizeof(si) };
  2. PROCESS_INFORMATION pi;
  3. TCHAR szCommandLine[] = TEXT("NOTEPAD");
  4. CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
复制代码
可以使用pszCommandLine参数来指定一个完整的命令行,供CreateProcess用于创建新进程。当CreateProcess解析pszCommandLine字符串时,它会检查字符串中的第一个标记(token),并假定此标记是你想运行的执行体文件的名称。如果执行体文件的名称没有扩展名,就会默认是.exe扩展名。 CreateProcess还会按照以下顺序搜索执行体。
1. 主调进程.EXE文件所在的目录
2. 主调进程的当前目录
3. Windows系统目录,即GetSystemDirectory返回的System32子文件夹
4. Windows目录
5. PATH环境变量中列出的目录
当然,假如文件名包含一个完整路径,系统就会利用这个完整路径来查找执行体,而不会搜索目录。如果系统找到了执行体文件,就创建一个新进程,并将执行体的代码和数据映射到新进程的地址空间。然后,系统调用由链接器设为应用程序入口的C/C++运行时启动例程。

dwCreationFlags参数标识了影响新进程创建方式的标志(flag)。多个标志可以使用按位OR运算符来组合。可用的标志如下。
DEBUG_PROCESS 标志向系统表明父进程希望调试子进程以及子进程将来生成的所有进程。该标志向系统表明,在任何一个子进程(现在的身份是被调试对象,或者说debugee)中发生特定的事件时,要通知父进程(现在的身份是调试器,或者说debugger)。
DEBUG_ONLY_THIS_PROCESS 标志类似于DEBUG_PROCESS,但是,只有在关系最近的子进程中发生特定事件时,父进程才会得到通知。如果子进程又生成了新的进程,那么在这些新进程中发生特定事件时,调试器是不会得到通知的。要进一步了解如何利用这两个标志来写一个调试器,并获取被调试应用程序中的DLL和线程的信息,请阅读MSDN的一篇文章:“Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities, Part 2”,网址是 http://msdn.microsoft.com/msdnmag/issues/02/08/EscapefromDLLHell/
CREATE_SUSPENDED 标志造成系统创建新进程,但会挂起其主线程。这样一来,父进程就可以修改子进程地址空间中的内存,更改子进程的主线程的优先级,或者在进程执行任何代码之前,将此进程添加到一个作业(job)中。父进程修改好子进程之后,可以调用 ResumeThread 函数来允许子进程执行代码。欲知这个函数的详情,请参见第7章。
DETACHED_PROCESS 标志阻止一个基于CUI(控制台用户界面)的进程访问其父进程的控制台窗口,并告诉系统将它的输出发送到一个新的控制台窗口,如果一个基于CUI的进程是由另一个基于CUI的进程创建的,那么在默认情况下,新进程将使用父进程的控制台窗口。(在命令提示符中运行C++编译器的时候,不会新建一个控制台窗口;输出将附加到现有控制台窗口的底部。)通过指定这个标志,新进程如果需要将输出发送到一个新的控制台窗口,就必须调用AllocConsole函数来创建它自己的控制台。
CREATE_NEW_CONSOLE 标志指示系统为新进程创建一个新的控制台窗口。如果同时指定 CREATE_NEW_CONSOLE DETACHED_PROCESS 标志,会导致一个错误。
CREATE_NO_WINDOW 标志指示系统不要为应用程序创建任何控制台窗口。可以使用这个标志来执行没有用户界面的控制台应用程序。
CREATE_NEW_PROCESS_GROUP 标志修改用户按Ctrl+C或Ctrl+Break时获得通知的进程列表。按下这些组合键时,假如有多个CUI进程正在运行,系统将通知一个进程组中的所有进程,告诉它们用户打算中断当前操作。创建一个新的CUI进程时,假如指定了这个标志,就会创建一个新的进程组。组中的一个进程处于活动状态时,一旦用户按下Ctrl+C或Ctrl+Break,系统就只是向这个组的进程发出通知。
CREATE_DEFAULT_ERROR_MODE 标志向系统表明新进程不会继承父进程所用的错误模式。(本章前面已经讨论了 SetErrorMode 函数。)
CREATE_SEPARATE_WOW_VDM 标志只有在你运行16位Windows应用程序时才有用。它指示系统创建一个单独的虚拟DOS机(Virtual DOS Machine,VDM),并在这个VDM上运行16位Windows应用程序。默认情况下,所有16位Windows应用程序都在一个共享的VDM中执行。在独立的VDM中运行的好处是,假如应用程序崩溃,它只需要杀死这个VDM,在其他VDM中运行的其他程序仍然能正常工作。另外,在独立的VDM中运行的16位Windows应用程序有独立的输入队列。这意味着假如一个应用程序暂时挂起,独立VDM中运行的应用程序仍然能接收输入。运行多个VDM的缺点在于,每个VDM都要消耗较多的物理内存。Windows 98在单独一个虚拟机中运行所有16位Windows应用程序——你不能覆盖这个行为。
CREATE_SHARED_WOW_VDM 标志只有在你运行16位Windows应用程序时才有用。默认情况下,所有16位Windows应用程序都在单独一个VDM中运行的,除非指定了 CREATE_SEPARATE_WOW_VDM 标志。不过,你也可以覆盖这个默认行为。办法是在注册表中将HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\WOW下 DefaultSeparateVDM 的值设为yes。在此之后,如果设置 CREATE_SHARED_WOW_VDM 标志,16位Windows应用程序就会在系统的共享VDM中运行。(修改了这个注册表设置后,必须重启电脑。)注意,为了检测在64位操作系统下运行的32位进程,你可以调用 IsWow64Process 函数,它的第一个参数是你要检测的进程的句柄,第二个参数则是指向一个Boolean值的指针;如果进程是在WOW64下运行,这个值就会设为TRUE;否则会设为FALSE。
CREATE_UNICODE_ENVIRONMENT 标志告诉系统子进程的环境块应包含Unicode字符。进程的环境块默认包含的是ANSI字符串。
CREATE_FORCEDOS 标志强制系统运行嵌入一个16位OS/2应用程序中的MS-DOS应用程序。
CREATE_BREAKAWAY_FROM_JOB 标志允许一个作业中的进程生成一个和作业无关的进程。(详情参见第5章。)
EXTENDED_STARTUPINFO_PRESENT 标志向操作系统表明一个STARTUPINFOEX结构被传给了psiStartInfo参数。

dwCreationFlags 参数还允许你指定一个优先级类(priority class)。不过,这样做没有多大必要,而且对于大多数应用程序,都不应该这样做——系统会为新进程分配一个默认的优先级类。下列表格展示了可能的优先级类。
优先级类 标志
Idle IDLE_PRIORITY_CLASS
Below normal BELOW_NORMAL_PRIORITY_CLASS
Normal NORMAL_PRIORITY_CLASS
Above normal ABOVE_NORMAL_PRIORITY_CLASS
High HIGH_PRIORITY_CLASS
Realtime REALTIME_PRIORITY_CLASS


这些优先级类决定了相对于其他进程的线程,这个进程中的线程的调度方式,详情参见第 7 章的“优先级的抽象视图”一节。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CreateProcess函数可以用来创建一个新的进程。以下是使用CreateProcess函数的步骤: 1. 包含Windows.h头文件。 2. 定义一个PROCESS_INFORMATION结构体和一个STARTUPINFO结构体。 PROCESS_INFORMATION结构体用来存储新进程的信息,如进程句柄和进程ID。 STARTUPINFO结构体用来指定新进程的属性,如窗口大小和标准输入输出的句柄。 3. 调用CreateProcess函数。 CreateProcess函数的第一个参数是应用程序的路径,第二个参数是命令行参数,第三个参数是安全属性,第四个参数是线程安全属性,第五个参数是指定是否创建一个新的控制台窗口,第六个参数是指定创建新进程的标志,第七个参数是指定环境变量,第八个参数是指定当前目录,第九个参数是STARTUPINFO结构体,第十个参数是PROCESS_INFORMATION结构体。 4. 关闭句柄。 关闭新进程的句柄和主线程的句柄。 以下是一个简单的使用CreateProcess函数的例子: ```c++ #include <Windows.h> #include <iostream> using namespace std; int main() { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // Start the child process. if (!CreateProcess(NULL, // No module name (use command line) (LPSTR)"notepad.exe", // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi) // Pointer to PROCESS_INFORMATION structure ) { cout << "CreateProcess failed" << endl; return 1; } // Wait until child process exits. WaitForSingleObject(pi.hProcess, INFINITE); // Close process and thread handles. CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; } ``` 这个例子会启动一个记事本程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值