《Windows核心编程》学习笔记(5)– 创建进程,结束进程,创建独立的子进程

1.创建进程

BOOL CreateProcess(

  LPCWSTR pszImageName,

  LPCWSTR pszCmdLine,

  LPSECURITY_ATTRIBUTES psaProcess,

  LPSECURITY_ATTRIBUTES psaThread,

  BOOL fInheritHandles,

  DWORD fdwCreate,

  LPVOID pvEnvironment,

  LPWSTR pszCurDir,

  LPSTARTUPINFOW psiStartInfo,

  LPPROCESS_INFORMATION pProcInfo);

 

  pszImageName :是要执行进程的路径名.,但一般为NULL.

  pszCmdLine :当pszImageName 参数为NULL.时,CreateProcess会解析pszCommandLine字符串,它会检查字符串中的第一个标记(token ),并假定此标记是你想运行的执行体文件的名称。如果执行体文件的名称没有扩展名,就会默认是.exe扩展名

  CreateProcess还会按照以下顺序搜索执行体

1. 主调进程.EXE文件所在的目录

2. 主调进程的当前目录

3. Windows系统目录,即GetSystemDirectory返回的System32子文件夹

4. Windows 目录

  psaProcess, psaThread:参数为进程对象指定安全性,当参数为NULL时,系统为这两个内核对象指定默认的安全描述符。也可以分配并初始化两个SECURITY_ATTRIBUTES 结构。当这两个参数的指向一个SECURITY_ATTRIBUTES 结构,并且该结构的成员bInheritHandle被设为TRUE时,表明新创建进程的进程内核句柄和线程内核句柄是可以被由父进程将来生成的任何子进程继承。

  fInheritHandles:设置为TRUE时,表明现在正要创建的新进程可以继承父进程中所有可继承的句柄。

  fdwCreate:标识了影响新进程创建方式的标志。

  pvEnvironment:一般为NULL,但也可以使用GetEnvironmentStrings函数,

PVOID GetEnvironmentStrings();

来获得主调进程正在使用的环境字符串数据块的地址。但是当不再需要这个内存块的时候,应该调用FreeEnvironmentStrings函数来释放它:

BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock);

  pszCurDirNULL;

  psiStartInfo:参数指向一个STARTUPINFO结构或STARTUPINFOEX结构:

typedef struct _STARTUPINFO {

       DWORD cb;      //包含STARTUPINFO结构中的字节数。应用程序必须将cb初始化为 sizeof(STARTUPINFO) //sizeof(STARTUPINFOEX)

PSTR lpReserved;


PSTR lpDesktop;   //标识要在上面启动应用程序的桌面的名称。如果桌面已经存在,则新进程会与指定的桌面关联。

           //如果桌面不存在,则用指定的名称和默认的属性为新进程创建一个桌面。如果lpDesktopNULL(这是最为常见的),

           //进程就会与当前桌面关联。

PSTR lpTitle;     //指定控制台窗口的窗口标题。如果lpTitle被设置为NULL ,就将执行体文件的名称作为窗口标题。

DWORD dwX;

DWORD dwY;

DWORD dwXSize;

DWORD dwYSize;

DWORD dwXCountChars;

DWORD dwYCountChars;

DWORD dwFillAttribute;

DWORD dwFlags;

WORD wShowWindow;

WORD cbReserved2;

PBYTE lpReserved2;

HANDLE hStdInput;

HANDLE hStdOutput;

HANDLE hStdError;

    } STARTUPINFO, *LPSTARTUPINFO;

    typedef struct _STARTUPINFOEX {

STARTUPINFO StartupInfo;

struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;

} STARTUPINFOEX, *LPSTARTUPINFOEX;

Windows在创建新进程的时候使用这个结构的成员。大多数应用程序都希望生成的应用程序只是使用默认值。最起码要将此结构中的所有成员初始化为0,并将cb成员设为此结构的大小,如下所示:

STARTUPINFO si = { sizeof(si) };

CreateProcess(..., &si, ...);

如果没有把结构的内容清零,则成员将包含主调线程的堆栈上的垃圾数据。把这种垃圾传给

CreateProcess,会造成新进程有时能创建,有时则不能,具体取决于垃圾的内容。因此,必须 将这个结构中的未使用的成员清零,确保CreateProcess始终都能正常地工作。

  pProcInfo:参数指向一个PROCESS_INFORMATION结构,CreateProcess函数在返回之前,会初始化这个结构的成员。

typedef struct _PROCESS_INFORMATION {

    HANDLE hProcess;

    HANDLE hThread;

    DWORD dwProcessId;

    DWORD dwThreadId;

} PROCESS_INFORMATION;

 

2.终止进程

进程可以通过以下4种方式终止:

 1)主线程的入口函数返回(强烈推荐的方式)。

 2)进程中的一个线程调用ExitProcess函数(要避免这个方式)

 3)另一个进程中的线程调用TerminateProcess函数(要避免这个方式)

 4)进程中的所有线程都“自然死亡”(这是很难发生的)

 

一个进程终止时,会依次采取以下操作:

    1.进程中遗留的任何线程都被终止。

    2.进程分配的所有UserGDI对象都被释放,所有内核对象都被关闭(如果没有其他进程打开了它们的句柄,这些内核对象也会被销毁。不过,如果其他进程打开了到它们的句柄,这些内核对象就不会被销毁)。

    3.进程的退出代码从STILL_ACTIVE变为传给ExitProcessTerminateProcess 函数的代码。

    4.进程内核对象的状态变成signaled(关于信号机制的详情,请参见第9章)。这就是为什么系统中的其他线程可以挂起它们自己,直至进程终止运行。

    5)进程内核对象的使用计数递减1

 

Ps.1 进程在终止后绝对不会泄漏任何东西。

2erminateProcess函数是异步的——换言之,它告诉系统你希望进程终止,但到函数返回的时候,并不能保证进程已经被“杀死”了。所以,为了确定进程是否已经终止,应该调用WaitForSingleObject或者一个类似的函数,并将进程的句柄传给它。

3)许多应用程序无法正确清理它自己,都是因为显式调用了ExitProcessExitThread 的原因。在ExitThread的情况下,进程会继续运行,但可能泄漏内存或其他资源。而调用ExitProcess函数会结束进程,程序没有正确执行的清理工作会交给操作系统,故进程在终止后绝对不会泄漏任何东西。

 

3.运行独立的子进程

大多数时候,应用程序以一个“分离的进程”(detached process)的形式来启动另一个进程

这意味着一旦进程创建并开始执行,父进程就不再与新进程通信,或者不用等它完成工作之后才继续自己的工作。Windows资源管理器就是这样工作的。当Windows资源管理器为用户创建了一个新的进程之后,就不再关心这个进程是否继续存在,也不关心用户是否要终止它。为了放弃与子进程的所有联系,Windows资源管理器必须调用CloseHandle,关闭到新的进程及 其主线程的句柄。以下代码示例展示了如何新建一个进程,然后让它分离出去运行:

 

PROCESS_INFORMATION pi;

 

// Spawn the child process.

BOOL fSuccess = CreateProcess(..., &pi);

if (fSuccess) {

   // Allow the system to destroy the process & thread kernel

   // objects as soon as the child process terminates.

   CloseHandle(pi.hThread);

   CloseHandle(pi.hProcess);

}

  创建新进程可使系统建立一个进程内核对象和一个线程内核对象。在创建进程的时候,系统为每个对象赋予一个初始使用计数值1 。然后,在createProcess返回之前,该函数打开进程对象和线程对象,并将每个对象的与进程相关的句柄放入 PROCESS_INFORMATION结构的hProcess和hThread 成员中。当CreateProcess 在内部打开这些对象时,每个对象的使用计数就变为2 。
  这意味着在系统能够释放进程对象前,该进程必须终止运行(将使用计数递减为1),并且父进程必须调用CloseHandle(再将使用计数递减1,使之变为0)。同样,若要释放线程对象,该线程必须终止运行,父进程必须关闭线程对象的句柄。

原文地址:https://www.cnblogs.com/forlina/p/2132004.html