您的位置:首页 > 其它

Windows创建进程winAPI-CreateProcess

2015-05-19 15:13 363 查看
如有转载,请注明出处:http://blog.csdn.net/embedded_sky


作者:super_bert@csdn
Windows环境下调用CreateProcess创建一个进程,操作系统会执行以下操作:

1.一个线程调用CreateProcess时,系统将创建一个进程内核对象,其初始使用计数为1.进程内核对象不是进程本身,而是操作系统用来管理这个进程的一个小型数据结构——可以把进程内核对象想象成由进程统计信息构成的一个小型数据结构。然后,系统为新进程创建一个虚拟地址空间,并将可执行文件(和所有必要的DLL)的代码及数据加载到进程的地址空间。

2.系统为新进程的主线程创建一个线程内核对象(其使用计数为1)。和进程内核对象一样,线程内核对象也是一个小型数据结构,操作系统用它来管理这个线程。这个主线程一开始就会执行C/C++运行时的启动例程,它是由链接器设为应用程序入口的,最终会调用应用程序WinMain,wWinMain、main或wmain函数。如果系统成功创建了新进程和主线程,CreateProcess将返回TRUE。

CreateProcess函数原型如下:

BOOL WINAPI CreateProcess(
_In_opt_    LPCTSTR               lpApplicationName,
_Inout_opt_ LPTSTR                lpCommandLine,
_In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_        BOOL                  bInheritHandles,
_In_        DWORD                 dwCreationFlags,
_In_opt_    LPVOID                lpEnvironment,
_In_opt_    LPCTSTR               lpCurrentDirectory,
_In_        LPSTARTUPINFO         lpStartupInfo,
_Out_       LPPROCESS_INFORMATION lpProcessInformation
);
参数

lpApplicationName:指定新进程要使用的可执行文件的名称(带全路径的exe文件名)。

lpCommandLine:指定要传给新进程的命令行字符串,lpCommandLine是一个非“常量字符串”的地址。在内部,CreateProcess实际上会修改我们传给它的命令行字符串。但在CreateProcess返回之前,它会将这个字符串还原为原来的样子;鉴于此,若命令行字符包含在文件映像的只读部分,就会引起访问违例。CreateProcess会检查字符串中的第一个标记,并假定此标记是我们想运行的可执行文件的名称。(当pszImageName
参数为NULL.时,CreateProcess会解析pszCommandLine字符串,它会检查字符串中的第一个标记(token),并假定此标记是你想运行的执行体文件的名称。如果执行体文件的名称没有扩展名,就会默认是.exe扩展名。)

lpProcessAttributes:进程内核对象安全属性,若为NULL,将指定默认的安全描述符;若为非NULL,当lpProcessAttributes->bInheritHandle为TRUE时,子进程将继承父进程中所有可继承的句柄;当lpProcessAttributes->bInheritHandle为FALSE时,子进程讲无法继承父进程中的任何句柄。

lpThreadAttributes:主线程内核对象安全属性,若为NULL,将指定默认的安全描述符;若为非NULL,当lpThreadAttributes->bInheritHandle为TRUE时,父进程的主线程内核对象将被继承;当lpThreadAttributes->bInheritHandle为FALSE时,父进程的主线程内核对象不可继承,不管传给lpProcessAttributes->bInheritHandle的值是多少。

bInheritHandles:指定子进程是否能继承父进程中可继承的内核对象,若为TRUE则可继承,若为FALSE则不可继承。

dwCreationFlags:标识了影响新进程创建方式的标志(flag)。多个标志可以使用按位或起来,以便同时指定多个标志组合。

IDLE_PRIORITY_CLASS:低

BELOW_NORMAL_PRIORITY_CLASS:标准

ABOVE_NORMAL_PRIORITY_CLASS:高于标准

HIGH_PRIORITY_CLASS:高

REALTIME_PRIORITY_CLASS:实时

DEBUG_PROCESS:父进程希望调试子进程以及子进程将来生成的所有进程

DEBUG_ONLY_THIS_PROCESS:只有在关系最近的子进程中发生特定事件时,父进程才会得到通知

CREATE_SUSPENDED:创建新进程的同时挂起其主线程。这样一来,父进程就可以修改子进程地址空间中的内存,更改子进程的主线程优先级,或者在进程执行任何代码之前,将此进程添加到一个作业(job)中。父进程修改好子进程之后,可以调用ResumeThread函数来允许子进程执行代码

DETACHED_PROCESS:阻止一个基于CUI的进程访问其父进程的控制台窗口,并告诉系统将它的输出发送到一个新的控制台窗口,如果一个基于CUI的进程是由另一个基于CUI的进程创建的,那么默认情况下,新进程将使用父进程的控制台窗口

CREATE_NEW_CONSOLE:为新进程创建一个新的控制台窗口。如果同时指定DETACHED_PROCESS和CREATE_NEW_CONSOLE,会导致一个错误

CREATE_NO_WINDOWS:不要为应用程序创建人和控制台窗口。可以使用这个标志来执行没有用户界面的控制台应用程序

CREATE_NEW_PROCESS_GROUP:修改用户按Ctrl+C或Ctrl+Break时获得通知的进程列表

CREATE_DEFAULT_ERROR_MODE:新进程不会进程父进程的错误模式而使用默认的错误模式

CREATE_UNICODE_ENVIRONMENT:子进程的环境块应包含Unicode字符。进程的环境块默认包含的是ANSI字符串

CREATE_FORCEDOS:强制系统运行一个嵌入在16位OS/2应用程序中的MS-DOS应用程序

CREATE_BREAKAWAY_FROM_JOB:标志允许一个作业中的进程生成一个和作业无关的进程

EXTENDED_STARTUPINFO_PRESENT:标志向操作系统表明传给lpStartupInfo参数的是一个STARTUPINFOEX结构

fdwCreate参数还允许我们指定一个优先级类(priority class),对大多数程序都不应该这样做——系统会为新进程分配一个默认的优先级类,可指定的优先级如下(由低到高):

lpEnvironment:指向一块内存,其中包含新进程要使用的环境字符串。一般为NULL,但也可以使用GetEnvironmentStrings函数,PVOID
GetEnvironmentStrings();来获得主调进程正在使用的环境字符串数据块的地址。但是当不再需要这个内存块的时候,应该调用FreeEnvironmentStrings函数来释放它:BOOL FreeEnvironmentStrings(PTSTRpszEnvironmentBlock)。

lpCurrentDirectory:子进程的当前驱动器和目录。

lpStartupInfo:指向一个STARTUPINFO或STARTUPINFOEX结构,通常情况下是使用默认即可(将所有成员初始化为0,并将cb成员设为此结构的大小)。

lpProcessInformation:[out],指向一个PROCESS_INFORMATION结构,函数在返回之前,会初始化这个结构体。如前所述,创建一个新的进程,会导致系统创建一个进程内核对象和一个线程内核对象。在创建时,系统会为每个对象指定一个初始的使用计数1.然后,在CreateProcess返回之前,它会使用完全访问权限打开进程对象和线程对象,并将与之相关的句柄放入lpProcessInformation的hProcess和hThread成员中。当CreateProcess在内部打开这些对象时,每个对象的使用计数变为2。这意味着系统要想释放进程对象,进程必须终止(使用计数递减1),而且父进程必须调用CloseHandle(使用计数再次递减1,变为0)。类似地,要想释放线程对象,线程必须终止而且父进程必须关闭到线程对象的句柄。

返回值

CreateProcess在进程完全初始化好之前就返回TRUE。这意味着操作系统加载程序(loader)尚未尝试定位所有必要的DLL。如果一个DLL找不到或者不能正确初始化,进程就会终止。因为CreateProcess返回TRUE,所以父进程不会不会注意到任何初始化问题。

若失败则返回FALSE。

注意事项

传入参数:

typedef struct _STARTUPINFO {
<span style="font-family:宋体;"> </span>DWORD cb;      //包含STARTUPINFO结构中的字节数。应用程序必须将cb初始化为 sizeof(STARTUPINFO) //或sizeof(STARTUPINFOEX)。
<span style="font-family:宋体;"> </span>PSTR lpReserved;
<span style="font-family:宋体;"> </span>PSTR lpDesktop;   //标识要在上面启动应用程序的桌面的名称。如果桌面已经存在,则新进程会与指定的桌面关联。
<span style="font-family:宋体;"> </span>//如果桌面不存在,则用指定的名称和默认的属性为新进程创建一个桌面。如果lpDesktop为NULL(这是最为常见的),
<span style="font-family:宋体;"> </span>           //进程就会与当前桌面关联。
<span style="font-family:宋体;"> </span>PSTR lpTitle;<span style="font-family:宋体;"> </span>//指定控制台窗口的窗口标题。如果lpTitle被设置为NULL ,就将执行体文件的名称作为窗口标题。
<span style="font-family:宋体;"> </span>DWORD dwX;
<span style="font-family:宋体;"> </span>DWORD dwY;
<span style="font-family:宋体;"> </span>DWORD dwXSize;
<span style="font-family:宋体;"> </span>DWORD dwYSize;
<span style="font-family:宋体;"> </span>DWORD dwXCountChars;
<span style="font-family:宋体;"> </span>DWORD dwYCountChars;
<span style="font-family:宋体;"> </span>DWORD dwFillAttribute;
<span style="font-family:宋体;"> </span>DWORD dwFlags;
<span style="font-family:宋体;"> </span>WORD wShowWindow;
<span style="font-family:宋体;"> </span>WORD cbReserved2;
<span style="font-family:宋体;"> </span>PBYTE lpReserved2;
<span style="font-family:宋体;"> </span>HANDLE hStdInput;
<span style="font-family:宋体;"> </span>HANDLE hStdOutput;
<span style="font-family:宋体;"> </span>HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;

typedef struct _STARTUPINFOEX {
<span style="font-family:宋体;"> </span>STARTUPINFO StartupInfo;
<span style="font-family:宋体;"> </span>struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;
} STARTUPINFOEX, *LPSTARTUPINFOEX;
Windows在创建新进程的时候使用这个结构的成员。大多数应用程序都希望生成的应用程序只是使用默认值。最起码要将此结构中的所有成员初始化为0,并将cb成员设为此结构的大小,如下所示:

STARTUPINFO si = {sizeof(si) };

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

如果没有把结构的内容清零,则成员将包含主调线程的堆栈上的垃圾数据。把这种垃圾传给CreateProcess,会造成新进程有时能创建,有时则不能,具体取决于垃圾的内容。因此,必须将这个结构中的未使用的成员清零,确保CreateProcess始终都能正常地工作。

以下是CreateProcess返回时输出的结构体lpProcessInformation

typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
  HANDLE hThread;
 DWORD dwProcessId;
  DWORD dwThreadId;
} PROCESS_INFORMATION;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  winapi CreateProcess