YY菌 发表于 2024-12-27 17:55:17

【Win32API】关于CreateProcess的第二个参数的研究

本帖最后由 YY菌 于 2024-12-27 17:52 编辑

原帖来源:【Win32API】关于CreateProcess的第二个参数的研究 https://www.0xaa55.com/thread-27526-1-1.html (出处: 技术宅的结界)

  在学Win32API编程的过程中肯定少不了创建子进程的需求,以前在学VB6的时候就会用到Shell函数来创建子进程,到了学VC的时候就需要手动调用WinAPI来实现了,这个时候我们最开始发现和VB6的Shell类似的自然是WinExec函数(连参数都跟VB6的Shell“一样”)。随着需求的进一步增加又会发现WinExec的功能不够用了(比如VB6的Shell至少会返回进程id,而WinExec返回的却是错误代码,还有就是WinExec只有ANSI版没有Unicode版),这时就会接触到一个更加高级的API函数CreateProcess,可以发现WinExec的第一个参数对应CreateProcess的第二个参数,WinExec的第二参数则是对应STARTUPINFO结构体中的wShowWindow字段。最开始大家都是先从ANSI编程开始的按照WinExec的习惯去使用CreateProcessA没有任何问题,后来开始学习Unicode编程了用到CreateProcessW就发现不对劲了(程序产生了0xC0000005异常),新手时期大多数人都会一脸懵逼。

  在后面有了一定技术水平之后,就会知道0xC0000005异常是内存权限错误,而CreateProcessW的第二个参数的类型是LPWSTR(并且标记了__inout_opt),也就是说这个参数是会被CreateProcessW修改的,于是试了一下先把字符串常量赋值给字符数组变量再传给CreateProcessW果然就OJBK了(提示:在旧版C艹标准中,const wchar_t *和const wchar_t []的都是不能赋值给wchar_t *变量的,但是 L"字符串常量" 却可以赋值给wchar_t *这就是导致CreateProcessW坑到一大堆新手的罪魁祸首)。当第一次了解到这里的时候直接就被震惊了,甚至感觉很不科学,明明在CreateProcessW返回后的这个参数的内容和传入之前一模一样,为啥就被修改了呢?既然修改了返回后的内容还是一样,那修改这个参数的意义是啥呢?同样是 __inout_opt LPSTR 参数的CreateProcessA为啥却没有这个问题?随着一系列的疑问下来,便决定对CreateProcess的这第二个参数好好研究。

  根据MSDN的描述来看,当第一个参数lpApplicationName为nullptr时,会根据第二个参数lpCommandLine中的第一个命令行来生成lpApplicationName,看到这里还暂时看不出来为啥跟lpCommandLine参数被修改有啥关系,接下来再看如果lpCommandLine参数的第一个命令行不是绝对路径的话,它会按搜素路径顺序去查找对应可执行文件(1.当前进程的主exe安装路径,2.当前进程的工作目录,3.GetSystemDirectory返回的目录,4. GetWindowsDirectory返回的目录,5.Path环境变量对应的目录),到这里绝对大部对C/C艹语言或WinAPI不熟悉的开发者多半会觉得不就是查找文件嘛,跟lpCommandLine被修改有啥关系?这时我就需要提示一下,在C语言标准中字符串都是以\0为结束标记的,而lpCommandLine是包含多个命令行参数组合的字符串,每个命令行参数之间都是使用空格来分割的(如果参数中本身包含空格则需要带上双引号,如果参数本身也包含双引号那就需要连续两个双引号转义一个双引号),这时是不是就会想到lpCommandLine中的第一个命令行参数大概率不会是\0结尾(除非只有一个参数且没带双引号的情况)。所以,CreateProcessW是不是需要先把可执行文件名结尾处的空格或双引号先临时改成\0才能实现对文件的搜索?至于最后调用完后返回的内容没有变,这是因为在搜索完可执行文件的全路径后,再最终创建进程的时候要把原始的命令行参数传给子进程,所以必须得改回去。经过测试发现,当我们正确传入非空lpApplicationName参数时,再给lpCommandLine直接传字符串常量,这时调用CreateProcessW就没有任何问题,这也更加进一步验证了lpCommandLine被修改问题的关键就在这个路径搜索上。

  关于CreateProcessW的原理是研究明白了,但大家是不是还有很多疑惑?为啥CreateProcessA的lpApplicationName为nullptr给lpCommandLine传常量却没有这个问题呢?难道CreateProcessA不走路径搜索吗?答案显然不是,因为在Win32中kernel32.dll这层API函数的ANSI版绝大多数都是内部做临时编码转换,然后再调用的Unicode版实现。也就是说CreateProcessA会把ANSI字符集的lpCommandLine在内存中临时转换成Unicode字符集的副本,然后再调用CreateProcessW,这样CreateProcessW在处理路径搜索时修改的就是在CreateProcessA分配的临时内存上(这个内存是一定具有写入权限的,不然转换出来的Unicode字符串也没法写进去),而不是CreateProcessA本来接收的lpCommandLine参数。因此CreateProcessA永远不会修改用户传入的参数,自然也就不会有这个问题。在最新版的MSDN文档中巨硬已经补充了一句“此函数的 Unicode 版本(CreateProcessW)可以修改此字符串的内容。 因此,此参数不能是指向只读内存的指针(例如 常量 变量或文本字符串)。 如果此参数是常量字符串,则函数可能会导致访问冲突。”

  自此,我们现在已经完全搞明白了CreateProcess的第二个参数lpCommandLine神奇现象的原理,下面就来模仿一下其内部实现(这里会用到一个冷门API函数SearchPathW):

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <malloc.h>

#ifdef UNICODE
#define MyCreateProcessMyCreateProcessW
#else
#define MyCreateProcessMyCreateProcessA
#endif // !UNICODE

// Unicode 版实现
EXTERN_C DECLSPEC_NOINLINE BOOL WINAPI MyCreateProcessW(__in_opt LPCWSTR lpApplicationName, __inout_opt LPWSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCWSTR lpCurrentDirectory, __in LPSTARTUPINFOW lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation)
{
        // 先判断第一个参数为空且第二个参数不为空的情况(如果成立则从lpCommandLine中取第一个命令行参数来搜索可执行文件)
        if (!lpApplicationName && lpCommandLine) {
                // 定义两个变量分别标记lpCommandLine中可执行文件的起始位置和结束位置
                LPWSTR app4cl = lpCommandLine, appec;
                // 如果lpCommandLine的第一个字符不是双引号,那么就以空格结尾,否则就以双引号结尾。
                if (L'\"' != *app4cl) appec = wcschr(app4cl, L' ');
                else appec = wcschr(++app4cl, L'\"');
                // 如果找到正确位置需要临时修改成\0结束标记,否则SearchPathW会出错,没有找到则说明lpCommandLine中没有多个参数(刚好本身就有\0结尾)。
                if (appec) *appec = L'\0';
                // 开始用从lpCommandLine中取到的文件名搜索对应的.exe文件(SearchPathW的第一个参数传nullptr表示使用系统默认的搜索路径规则)
                DWORD len = SearchPathW(nullptr, app4cl, L".exe", 0, nullptr, nullptr);        // 在不接收搜索结果的情况下函数会返回包括\0的长度
                if (len) {        // 搜索成功则分配内存并获取结果
                        // 一般来说可执行文件路径都会被限制在MAX_PATH长度,不会太长这里就偷懒从堆栈上来分配临时内存了。
                        (void*&)lpApplicationName = _alloca(len << 1);        // 注意:内存的分配都是按字节大小来算长度的,所以Unicode文本长度需要 * 2。
                        // 分配好缓冲区内存之后就可以开始接收搜索结果了(提示:现在返回的长度是不包括\0的了)
                        len = SearchPathW(nullptr, app4cl, L".exe", len, LPWSTR(lpApplicationName), nullptr);
                }
                // 搜索完成后就把被临时修改的lpCommandLine给改回去(由于确定只会有双引号和空格两种情况,所以我这里偷懒了没有保存原始的字符)
                if (appec) *appec = app4cl != lpCommandLine ? L'\"' : L' ';
                // 如果搜索失败则返回失败(SearchPathW内部会设置LastError,不用自己再设置)
                if (!len) return FALSE;
        }
        // 假设存在的一个内部CreateProcess函数(由于这里仅研究第二个参数lpCommandLine,不考虑其它细节,因此放弃了使用RtlCreateUserProcess来实现的想法)
        return CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
}

// ANSI 版实现
EXTERN_C DECLSPEC_NOINLINE BOOL WINAPI MyCreateProcessA(__in_opt LPCSTR lpApplicationName, __inout_opt LPSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCSTR lpCurrentDirectory, __in LPSTARTUPINFOA lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation)
{
        // 字符串参数的索引常量
        enum { AppLen, CmdLen, CurLen, DskLen, TitLen, LensCount, };
        // Unicode 缓冲区
        LPSTARTUPINFOEXW psiex = nullptr;
        __try {
                int srcls, dstls;
                // 计算所有ANSI参数的完整长度(含\0,因此nullptr长度为0 ""则为1)
                srcls = lpApplicationName ? lstrlenA(lpApplicationName) + 1 : 0;
                srcls = lpCommandLine ? lstrlenA(lpCommandLine) + 1 : 0;
                srcls = lpCurrentDirectory ? lstrlenA(lpCurrentDirectory) + 1 : 0;
                srcls = lpStartupInfo->lpDesktop ? lstrlenA(lpStartupInfo->lpDesktop) + 1 : 0;
                srcls = lpStartupInfo->lpTitle ? lstrlenA(lpStartupInfo->lpTitle) + 1 : 0;
                *dstls = ((max(sizeof(*lpStartupInfo), lpStartupInfo->cb) + 3) >> 2) << 1;
                // 计算所有ANSI参数转换为Unicode之后的完全长度(含\0,算的字符个数不是字节数)
                dstls = lpApplicationName ? MultiByteToWideChar(CP_ACP, 0, lpApplicationName, srcls, nullptr, 0) : 0;
                dstls = lpCommandLine ? MultiByteToWideChar(CP_ACP, 0, lpCommandLine, srcls, nullptr, 0) : 0;
                dstls = lpCurrentDirectory ? MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, srcls, nullptr, 0) : 0;
                dstls = lpStartupInfo->lpDesktop ? MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpDesktop, srcls, nullptr, 0) : 0;
                dstls = lpStartupInfo->lpTitle ? MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpTitle, srcls, nullptr, 0) : 0;
                // 累加所有长度,以便后期使用的复杂度从On降至O1
                for (int i = 0; i < LensCount;) { register auto cur = dstls; dstls[++i] += cur; }
                // 使用总长度分配缓冲区内存(这次的参数可能会特别长,特别是lpCommandLine最大长度为32767,因此最好从堆上分配)
                (LPVOID&)psiex = LocalAlloc(LMEM_FIXED, dstls << 1);
                if (!psiex) __leave;
                // 拷贝StartupInfo结构体(STARTUPINFOA和STARTUPINFOW的结构体完全一致,只是需要将其中的LPSTR字段替换成LPWSTR字段)
                CopyMemory(psiex, lpStartupInfo, max(sizeof(*lpStartupInfo), lpStartupInfo->cb));
                // 转换所有的ANSI参数为Unicode,并按条件替换
                if (lpApplicationName) MultiByteToWideChar(CP_ACP, 0, lpApplicationName, srcls, LPWSTR(psiex) + dstls, dstls - dstls);
                if (lpCommandLine) MultiByteToWideChar(CP_ACP, 0, lpCommandLine, srcls, LPWSTR(psiex) + dstls, dstls - dstls);
                if (lpCurrentDirectory) MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, srcls, LPWSTR(psiex) + dstls, dstls - dstls);
                if (lpStartupInfo->lpDesktop) MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpDesktop, srcls, psiex->StartupInfo.lpDesktop = LPWSTR(psiex) + dstls, dstls - dstls);
                if (lpStartupInfo->lpTitle) MultiByteToWideChar(CP_ACP, 0, lpStartupInfo->lpTitle, srcls, psiex->StartupInfo.lpTitle = LPWSTR(psiex) + dstls, dstls - dstls);
                //转换完成后调用对应的 Unicode 版实现就行了
                return MyCreateProcessW((lpApplicationName ? LPCWSTR(psiex) + dstls : 0), (lpCommandLine ? LPWSTR(psiex) + dstls : 0), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, (lpCurrentDirectory ? LPCWSTR(psiex) + dstls : 0), &psiex->StartupInfo, lpProcessInformation);
        }
        __finally {
                // 再离开作用域后需要释放从堆中分配的内存
                if (psiex) LocalFree(psiex);
        }
        return FALSE;
}

// WinExec 实现
EXTERN_C DECLSPEC_NOINLINE UINT WINAPI MyWinExec(__in LPCSTR lpCmdLine, __in UINT uCmdShow)
{
        // 等待超时时长(WinExec最长等待30秒,ShellExecuteEx则是60秒,CreateProcess则不会做任何等待)
        static const DWORD TIMEOUT = 30000;
        PROCESS_INFORMATION processInfo;
        // 启动参数设置(WinExec只有用到wShowWindow字段)
        STARTUPINFOA startupInfo = { sizeof(startupInfo), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, STARTF_USESHOWWINDOW, WORD(uCmdShow), };
        // 调用CreateProcessA(由于CreateProcessA内部永远不会修改到lpCommandLine,所以直接强转LPSTR传递是安全的)
        if (MyCreateProcessA(nullptr, LPSTR(lpCmdLine), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo)) {
                // 关闭线程句柄(用不上)
                CloseHandle(processInfo.hThread);
                // 等待子进程GUI初始化完成(以第一次把消息队列清空为条件)
                WaitForInputIdle(processInfo.hProcess, TIMEOUT);
                // 关闭进程句柄(用完了)
                CloseHandle(processInfo.hProcess);
                // 返回成功
                return HINSTANCE_ERROR + TRUE;
        }
        // 失败则获取错误码
        DWORD dwErr = GetLastError();
        // 转换错误码
        switch (dwErr)
        {
        case ERROR_FILE_NOT_FOUND:        // 找不到可执行文件
        case ERROR_PATH_NOT_FOUND:        // 无效的文件路径
                break;
        case ERROR_BAD_EXE_FORMAT:        // 文件不是EXE格式
                dwErr = ERROR_BAD_FORMAT;
                break;
        default:        // 其它错误状态都是返回0
                dwErr = FALSE;
        }
        return UINT(dwErr);
}

压缩包密码没有密码:lol ,如果有那就在隐藏内容中(回帖可见) )
**** Hidden Message *****

美俪女神 发表于 2024-12-28 07:35:57

如果有任何关于WIN32API的疑问(包括内核API),都可以直接参考泄露的WINXP源码。

可爱的小猪 发表于 2024-12-27 19:11:55

膜拜大佬技术贴
兴奋的进来,一脸懵逼的出去。。。

gujin163 发表于 2024-12-30 15:08:07

啥也不说了,帖子就是带劲!

YY菌 发表于 2025-1-2 18:45:59

美俪女神 发表于 2024-12-28 07:35
如果有任何关于WIN32API的疑问(包括内核API),都可以直接参考泄露的WINXP源码。 ...

找到XP源码了,它内部确实是使用的临时修改lpCommandLine参数然后SearchPathW搜索的exe文件,搜完后再修改回来,最后再真正创建进程:


BOOL
WINAPI
CreateProcessInternalW(
    HANDLE hUserToken,
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation,
    PHANDLE hRestrictedUserToken
    )

/*++

Routine Description:

    This is the worker routine for CreateProcess and CreateProcessAsUser.
    CreateProcessAsUser supplies a User token to be stamped on the new process.
    CreateProcess supplies NULL and the current process token is used.

    A process and thread object are created and a handle opened to each
    object using CreateProcessInternal.Note that WinExec and LoadModule are
    still supported, but are implemented as a call to CreateProcess.

Arguments:

    hUserToken - Supplies an optional token handle to be set on the new process.
      Process token is used is this parameter is NULL.

    lpApplicationName - Supplies an optional pointer to a null terminated
      character string that contains the name of the image file to
      execute.This is a fully qualified DOS path name.If not
      specified, then the image file name is the first whitespace
      delimited token on the command line.

    lpCommandLine - Supplies a null terminated character string that
      contains the command line for the application to be executed.
      The entire command line is made available to the new process
      using GetCommandLine.If the lpApplicationName parameter was
      not specified, then the first token of the command line
      specifies file name of the application (note that this token
      begins at the beginning of the command line and ends at the
      first "white space" character).If the file name does not
      contain an extension (the presence of a "."), then .EXE is
      assumed.If the file name does not contain a directory path,
      Windows will search for the executable file in:

          - The current directory

          - The windows directory

          - The windows system directory

          - The directories listed in the path environment variable

      This parameter is optional onlu if the lpApplicationName
      parameter is specified.In this case the command line the
      application receives will be the application name.

    lpProcessAttributes - An optional parameter that may be used to
      specify the attributes of the new process.If the parameter is
      not specified, then the process is created without a security
      descriptor, and the resulting handle is not inherited on process
      creation:

      SECURITY_ATTRIBUTES Structure:

      DWORD nLength - Specifies the length of this structure.Must be
            set to sizeof( SECURITY_ATTRUBUTES ).

      LPVOID lpSecurityDescriptor - Points to a security descriptor for
            the object (must be NULL for Win32, used on NT/Win32). The
            security descriptor controls the sharing of an object.

      BOOL bInheritHandle - Supplies a flag that indicates whether
            or not the returned handle is to be inherited by a new
            process during process creation.A value of TRUE
            indicates that the new process will inherit the handle.

    lpThreadAttributes - An optional parameter that may be used to specify
      the attributes of the new thread.If the parameter is not
      specified, then the thread is created without a security
      descriptor, and the resulting handle is not inherited on
      process creation.

    dwCreationFlags - Supplies additional flags that control the creation
      of the process.

      dwCreationFlags Flags:

      DEBUG_PROCESS - If this flag bit is set, then the creating
            process is treated as a debugger, and the process being
            created is created as a debugee.All debug events occuring
            in the debugee are reported to the debugger.If this bit is
            clear, but the calling process is a debugee, then the
            process becomes a debugee of the calling processes debugger.
            If this bit is clear and the calling processes is not a
            debugee then no debug related actions occur.

      DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
            DEBUG_PROCESS flag bit must also be set.The calling
            process is is treated as a debugger, and the new process is
            created as its debuggee.If the new process creates
            additional processes, no debug related activities (with
            respect to the debugger) occur.

      CREATE_SUSPENDED - The process is created, but the initial thread
            of the process remains suspended. The creator can resume this
            thread using ResumeThread. Until this is done, code in the
            process will not execute.

      CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
            points to a Unicode environment block.Otherwise, the
            block is ANSI (actually OEM.)

    bInheritHandles - Supplies a flag that specifies whether or not the
      new process is to inherit handles to objects visible to the
      calling process.A value of TRUE causes handles to be inherited
      by the new process.If TRUE was specified, then for each handle
      visible to the calling process, if the handle was created with
      the inherit handle option, the handle is inherited to the new
      process.The handle has the same granted access in the new
      process as it has in the calling process, and the value of the
      handle is the same.

    lpEnvironment - An optional parameter, that if specified, supplies a
      pointer to an environment block.If the parameter is not
      specified, the environment block of the current process is used.
      This environment block is made available to the new process
      using GetEnvironmentStrings.

    lpCurrentDirectory - An optional parameter, that if specified,
      supplies a string representing the current drive and directory
      for the new process.The string must be a fully qualified
      pathname that includes a drive letter.If the parameter is not
      specified, then the new process is created with the same current
      drive and directory as the calling process.This option is
      provided primarily for shells that want to start an application
      and specify its initial drive and working directory.

    lpStartupInfo - Supplies information that specified how the
      applications window is to be shown. This structure is described
      in the Win32 User Interface API Book.

    lpProcessInformation - Returns identification information about the
      new process.

    PROCESS_INFORMATION Structure:

      HANDLE hProcess - Returns a handle to the newly created process.
            Through the handle, all operations on process objects are
            allowed.

      HANDLE hThread - Returns a handle to the newly created thread.
            Through the handle, all operations on thread objects are
            allowed.

      DWORD dwProcessId - Returns a global process id that may be used
            to identify a process.The value is valid from the time the
            process is created until the time the process is terminated.

      DWORD dwThreadId - Returns a global thread id that may be used
            to identify a thread.The value is valid from the time the
            thread is created until the time the thread is terminated.

    hRestrictedUserToken - Returns a restricted token if a UsetToken was
      supplied. This is applicable for the CreateProcessAsUser case.
      The token is released by CreateProcessAsUser.

Return Value:

    TRUE - The operation was successful

    FALSE/NULL - The operation failed. Extended error status is available
      using GetLastError.

--*/

{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    POBJECT_ATTRIBUTES pObja;
    HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL;
    HANDLE FileHandle, SectionHandle;
    CLIENT_ID ClientId;
    UNICODE_STRING PathName;
    IO_STATUS_BLOCK IoStatusBlock;
    BOOLEAN TranslationStatus;
    RTL_RELATIVE_NAME RelativeName;
    PVOID FreeBuffer;
    LPWSTR NameBuffer;
    LPWSTR WhiteScan;
    ULONG Length,i;
    PROCESS_BASIC_INFORMATION ProcessInfo;
    SECTION_IMAGE_INFORMATION ImageInformation;
    NTSTATUS StackStatus;
    BOOLEAN bStatus;
    INITIAL_TEB InitialTeb;
    CONTEXT ThreadContext;
    PPEB Peb;
    BASE_API_MSG m;
    PBASE_CREATEPROCESS_MSG a = &m.u.CreateProcess;
    PBASE_CHECKVDM_MSG b = &m.u.CheckVDM;
    PWCH TempNull = NULL;
    WCHAR TempChar;
    UNICODE_STRING VdmNameString;
    PVOID BaseAddress;
    ULONG VdmReserve;
    SIZE_T BigVdmReserve;
    ULONG iTask=0;
    LPWSTR CurdirBuffer, CurdirFilePart;
    DWORD CurdirLength,CurdirLength2;
    ULONG VDMCreationState=0;
    ULONG VdmBinaryType = 0;
    BOOLbMeowBinary = FALSE;
    UNICODE_STRINGSubSysCommandLine;
    PIMAGE_NT_HEADERS NtHeaders;
    DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW);
    ANSI_STRING AnsiStringVDMEnv;
    UNICODE_STRING UnicodeStringVDMEnv;
    WCHAR ImageFileDebuggerCommand[ MAX_PATH ];
    LPWSTR QuotedBuffer;
    BOOLEAN QuoteInsert;
    BOOLEAN QuoteCmdLine = FALSE;
    BOOLEAN QuoteFound;
    BOOL bSaferChecksNeeded = FALSE;
    BOOLEAN SearchRetry;
    BOOLEAN IsWowBinary = FALSE;
    STARTUPINFOW StartupInfo;
    DWORD LastError;
    DWORD fileattr;
    PROCESS_PRIORITY_CLASS PriClass;
    PVOID State;
    HANDLE DebugPortHandle = NULL;
    PVOID pAppCompatDataTemp;
    PVOID pAppCompatData= NULL;
    DWORD cbAppCompatData = 0;    // for the future
    BOOLEAN bVdmRetry = FALSE;
    DWORD Flags;
    PVOID pAppCompatSxsData = NULL;
    DWORD cbAppCompatSxsData = 0;
    SXS_OVERRIDE_STREAM AppCompatSxsManifest;
    PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
    SIZE_T SxsConglomeratedBufferSizeBytes;
    PBYTE SxsConglomeratedByteBuffer = NULL; // this contains all the of the below in one large right-sized heap allocation
                            // if we compute its size wrong, other code (if it gets it right..) should
                            // do more heap allocation
    ULONG sxsi; // for loop counter
    RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPathBuffer;
    RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPathBuffer;
    RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectoryBuffer;
    RTL_UNICODE_STRING_BUFFER SxsNtManifestPathBuffer;
    RTL_UNICODE_STRING_BUFFER SxsNtPolicyPathBuffer;
    const PRTL_UNICODE_STRING_BUFFER SxsStringBuffers[] = {
      // The order here does not matter.
      &SxsWin32ManifestPathBuffer,
      &SxsWin32PolicyPathBuffer,
      &SxsWin32AssemblyDirectoryBuffer,
      &SxsNtManifestPathBuffer,
      &SxsNtPolicyPathBuffer
    };
    UNICODE_STRING SxsWin32ExePath;
    UNICODE_STRING SxsNtExePath;
    BASE_MSG_SXS_HANDLES SxsExeHandles = {0};
    BASE_MSG_SXS_HANDLES SxsManifestFileHandles = {0};
    CONST SXS_CONSTANT_WIN32_NT_PATH_PAIR SxsExePathPair = { &SxsWin32ExePath, &SxsNtExePath };
    CONST SXS_WIN32_NT_PATH_PAIR SxsManifestPathPair = { &SxsWin32ManifestPathBuffer, &SxsNtManifestPathBuffer };
    CONST SXS_WIN32_NT_PATH_PAIR SxsPolicyPathPair = { &SxsWin32PolicyPathBuffer, &SxsNtPolicyPathBuffer };
    BASE_MSG_SXS_HANDLES SxsPolicyHandles = {0};
    PWSTR ExePathFullBuffer = NULL;

    DWORD dwJobMemberLevel = 0;
    HANDLE hSaferAssignmentJob = NULL;
    HANDLE hSaferRestrictedToken = NULL;

    DWORD dwBasePushProcessParametersFlags = 0;

#if defined(BUILD_WOW6432) || defined(_WIN64)
    BOOLEAN ComPlusILImage;
    LPCWSTR lpOriginalApplicationName = lpApplicationName;
    LPWSTR lpOriginalCommandLine = lpCommandLine;
#endif

#if defined(WX86)
    HANDLE Wx86Info = NULL;
#endif

#if defined WX86
    BOOLEAN UseKnownWx86Dll;
    UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll;
    NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
#endif

    RtlZeroMemory(&a->Sxs, sizeof(a->Sxs));
    RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation));

    if (ARGUMENT_PRESENT( hRestrictedUserToken )) {
      *hRestrictedUserToken = NULL;
      }

    // Private VDM flag should be ignored; Its meant for internal use only.
    dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW;

    if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
      (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) {

      SetLastError(ERROR_INVALID_PARAMETER);
      return FALSE;
      }

    AnsiStringVDMEnv.Buffer = NULL;
    UnicodeStringVDMEnv.Buffer = NULL;

    //
    // the lowest specified priority class is used.
    //

    if (dwCreationFlags & IDLE_PRIORITY_CLASS ) {
      PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
      }
    else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) {
      PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
      }
    else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) {
      PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
      }
    else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) {
      PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
      }
    else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) {
      PriClass.PriorityClass =PROCESS_PRIORITY_CLASS_HIGH;
      }
    else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) {
      if ( BasepIsRealtimeAllowed(FALSE) ) {
            PriClass.PriorityClass =PROCESS_PRIORITY_CLASS_REALTIME;
            }
      else {
            PriClass.PriorityClass =PROCESS_PRIORITY_CLASS_HIGH;
            }
      }
    else {
      PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN;
      }
    PriClass.Foreground = FALSE;

    dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK );

    //
    // Default separate/shared VDM option if not explicitly specified.
    //

    if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) {
      if (dwCreationFlags & CREATE_SHARED_WOW_VDM) {
            SetLastError(ERROR_INVALID_PARAMETER);

            return FALSE;
            }
      }
    else
    if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) {
      if (BaseStaticServerData->DefaultSeparateVDM) {
            dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
            }
      }

    if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) {
      //
      // If the creator is running inside a job object, always
      // set SEPERATE_WOW_VDM so the VDM is part of the job.
      //
      JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions;

      Status = NtQueryInformationJobObject(NULL,
                                             JobObjectBasicUIRestrictions,
                                             &UiRestrictions,
                                             sizeof(UiRestrictions),
                                             NULL);
      if (Status != STATUS_ACCESS_DENIED) {
            //
            // Anything other than STATUS_ACCESS_DENIED indicates the
            // current process is inside a job.
            //
            dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) |
                                  CREATE_SEPARATE_WOW_VDM;
            }
      }


    //
    //If ANSI environment, convert to Unicode
    //

    if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
      PUCHAR s;
      STRING Ansi;
      UNICODE_STRING Unicode;
      MEMORY_BASIC_INFORMATION MemoryInformation;

      Ansi.Buffer = s = lpEnvironment;
      while (*s || *(s+1))            // find end of block
            s++;

      Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1;
      Ansi.MaximumLength = Ansi.Length + 1;
      MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR);
      Unicode.Buffer = NULL;
      Status = NtAllocateVirtualMemory( NtCurrentProcess(),
                                          &Unicode.Buffer,
                                          0,
                                          &MemoryInformation.RegionSize,
                                          MEM_COMMIT,
                                          PAGE_READWRITE
                                        );
      if (!NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);

            return FALSE;
            }

      Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize;
      Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);
      if (!NT_SUCCESS(Status) ) {
            NtFreeVirtualMemory( NtCurrentProcess(),
                                 &Unicode.Buffer,
                                 &MemoryInformation.RegionSize,
                                 MEM_RELEASE
                               );
            BaseSetLastNTError(Status);

            return FALSE;
            }
      lpEnvironment = Unicode.Buffer;
      }

    FileHandle = NULL;
    SectionHandle = NULL;
    ProcessHandle = NULL;
    ThreadHandle = NULL;
    FreeBuffer = NULL;
    NameBuffer = NULL;
    VdmNameString.Buffer = NULL;
    BaseAddress = (PVOID)1;
    VdmReserve = 0;
    CurdirBuffer = NULL;
    CurdirFilePart = NULL;
    SubSysCommandLine.Buffer = NULL;
    QuoteFound = FALSE;
    QuoteInsert = FALSE;
    QuotedBuffer = NULL;

    try {

      //
      // Make a copy of the startup info so we can change it.
      //

      StartupInfo = *lpStartupInfo;

      //
      // STARTF_USEHOTKEY means hStdInput is really the hotkey value.
      // STARTF_HASSHELLDATA means std handles are used for shell-private
      // data.This flag is used if an icon is passed to ShellExecuteEx.
      // As a result they cannot be specified with STARTF_USESTDHANDLES.
      // Consistent with Win95, USESTDHANDLES is ignored.
      //

      if (StartupInfo.dwFlags & STARTF_USESTDHANDLES &&
            StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) {

            StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
            }

VdmRetry:
      //
      // None of this cleanup/reinit occurs for launching a Win32 or Win64 .exe,
      // but they generally do occur for launching 16bit, .bat, etc.
      //
      if (NameBuffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
            NameBuffer = NULL;
      }
      if (FreeBuffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
            FreeBuffer = NULL;
      }
      if (FileHandle) {
            NtClose(FileHandle);
            FileHandle = NULL;
      }

      LastError = 0;
      SearchRetry = TRUE;
      QuoteInsert = FALSE;
      QuoteCmdLine = FALSE;
      if (!ARGUMENT_PRESENT( lpApplicationName )) {

            //
            // Locate the image
            //

            // forgot to free NameBuffer before goto VdmRetry???
            ASSERT(NameBuffer == NULL);

            NameBuffer = RtlAllocateHeap( RtlProcessHeap(),
                                          MAKE_TAG( TMP_TAG ),
                                          MAX_PATH * sizeof( WCHAR ));
            if ( !NameBuffer ) {
                BaseSetLastNTError(STATUS_NO_MEMORY);
                return FALSE;
                }
            lpApplicationName = lpCommandLine;
            TempNull = (PWCH)lpApplicationName;
            WhiteScan = (LPWSTR)lpApplicationName;

            //
            // check for lead quote
            //
            if ( *WhiteScan == L'\"' ) {
                SearchRetry = FALSE;
                WhiteScan++;
                lpApplicationName = WhiteScan;
                while(*WhiteScan) {
                  if ( *WhiteScan == (WCHAR)'\"' ) {
                        TempNull = (PWCH)WhiteScan;
                        QuoteFound = TRUE;
                        break;
                        }
                  WhiteScan++;
                  TempNull = (PWCH)WhiteScan;
                  }
                }
            else {
retrywsscan:
                lpApplicationName = lpCommandLine;
                while(*WhiteScan) {
                  if ( *WhiteScan == (WCHAR)' ' ||
                         *WhiteScan == (WCHAR)'\t' ) {
                        TempNull = (PWCH)WhiteScan;
                        break;
                        }
                  WhiteScan++;
                  TempNull = (PWCH)WhiteScan;
                  }
                }
            TempChar = *TempNull;
            *TempNull = UNICODE_NULL;

#ifdef WX86

            //
            // Wx86 applications must use x86 version of known exes
            // for compatibility.
            //

            if (UseKnownWx86Dll) {
               LPWSTR KnownName;

               NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;

               KnownName = BasepWx86KnownExe(lpApplicationName);
               if (KnownName) {
                  lpApplicationName = KnownName;
                  }
               }
#endif


            Length = SearchPathW(
                        NULL,
                        lpApplicationName,
                        L".exe",
                        MAX_PATH,
                        NameBuffer,
                        NULL
                        )*2;

            if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) {
                //
                // SearchPathW worked, but file might be a directory
                // if this happens, we need to keep trying
                //
                fileattr = GetFileAttributesW(NameBuffer);
                if ( fileattr != 0xffffffff &&
                     (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
                  Length = 0;
                } else {
                  Length++;
                  Length++;
                }
            }

            if ( !Length || Length >= MAX_PATH<<1 ) {

                //
                // If we search pathed, then return file not found.
                // otherwise, try to be more specific.
                //
                RTL_PATH_TYPE PathType;
                HANDLE hFile;

                PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
                if ( PathType != RtlPathTypeRelative ) {

                  //
                  // The failed open should set get last error properly.
                  //

                  hFile = CreateFileW(
                              lpApplicationName,
                              GENERIC_READ,
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL
                              );
                  if ( hFile != INVALID_HANDLE_VALUE ) {
                        CloseHandle(hFile);
                        BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
                        }
                  }
                else {
                  BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
                  }

                //
                // remember initial last error value for the retry scan path
                //

                if ( LastError ) {
                  SetLastError(LastError);
                  }
                else {
                  LastError = GetLastError();
                  }

                //
                // restore the command line
                //

                *TempNull = TempChar;
                lpApplicationName = NameBuffer;

                //
                // If we still have command line left, then keep going
                // the point is to march through the command line looking
                // for whitespace so we can try to find an image name
                // launches of things like:
                // c:\word 95\winword.exe /embedding -automation
                // require this. Our first iteration will stop at c:\word, our next
                // will stop at c:\word 95\winword.exe
                //
                if (*WhiteScan && SearchRetry) {
                  WhiteScan++;
                  TempNull = WhiteScan;
                  QuoteInsert = TRUE;
                  QuoteFound = TRUE;
                  goto retrywsscan;
                }

                return FALSE;
                }
            //
            // restore the command line
            //

            *TempNull = TempChar;
            lpApplicationName = NameBuffer;

            //
            // check whether it is setup.exe started by winlogon.exe
            //
            if (BasepIsSetupInvokedByWinLogon(lpApplicationName))
            {
                // validate the flag
                if (!(dwCreationFlags & CREATE_IGNORE_SYSTEM_DEFAULT))
                {
                  //
                  // BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
                  //    Winlogon does not set the flag correctly
                  //    in phase1, ignore it(now)
                  //    in phase2, ASSERT it
                  // BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
                  //
                  dwCreationFlags |= CREATE_IGNORE_SYSTEM_DEFAULT;
                }
            }
            
      }
      else
      if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) {
            QuoteCmdLine = TRUE;
            lpCommandLine = (LPWSTR)lpApplicationName;
            }


#ifdef WX86

       //
       // Wx86 applications must use x86 version of known exes
       // for compatibility.
       //

       if (UseKnownWx86Dll) {
         LPWSTR KnownName;

         NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;

         KnownName = BasepWx86KnownExe(lpApplicationName);
         if (KnownName) {

               RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
               NameBuffer = KnownName;
               lpApplicationName = KnownName;
               }
         }

#endif

      //
      // Translate to an NT name.
      //

      TranslationStatus = RtlDosPathNameToNtPathName_U(
                              lpApplicationName,
                              &PathName,
                              NULL,
                              &RelativeName
                              );

      if ( !TranslationStatus ) {
            SetLastError(ERROR_PATH_NOT_FOUND);

            return FALSE;
            }

      // forgot to free FreeBuffer before goto VdmRetry????
      ASSERT(FreeBuffer == NULL);
      FreeBuffer = PathName.Buffer;

      RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);

      {
            RTL_PATH_TYPE SxsWin32ExePathType = RtlDetermineDosPathNameType_U(lpApplicationName);

            if ((SxsWin32ExePathType != RtlPathTypeDriveAbsolute) &&
                (SxsWin32ExePathType != RtlPathTypeUncAbsolute)) {
                if (ExePathFullBuffer == NULL) {
                  // It seems that with VDM things, we can rerun this code with a new lpApplication, so
                  // we protect against double-allocating the buffer and just allocate a big
                  // MAX_PATH one the first time through, assuming it's good enough for the 2ndary times
                  // too.
                  ExePathFullBuffer = RtlAllocateHeap(RtlProcessHeap(), 0, (MAX_PATH + 1) * sizeof(WCHAR));
                  if (ExePathFullBuffer == NULL) {
                        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                        return FALSE;
                  }
                }

                RtlGetFullPathName_U(lpApplicationName, (MAX_PATH + 1) * sizeof(WCHAR), ExePathFullBuffer, NULL);

                RtlInitUnicodeString(&SxsWin32ExePath, ExePathFullBuffer);
            }
      }


      SxsNtExePath = PathName;

      if ( RelativeName.RelativeName.Length ) {
            PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
            }
      else {
            RelativeName.ContainingDirectory = NULL;
            }

      InitializeObjectAttributes(
            &Obja,
            &PathName,
            OBJ_CASE_INSENSITIVE,
            RelativeName.ContainingDirectory,
            NULL
            );

      //
      // Open the file for red and execute access
      //

      Status = NtOpenFile(
                  &FileHandle,
                  SYNCHRONIZE | FILE_EXECUTE | FILE_READ_ATTRIBUTES | FILE_READ_DATA,
                  &Obja,
                  &IoStatusBlock,
                  FILE_SHARE_READ | FILE_SHARE_DELETE,
                  FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
                  );

      if (!NT_SUCCESS(Status) ) {

            //
            // We failed. Open the file for lesser access.
            //

            Status = NtOpenFile(
                        &FileHandle,
                        SYNCHRONIZE | FILE_EXECUTE,
                        &Obja,
                        &IoStatusBlock,
                        FILE_SHARE_READ | FILE_SHARE_DELETE,
                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
                        );

            if (!NT_SUCCESS(Status) ) {
                //
                // if we failed, see if this is a device. If it is a device,
                // then just return invalid image format
                //

                if ( RtlIsDosDeviceName_U(RTL_CONST_CAST(PWSTR)(lpApplicationName)) ) {
                  SetLastError(ERROR_BAD_DEVICE);
                  }
                else {
                  BaseSetLastNTError(Status);
                  }

                return FALSE;
            }

      }

      //
      // If no desktop has been specified, use the caller's
      // desktop.
      //

      if (StartupInfo.lpDesktop == NULL) {
            StartupInfo.lpDesktop =
                  (LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
                        ProcessParameters)->DesktopInfo.Buffer;
            }

      //
      // Create a section object backed by the file
      //

      Status = NtCreateSection(
                  &SectionHandle,
                  SECTION_ALL_ACCESS,
                  NULL,
                  NULL,
                  PAGE_EXECUTE,
                  SEC_IMAGE,
                  FileHandle
                  );

      //
      // App Certification DLL
      //

      if (NT_SUCCESS(Status)) {
            Status = BasepIsProcessAllowed(lpApplicationName);

            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                NtClose(SectionHandle);
                return FALSE;
            }


      
          //
          // If Meow subsystem is enabled and caller specified CREATE_FORECEDOS for a win32 image
          // push it into the meow subsystem
          //

          if ((dwCreationFlags & CREATE_FORCEDOS) && BaseStaticServerData->ForceDos) {
               dwCreationFlags &= ~(CREATE_SHARED_WOW_VDM | CREATE_FORCEDOS);
               dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
               Status = STATUS_INVALID_IMAGE_WIN_16;
               bMeowBinary = TRUE;

               NtClose(SectionHandle);
               SectionHandle = NULL;
          }


       }

      //
      // check appcompat (aka apphelp)
      //
      // if we are running under debugger, bVdmRetry will be FALSE
      // yet pAppCompatData may have some data (from the app itself)
      // debugger will do a separate CreateProcess on debugee
      //
      // apphelp gets called if it is win32 app or if it is a .bat or .cmd

       if(!bVdmRetry &&
          (NT_SUCCESS(Status) ||
         (Status == STATUS_INVALID_IMAGE_NOT_MZ && !BaseIsDosApplication(&PathName,Status)))
         ) {
            NTSTATUS BadAppStatus;

            if (NULL != pAppCompatData) {
                RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
                pAppCompatData = NULL;
                }
            
            if (NULL != pAppCompatSxsData) {
                RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
                pAppCompatSxsData = NULL;
                }
            
            //
            // we only check ONCE --
            // the second time around is rather meaningless - to check for posix/ntvdm/os2 emulation
            //
            BadAppStatus = BasepCheckBadapp(FileHandle,
                                          PathName.Buffer,
                                          (WCHAR*)lpEnvironment,
                                          &pAppCompatData,
                                          &cbAppCompatData,
                                          &pAppCompatSxsData,
                                          &cbAppCompatSxsData);
                  
            if (!NT_SUCCESS(BadAppStatus)) {
                if (BadAppStatus == STATUS_ACCESS_DENIED) {
                  SetLastError(ERROR_CANCELLED);
                  }
                else {
                  BaseSetLastNTError(BadAppStatus);
                  }

                if (SectionHandle) {
                  NtClose(SectionHandle);
                  SectionHandle = NULL;
                  }
                return FALSE;
                }
            }

       //
       // Winsafer code
       //
       // If this is the first time then we will have to do Safer checks.
       // Note that we do not impose any restrictions on the interpreter
       // itself since it is part of OS.
       //


       if ((!bVdmRetry) &&
         ( (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL) == 0 )) {

         NTSTATUS SaferStatus;


         {

               //
               // WinSafer process sandbox restrictions handling.
               // Should be done for non .NET images only.
               //

               SaferStatus = BasepCheckWinSaferRestrictions(
                                                      hUserToken,
                                                      lpApplicationName,   // same as PathName.Buffer
                                                      FileHandle,
                                                      &dwJobMemberLevel,
                                                      &hSaferRestrictedToken,
                                                      &hSaferAssignmentJob);
               if (SaferStatus == -1) {
                   SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
                   bStatus =FALSE;
                   leave;
               } else if (!NT_SUCCESS(SaferStatus)) {
                   BaseSetLastNTError(SaferStatus);
                   bStatus = FALSE;
                   leave;
               }
         }
       }


      if (!NT_SUCCESS(Status)) {
            switch (Status) {
                // 16 bit OS/2 exe
                case STATUS_INVALID_IMAGE_NE_FORMAT:
#if defined(i386) && defined(OS2_SUPPORT_ENABLED)
                //
                // Use OS/2 if x86 (OS/2 not supported on risc),
                //    and CreationFlags don't have forcedos bit
                //    and Registry didn't specify ForceDos
                //
                // else execute as a DOS bound app.
                //
                //

                if (!(dwCreationFlags & CREATE_FORCEDOS) &&
                  !BaseStaticServerData->ForceDos)
                  {

                  if ( !BuildSubSysCommandLine( L"OS2 /P ",
                                                lpApplicationName,
                                                lpCommandLine,
                                                &SubSysCommandLine
                                                ) ) {
                        return FALSE;
                        }

                  lpCommandLine = SubSysCommandLine.Buffer;

                  lpApplicationName = NULL;

                  bVdmRetry = TRUE;
                  goto VdmRetry;
                  }
#endif
                  // Falls into Dos case, so that stub message will be
                  // printed, and bound apps will run w/o OS/2 subsytem

                // Dos .exe or .com

                case STATUS_INVALID_IMAGE_PROTECT:
                case STATUS_INVALID_IMAGE_NOT_MZ:
ForceDos:
                  {
                  ULONG BinarySubType;

                  BinarySubType = BINARY_TYPE_DOS_EXE;
                  if (Status == STATUS_INVALID_IMAGE_PROTECT   ||
                        Status == STATUS_INVALID_IMAGE_NE_FORMAT ||
                     (BinarySubType = BaseIsDosApplication(&PathName,Status)) )
                     {
#if defined(_WIN64) || defined(BUILD_WOW6432)
                        //
                        // If this a DOS application, then we need to pop up a dialog
                        // saying that this an invalid win32 application.
                        //
                        goto RaiseInvalidWin32Error;
#endif
                        VdmBinaryType = BINARY_TYPE_DOS;

                        // create the environment before going to the
                        // server. This was done becuase we want NTVDM
                        // to have the new environment when it gets
                        // created.
                        if (!BaseCreateVDMEnvironment(
                                    lpEnvironment,
                                    &AnsiStringVDMEnv,
                                    &UnicodeStringVDMEnv
                                    )) {
                            return FALSE;
                        }

                        if(!BaseCheckVDM(VdmBinaryType | BinarySubType,
                                       lpApplicationName,
                                       lpCommandLine,
                                       lpCurrentDirectory,
                                       &AnsiStringVDMEnv,
                                       &m,
                                       &iTask,
                                       dwCreationFlags,
                                       &StartupInfo
                                       )) {

                            return FALSE;
                        }

                        // Check the return value from the server
                        switch (b->VDMState & VDM_STATE_MASK) {
                            case VDM_NOT_PRESENT:
                              // mark this so the server can undo
                              // creation if something goes wrong.
                              // We marked it "partially created" because
                              // the NTVDM has yet not been fully created.
                              // a call to UpdateVdmEntry to update
                              // process handle will signal the NTVDM
                              // process completed creation
                              VDMCreationState = VDM_PARTIALLY_CREATED;
                              // fail the call if NTVDM process is being
                              // created DETACHED.
                              // note that, we let it go if NTVDM process
                              // is already running.
                              if (dwCreationFlags & DETACHED_PROCESS) {
                                    SetLastError(ERROR_ACCESS_DENIED);
                                    return FALSE;
                                    }
                              if (!BaseGetVdmConfigInfo(lpCommandLine,
                                                          iTask,
                                                          VdmBinaryType,
                                                          &VdmNameString,
                                                          &VdmReserve)) {
                                    BaseSetLastNTError(Status);
                                    return FALSE;
                                    }

                              lpCommandLine = VdmNameString.Buffer;
                              lpApplicationName = NULL;

                              break;

                            case VDM_PRESENT_NOT_READY:
                              SetLastError (ERROR_NOT_READY);
                              return FALSE;

                            case VDM_PRESENT_AND_READY:
                              VDMCreationState = VDM_BEING_REUSED;
                              VdmWaitHandle = b->WaitObjectForParent;
                              break;
                            }
                         VdmReserve--;               // we reserve from addr 1
                         if(VdmWaitHandle)
                            goto VdmExists;
                         else{
                            bInheritHandles = FALSE;
                            if (lpEnvironment &&
                              !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){
                              RtlDestroyEnvironment(lpEnvironment);
                              }
                            lpEnvironment = UnicodeStringVDMEnv.Buffer;
                            bVdmRetry = TRUE;
                            goto VdmRetry;
                            }
                        }
                  else {

                        //
                        //must be a .bat or .cmd file
                        //

                        static PWCHAR CmdPrefix = L"cmd /c ";
                        PWCHAR NewCommandLine;
                        ULONG Length;
                        PWCHAR Last4 = &PathName.Buffer;

                        if ( PathName.Length < 8 ) {
                            SetLastError(ERROR_BAD_EXE_FORMAT);
                            return FALSE;
                            }

                        if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) {
                            SetLastError(ERROR_BAD_EXE_FORMAT);
                            return FALSE;
                        }

                        Length = wcslen( CmdPrefix )
                                 + (QuoteCmdLine || QuoteFound )
                                 + wcslen( lpCommandLine )
                                 + (QuoteCmdLine || QuoteFound)
                                 + 1;

                        NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ),
                                                          MAKE_TAG( TMP_TAG ),
                                                          Length * sizeof( WCHAR ) );

                        if (NewCommandLine == NULL) {
                            BaseSetLastNTError(STATUS_NO_MEMORY);
                            return FALSE;
                        }

                        wcscpy( NewCommandLine, CmdPrefix );
                        if (QuoteCmdLine || QuoteFound) {
                            wcscat( NewCommandLine, L"\"" );
                        }
                        wcscat( NewCommandLine, lpCommandLine );
                        if (QuoteCmdLine || QuoteFound) {
                            wcscat( NewCommandLine, L"\"" );
                        }

                        RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine );

                        lpCommandLine = SubSysCommandLine.Buffer;

                        lpApplicationName = NULL;

                        bVdmRetry = TRUE;
                        goto VdmRetry;

                        }

                  }

                // 16 bit windows exe
                case STATUS_INVALID_IMAGE_WIN_16:
#if defined(BUILD_WOW6432) || defined(_WIN64)
                   if (lpOriginalApplicationName == NULL) {
                     // pass in the part of the command line after the exe name
                     // including whitespace
                     lpCommandLine = ((*TempNull == '\"') ? TempNull + 1 : TempNull);
                   } else {
                     lpCommandLine = lpOriginalCommandLine;
                   }

                   return NtVdm64CreateProcess(lpOriginalApplicationName == NULL,
                                             lpApplicationName,             // this is now the real file name we've loaded
                                             lpCommandLine,
                                             lpProcessAttributes,
                                             lpThreadAttributes,
                                             bInheritHandles,
                                             (dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT),// the environment has already been converted to unicode
                                             lpEnvironment,
                                             lpCurrentDirectory,
                                             lpStartupInfo,
                                             lpProcessInformation
                                             );
#endif
                   if (dwCreationFlags & CREATE_FORCEDOS) {
                     goto ForceDos;
                     }

                  IsWowBinary = TRUE;
                  if (!BaseCreateVDMEnvironment(lpEnvironment,
                                                &AnsiStringVDMEnv,
                                                &UnicodeStringVDMEnv)) {
                        return FALSE;
                        }

RetrySepWow:
                  VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM
                                     ? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16;

                  if (!BaseCheckVDM(VdmBinaryType,
                                    lpApplicationName,
                                    lpCommandLine,
                                    lpCurrentDirectory,
                                    &AnsiStringVDMEnv,
                                    &m,
                                    &iTask,
                                    dwCreationFlags,
                                    &StartupInfo
                                    ))
                     {
                        //
                        // If we failed with access denied, caller may not
                        // be allowed allowed to access the shared wow's
                        // desktop, so retry as a separate wow
                        //
                        if (VdmBinaryType == BINARY_TYPE_WIN16 &&
                            GetLastError() == ERROR_ACCESS_DENIED)
                        {
                           dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
                           }
                        else {
                            return FALSE;
                            }
                        goto RetrySepWow;
                        }

                  // Check the return value from the server
                  switch (b->VDMState & VDM_STATE_MASK){
                        case VDM_NOT_PRESENT:
                            // mark this so the server can undo
                            // creation if something goes wrong.
                            // We marked it "partitially created" because
                            // the NTVDM has yet not been fully created.
                            // a call to UpdateVdmEntry to update
                            // process handle will signal the NTVDM
                            // process completed creation

                            VDMCreationState = VDM_PARTIALLY_CREATED;

                            // jarbats: 1/8/2001
                            // Tell BaseGetVdmConfigInfo to create
                            // vdm commandline for meow
                            //

                            if (bMeowBinary)
                               {
                               VdmReserve = 1;
                               }

                            if (!BaseGetVdmConfigInfo(
                                    lpCommandLine,
                                    iTask,
                                    VdmBinaryType,
                                    &VdmNameString,
                                    &VdmReserve
                                    )) {
                              BaseSetLastNTError(Status);
                              return FALSE;
                              }

                            lpCommandLine = VdmNameString.Buffer;
                            lpApplicationName = NULL;


                            //
                            // Wow must have a hidden console
                            // Throw away DETACHED_PROCESS flag which isn't
                            // meaningful for Win16 apps.
                            //

                            dwCreationFlags |= CREATE_NO_WINDOW;
                            dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);


                            //
                            // We're starting a WOW VDM, turn on feedback unless
                            // the creator passed STARTF_FORCEOFFFEEDBACK.
                            //

                            StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;

                            break;

                        case VDM_PRESENT_NOT_READY:
                            SetLastError (ERROR_NOT_READY);
                            return FALSE;

                        case VDM_PRESENT_AND_READY:
                            VDMCreationState = VDM_BEING_REUSED;
                            VdmWaitHandle = b->WaitObjectForParent;
                            break;
                        }

                  VdmReserve--;               // we reserve from addr 1
                  if(VdmWaitHandle)
                        goto VdmExists;
                  else {
                        bInheritHandles = FALSE;
                        // replace the environment with ours
                        if (lpEnvironment &&
                            !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
                            RtlDestroyEnvironment(lpEnvironment);
                            }
                        lpEnvironment = UnicodeStringVDMEnv.Buffer;
                        bVdmRetry = TRUE;
                        goto VdmRetry;
                        }

                case STATUS_FILE_IS_OFFLINE:
                  SetLastError(ERROR_FILE_OFFLINE);
                  break;

                default :
                  SetLastError(ERROR_BAD_EXE_FORMAT);
                  return FALSE;
            }
      }

      //
      // Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag.
      //

      if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
            dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
      }

      //
      // Query the section to determine the stack parameters and
      // image entrypoint.
      //

      Status = NtQuerySection(
                  SectionHandle,
                  SectionImageInformation,
                  &ImageInformation,
                  sizeof( ImageInformation ),
                  NULL
                  );

      if (!NT_SUCCESS( Status )) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

      if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
            SetLastError(ERROR_BAD_EXE_FORMAT);
            return FALSE;
            }

      ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL;
      if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
            NtCurrentPeb()->ReadImageFileExecOptions
         ) {
            LdrQueryImageFileExecutionOptions( &PathName,
                                             L"Debugger",
                                             REG_SZ,
                                             ImageFileDebuggerCommand,
                                             sizeof( ImageFileDebuggerCommand ),
                                             NULL
                                             );
            }


      if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) ||
            (ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
#if defined(_WIN64) || defined(BUILD_WOW6432)
            if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
               // Fall through since this is a valid machine type.
                }
             else
#endif
                {
                ULONG_PTR ErrorParameters;
                ULONG ErrorResponse;

#if defined(_WIN64) || defined(BUILD_WOW6432)
RaiseInvalidWin32Error:
#endif
                ErrorResponse = ResponseOk;
                ErrorParameters = (ULONG_PTR)&PathName;

                NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
                                  1,
                                  1,
                                  ErrorParameters,
                                  OptionOk,
                                  &ErrorResponse
                              );
                if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) {
                  SetLastError(ERROR_BAD_EXE_FORMAT);
                  }
                else {
                  SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
                  }
                return FALSE;
                }
            }

      if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
             ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) {

            // POSIX exe

            NtClose(SectionHandle);
            SectionHandle = NULL;

            if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) {

                if ( !BuildSubSysCommandLine( L"POSIX /P ",
                                              lpApplicationName,
                                              lpCommandLine,
                                              &SubSysCommandLine
                                          ) ) {
                  return FALSE;
                }

                lpCommandLine = SubSysCommandLine.Buffer;

                lpApplicationName = NULL;
                bVdmRetry = TRUE;
                goto VdmRetry;
                }
            else {
                SetLastError(ERROR_CHILD_NOT_COMPLETE);
                return FALSE;
                }
            }
      else {
            if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion,
                                        ImageInformation.SubSystemMinorVersion) ) {
                SetLastError(ERROR_BAD_EXE_FORMAT);
                return FALSE;
                }
            }

      if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) {
            SIZE_T n;

            n = wcslen( lpCommandLine );
            if (n == 0) {
                lpCommandLine = (LPWSTR)lpApplicationName;
                n = wcslen( lpCommandLine );
                }

            n += wcslen( ImageFileDebuggerCommand ) + 1 + 2;
            n *= sizeof( WCHAR );

            SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n );
            SubSysCommandLine.Length = 0;
            SubSysCommandLine.MaximumLength = (USHORT)n;
            RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand );
            RtlAppendUnicodeToString( &SubSysCommandLine, L" " );
            RtlAppendUnicodeToString( &SubSysCommandLine, lpCommandLine );
#if DBG
            DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine );
#endif
            lpCommandLine = SubSysCommandLine.Buffer;
            lpApplicationName = NULL;
            NtClose(SectionHandle);
            SectionHandle = NULL;
            RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
            NameBuffer = NULL;
            RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
            FreeBuffer = NULL;
            goto VdmRetry;
            }

      //
      // Create the process object
      //

      pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL);

      Flags = 0;
      if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) {
            Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
      }

      if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) {
            Status = DbgUiConnectToDbg();
            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return FALSE;
            }
            DebugPortHandle = DbgUiGetThreadDebugObject ();
            if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS) {
                Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
            }
      }

      if (bInheritHandles) {
            Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
      }

      if (((ImageInformation.LoaderFlags & IMAGE_LOADER_FLAGS_COMPLUS) != 0)) {

#if defined(_WIN64) || defined(BUILD_WOW6432)

            //
            // Check if this is a 32-bit IL_ONLY COM+ image that needs to run natively
            // on Win64.
            //

            if ( ImageInformation.Machine == IMAGE_FILE_MACHINE_I386 ) {

                Status = BasepIsComplusILImage(
                                              SectionHandle,
                                              &ImageInformation,
                                              &ComPlusILImage
                                              );

                if ((NT_SUCCESS (Status)) && (ComPlusILImage != FALSE)) {
                  Flags |= PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE;
                }
            }

#endif
      }
            
      //
      // This is temporary till we get Shim dlls support for native Win64 applications.
      //

      if (ImageInformation.Machine != IMAGE_FILE_MACHINE_I386) {
            pAppCompatDataTemp = NULL;
      } else {
            pAppCompatDataTemp = pAppCompatData;
      }

      Status = NtCreateProcessEx(
                  &ProcessHandle,
                  PROCESS_ALL_ACCESS,
                  pObja,
                  NtCurrentProcess(),
                  Flags,
                  SectionHandle,
                  DebugPortHandle,
                  NULL,
                  dwJobMemberLevel         // Job member level
                  );
      if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            return FALSE;
      }

      //
      // NtCreateProcess will set to normal OR inherit if parent is IDLE or Below
      // only override if a mask is given during the create.
      //

      if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) {
            State = NULL;
            if ( PriClass.PriorityClass ==PROCESS_PRIORITY_CLASS_REALTIME ) {
                State = BasepIsRealtimeAllowed(TRUE);
                }
            Status = NtSetInformationProcess(
                        ProcessHandle,
                        ProcessPriorityClass,
                        (PVOID)&PriClass,
                        sizeof(PriClass)
                        );
            if ( State ) {
                BasepReleasePrivilege( State );
                }

            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return FALSE;
                }
            }

      if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) {
            UINT NewMode;
            NewMode = SEM_FAILCRITICALERRORS;
            NtSetInformationProcess(
                ProcessHandle,
                ProcessDefaultHardErrorMode,
                &NewMode,
                sizeof(NewMode)
                );
            }

      //
      // If the process is being created for a VDM call the server with
      // process handle.
      //

      if (VdmBinaryType) {
            VdmWaitHandle = ProcessHandle;
            if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE,
                                    &VdmWaitHandle,
                                    iTask,
                                    VdmBinaryType
                                    ))
                {
                //make sure we don't close the handle twice --
                //(VdmWaitHandle == ProcessHandle) if we don't do this.
                VdmWaitHandle = NULL;
                return FALSE;
                }

            //
            // For Sep wow the VdmWaitHandle = NULL (there is none!)
            //

            VDMCreationState |= VDM_FULLY_CREATED;
            }


#if defined(i386)
      //
      // Reserve memory in the new process' address space if necessary
      // (for vdms). This is required only for x86 system.
      //

    if ( VdmReserve ) {
            BigVdmReserve = VdmReserve;
            Status = NtAllocateVirtualMemory(
                        ProcessHandle,
                        &BaseAddress,
                        0L,
                        &BigVdmReserve,
                        MEM_RESERVE,
                        PAGE_EXECUTE_READWRITE
                        );
            if ( !NT_SUCCESS(Status) ){
                BaseSetLastNTError(Status);
                return FALSE;
            }
    }
#endif

      //
      // Abuse the StaticSize fields temporarily.
      // They are somewhat private, but we are using them for temporary space, sort of.
      // These are the right values for them, but we need to call the proper initialization function,
      // which will store the values a second time (possibly optimized away).
      //
      SxsWin32ManifestPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
      SxsWin32PolicyPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_POLICY_SUFFIX);
      SxsWin32AssemblyDirectoryBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(WCHAR); // Win32AssemblyDirectory overestimate
      SxsNtManifestPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
      SxsNtPolicyPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_POLICY_SUFFIX);
      //
      // now add them up as BYTE sizes
      //
      SxsConglomeratedBufferSizeBytes = 0;
      for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
            SxsConglomeratedBufferSizeBytes += SxsStringBuffers->ByteBuffer.StaticSize;
      }
#if DBG
      DbgPrintEx(
            DPFLTR_SXS_ID,
            DPFLTR_INFO_LEVEL,
            "SXS: SxsConglomeratedBufferSizeBytes:%Id\n",
            SxsConglomeratedBufferSizeBytes
            );
#endif
      //
      // one honking heap allocation
      //
      SxsConglomeratedByteBuffer = (PBYTE)RtlAllocateHeap(RtlProcessHeap(), 0, SxsConglomeratedBufferSizeBytes);
      if (SxsConglomeratedByteBuffer == NULL) {
            BaseSetLastNTError(STATUS_NO_MEMORY);
            return FALSE;
      }
      //
      // now dole out pieces, calling the proper initialization function
      //
      for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
            RtlInitUnicodeStringBuffer(
                SxsStringBuffers,
                (sxsi != 0) ? SxsStringBuffers->ByteBuffer.Buffer + SxsStringBuffers->ByteBuffer.StaticSize
                         : SxsConglomeratedByteBuffer,
                SxsStringBuffers->ByteBuffer.StaticSize
                );
      }

      SxsExeHandles.Process = ProcessHandle;
      SxsExeHandles.File = FileHandle;
         // The 1 bit here means something different than in the loader.
      ASSERT((((ULONG_PTR)SectionHandle) & (ULONG_PTR)1) == 0);
      SxsExeHandles.Section = SectionHandle;

      // if we have an override stream, use it
      if (NULL != pAppCompatSxsData) {
            AppCompatSxsManifest.Name    = SxsWin32ExePath;   // unicode string
            AppCompatSxsManifest.Address = pAppCompatSxsData;   // pointer to unicode manifest
            AppCompatSxsManifest.Size    = cbAppCompatSxsData;// byte count
      }

      Status = BasepSxsCreateProcessCsrMessage(
            (NULL != pAppCompatSxsData) ? &AppCompatSxsManifest : NULL, // override manifest (appcompat hook)
            NULL, // override policy (appcompat hook)
            &SxsManifestPathPair,
            &SxsManifestFileHandles,
            &SxsExePathPair,
            &SxsExeHandles,
            &SxsPolicyPathPair,
            &SxsPolicyHandles,
            &SxsWin32AssemblyDirectoryBuffer,
            &a->Sxs
            );
#if DBG
      // verify the buffer size calculation
      for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi)
      {
            if (SxsStringBuffers->ByteBuffer.Buffer != SxsStringBuffers->ByteBuffer.StaticBuffer)
            {
                DbgPrintEx(
                  DPFLTR_SXS_ID,
                  DPFLTR_WARNING_LEVEL,
                  "SXS: SxsStringBuffers[%lu]'s StaticSize was computed too small (%Id, %Id)\n",
                  sxsi,
                  SxsStringBuffers->ByteBuffer.StaticSize,
                  SxsStringBuffers->ByteBuffer.Size
                  );
            }
      }
#endif
      if ( !NT_SUCCESS( Status ) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

      //
      // Determine the location of the
      // processes PEB.
      //

      Status = NtQueryInformationProcess(
                  ProcessHandle,
                  ProcessBasicInformation,
                  &ProcessInfo,
                  sizeof( ProcessInfo ),
                  NULL
                  );
      if ( !NT_SUCCESS( Status ) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

      Peb = ProcessInfo.PebBaseAddress;

      //
      // Push the parameters into the address space of the new process
      //

      if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) {
            CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(),
                                          MAKE_TAG( TMP_TAG ),
                                          (MAX_PATH + 1) * sizeof( WCHAR ) );
            if ( !CurdirBuffer ) {
                BaseSetLastNTError(STATUS_NO_MEMORY);
                return FALSE;
                }
            CurdirLength2 = GetFullPathNameW(
                              lpCurrentDirectory,
                              MAX_PATH,
                              CurdirBuffer,
                              &CurdirFilePart
                              );
            if ( CurdirLength2 > MAX_PATH ) {
                SetLastError(ERROR_DIRECTORY);
                return FALSE;
                }

            //
            // now make sure the directory exists
            //

            CurdirLength = GetFileAttributesW(CurdirBuffer);
            if ( (CurdirLength == 0xffffffff) ||
               !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) {
                SetLastError(ERROR_DIRECTORY);
                return FALSE;
                }
            }


      if ( QuoteInsert || QuoteCmdLine) {
            QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6);

            if ( QuotedBuffer ) {
                wcscpy(QuotedBuffer,L"\"");

                if ( QuoteInsert ) {
                  TempChar = *TempNull;
                  *TempNull = UNICODE_NULL;
                  }

                wcscat(QuotedBuffer,lpCommandLine);
                wcscat(QuotedBuffer,L"\"");

                if ( QuoteInsert ) {
                  *TempNull = TempChar;
                  wcscat(QuotedBuffer,TempNull);
                  }

                }
            else {
                if ( QuoteInsert ) {
                  QuoteInsert = FALSE;
                  }
                if ( QuoteCmdLine ) {
                  QuoteCmdLine = FALSE;
                  }
                }
            }


      // If we found a manifest, we want to push that fact to the new process.
      if (a->Sxs.Flags & BASE_MSG_SXS_MANIFEST_PRESENT)
            dwBasePushProcessParametersFlags |= BASE_PUSH_PROCESS_PARAMETERS_FLAG_APP_MANIFEST_PRESENT;

      if (!BasePushProcessParameters(
                dwBasePushProcessParametersFlags,
                ProcessHandle,
                Peb,
                lpApplicationName,
                CurdirBuffer,
                QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine,
                lpEnvironment,
                &StartupInfo,
                dwCreationFlags | dwNoWindow,
                bInheritHandles,
                IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0,
                pAppCompatDataTemp,
                cbAppCompatData
                ) ) {
            return FALSE;
            }


      RtlFreeUnicodeString(&VdmNameString);
      VdmNameString.Buffer = NULL;

      //
      // Stuff in the standard handles if needed
      //
      if (!VdmBinaryType &&
            !bInheritHandles &&
            !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
            !(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) &&
            ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI
         ) {
            PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess;

            Status = NtReadVirtualMemory( ProcessHandle,
                                          &Peb->ProcessParameters,
                                          &ParametersInNewProcess,
                                          sizeof( ParametersInNewProcess ),
                                          NULL
                                        );
            if (NT_SUCCESS( Status )) {
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) {
                  StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardInput,
                                    &ParametersInNewProcess->StandardInput
                                  );
                  }
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) {
                  StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardOutput,
                                    &ParametersInNewProcess->StandardOutput
                                  );
                  }
                if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) {
                  StuffStdHandle( ProcessHandle,
                                    NtCurrentPeb()->ProcessParameters->StandardError,
                                    &ParametersInNewProcess->StandardError
                                  );
                  }
                }
            }

      //
      // Create the thread...
      //

      //
      // Allocate a stack for this thread in the address space of the target
      // process.
      //

      StackStatus = BaseCreateStack(
                        ProcessHandle,
                        ImageInformation.CommittedStackSize,
                        (ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize,
                        &InitialTeb
                        );

      if ( !NT_SUCCESS(StackStatus) ) {
            BaseSetLastNTError(StackStatus);
            return FALSE;
            }


      //
      // Create an initial context for the new thread.
      //

      BaseInitializeContext(
            &ThreadContext,
            Peb,
            ImageInformation.TransferAddress,
            InitialTeb.StackBase,
            BaseContextTypeProcess
            );


      //
      // Create the actual thread object
      //

      pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL);

      Status = NtCreateThread(
                  &ThreadHandle,
                  THREAD_ALL_ACCESS,
                  pObja,
                  ProcessHandle,
                  &ClientId,
                  &ThreadContext,
                  &InitialTeb,
                  TRUE
                  );

      if (!NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            return FALSE;
            }

      a->Peb = (ULONGLONG) Peb;

      //
      // From here on out, do not modify the address space of the
      // new process.WOW64's implementation of NtCreateThread()
      // reshuffles the new process' address space if the current
      // process is 32-bit and the new process is 64-bit.
      //
#if DBG
      Peb = NULL;
#endif

#if defined(WX86)

      //
      // if this is a Wx86 Process, setup for a Wx86 emulated Thread
      //

      if (Wx86Info) {

            //
            // create a WX86Tib and initialize it's Teb->Vdm.
            //
            Status = BaseCreateWx86Tib(ProcessHandle,
                                       ThreadHandle,
                                       (ULONG)((ULONG_PTR)ImageInformation.TransferAddress),
                                       (ULONG)ImageInformation.CommittedStackSize,
                                       (ULONG)ImageInformation.MaximumStackSize,
                                       TRUE
                                       );

            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                return( FALSE );
                }


            //
            // Mark Process as WX86
            //
            Status = NtSetInformationProcess (ProcessHandle,
                                              ProcessWx86Information,
                                              &Wx86Info,
                                              sizeof(Wx86Info)
                                              );

            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                return( FALSE );
                }
            }
#endif


      //
      // Call the Windows server to let it know about the
      // process.
      //

      a->ProcessHandle = ProcessHandle;
      a->ThreadHandle = ThreadHandle;
      a->ClientId = ClientId;

      switch (ImageInformation.Machine) {
      case IMAGE_FILE_MACHINE_I386:
#if defined(_WIN64) || defined(BUILD_WOW6432)
            a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64;
#else
            a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
#endif
            break;

      case IMAGE_FILE_MACHINE_IA64:
            a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
            break;

      case IMAGE_FILE_MACHINE_AMD64:
            a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
            break;

      default:
            DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", ImageInformation.Machine);
            a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
            break;
      }

      //
      // remove debug flags now its not being done by CSR
      //
      a->CreationFlags = dwCreationFlags & ~ (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
      a->DebuggerClientId.UniqueProcess = NULL;
      a->DebuggerClientId.UniqueThread = NULL;


      //
      // Set the 2 bit if a gui app is starting. The window manager needs to
      // know this so it can synchronize the startup of this app
      // (WaitForInputIdle api). This info is passed using the process
      // handle tag bits.The 1 bit asks the window manager to turn on
      // or turn off the application start cursor (hourglass/pointer).
      //
      // When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI
      // process.We also turn on bit 0x8 so that UserSrv can ignore the
      // UserNotifyConsoleApplication call made by the console during startup.
      //

      if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI ||
             IsWowBinary ) {

            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2);

            //
            // If the creating process is a GUI app, turn on the app. start cursor
            // by default.This can be overridden by STARTF_FORCEOFFFEEDBACK.
            //

            NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL));
            if ( NtHeaders
               && (NtHeaders->OptionalHeader.Subsystem
                     == IMAGE_SUBSYSTEM_WINDOWS_GUI ) ) {
                a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
                }
            }


      //
      // If feedback is forced on, turn it on. If forced off, turn it off.
      // Off overrides on.
      //

      if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
      if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
            a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1);

      a->VdmBinaryType = VdmBinaryType; // just tell server the truth

      if (VdmBinaryType){
         a->hVDM    = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle;
         a->VdmTask = iTask;
      }

#if defined(BUILD_WOW6432)
      m.ReturnValue = CsrBasepCreateProcess(a);
#else
      m.u.CreateProcess = *a;
      if (m.u.CreateProcess.Sxs.Flags != 0)
      {
            const PUNICODE_STRING StringsToCapture[] =
            {
                &m.u.CreateProcess.Sxs.Manifest.Path,
                  &m.u.CreateProcess.Sxs.Policy.Path,
                  &m.u.CreateProcess.Sxs.AssemblyDirectory
            };

            Status =
                CsrCaptureMessageMultiUnicodeStringsInPlace(
                &CaptureBuffer,
                RTL_NUMBER_OF(StringsToCapture),
                StringsToCapture
                );
            if (!NT_SUCCESS(Status)) {
                BaseSetLastNTError(Status);
                return FALSE;
            }
      }

      CsrClientCallServer( (PCSR_API_MSG)&m,
                           CaptureBuffer,
                           CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
                                                BasepCreateProcess
                                                ),
                           sizeof( *a )
                           );

      if ( CaptureBuffer ) {
            CsrFreeCaptureBuffer( CaptureBuffer );
            CaptureBuffer = NULL;
      }

#endif

      if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
            BaseSetLastNTError((NTSTATUS)m.ReturnValue);
            NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue);
            return FALSE;
            }



      //
      // If the WinSafer sandboxing policy indicates that the
      // process needs to be run with a restricted token or placed
      // into a restricted job object, then do those actions now.
      // Do not replace the token if the restricted token was created
      // from a caller supplied token i.e. the CreateProcessAsUser case.
      //
      if ((hSaferRestrictedToken != NULL) && (hUserToken == NULL)) {
            Status = BasepReplaceProcessThreadTokens(
                  hSaferRestrictedToken,
                  ProcessHandle,
                  ThreadHandle);
            if (!NT_SUCCESS(Status)) {
                // kill and cleanup.
                NtTerminateProcess(ProcessHandle, Status);
                BaseSetLastNTError(Status);
                return FALSE;
            }
      }
      if (hSaferAssignmentJob != NULL) {
            Status = NtAssignProcessToJobObject(
                  hSaferAssignmentJob, ProcessHandle);
            if (!NT_SUCCESS(Status)) {
                // kill and cleanup.
                NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
                BaseSetLastNTError(Status);
                return FALSE;
            }
      }


      //
      // Make the thread start execution if we are allowed to.
      //
      if (!( dwCreationFlags & CREATE_SUSPENDED) ) {
            NtResumeThread(ThreadHandle,&i);
            }

VdmExists:
      bStatus = TRUE;
      if (VDMCreationState)
            VDMCreationState |= VDM_CREATION_SUCCESSFUL;

      try {
            if (VdmWaitHandle) {

                //
                // tag Shared WOW VDM handles so that wait for input idle has a
                // chance to work.Shared WOW VDM "process" handles are actually
                // event handles,Separate WOW VDM handles are real process
                // handles. Also mark DOS handles with 0x1 so WaitForInputIdle
                // has a way to distinguish DOS apps and not block forever.
                //

                if (VdmBinaryType == BINARY_TYPE_WIN16){
                  lpProcessInformation->hProcess =
                            (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2);

                  //
                  // Shared WOW doesn't always start a process, so
                  // we don't have a process ID or thread ID to
                  // return if the VDM already existed.
                  //
                  // Separate WOW doesn't hit this codepath
                  // (no VdmWaitHandle).
                  //

                  if (VDMCreationState & VDM_BEING_REUSED) {
                        ClientId.UniqueProcess = 0;
                        ClientId.UniqueThread = 0;
                        }

                  }
                else{
                  lpProcessInformation->hProcess =
                            (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1);
                  }


                //
                // Close the ProcessHandle, since we are returning the
                // VdmProcessHandle instead.
                //

                if (ProcessHandle != NULL)
                  NtClose(ProcessHandle);
                }
            else{
                lpProcessInformation->hProcess = ProcessHandle;
                }

            lpProcessInformation->hThread = ThreadHandle;
            lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
            lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
            ProcessHandle = NULL;
            ThreadHandle = NULL;
            }
      __except ( EXCEPTION_EXECUTE_HANDLER ) {
            NtClose( ProcessHandle );
            NtClose( ThreadHandle );
            ProcessHandle = NULL;
            ThreadHandle = NULL;
            if (VDMCreationState)
                VDMCreationState &= ~VDM_CREATION_SUCCESSFUL;
            }
      }
    __finally {

      if (ExePathFullBuffer != NULL) {
            SxsWin32ExePath.Buffer = NULL;
            SxsWin32ExePath.Length = 0;
            SxsWin32ExePath.MaximumLength = 0;
            RtlFreeHeap(RtlProcessHeap(), 0, ExePathFullBuffer);
            ExePathFullBuffer = NULL;
      }

      if (!VdmBinaryType) {
            NTSTATUS Status1;

            BasepSxsCloseHandles(&SxsManifestFileHandles);
            BasepSxsCloseHandles(&SxsPolicyHandles);
            //
            // don't close SxsExeHandles, they are
            // aliases of other variables that are either closed
            // or returned to the caller
            //

            //
            // This loop only really frees any memory if our computation
            // of the overall buffer size was too low, which it is not supposed to be.
            //
            if (SxsConglomeratedByteBuffer != NULL) {
                for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
                  RtlFreeUnicodeStringBuffer(SxsStringBuffers);
                  }
                RtlFreeHeap(RtlProcessHeap(), 0,SxsConglomeratedByteBuffer);
                }
            }
      if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
            RtlDestroyEnvironment(lpEnvironment);
            lpEnvironment = NULL;
            }
      RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer);
      RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer);
      RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer);
      RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
      if ( FileHandle ) {
            NtClose(FileHandle);
            }
      if ( SectionHandle ) {
            NtClose(SectionHandle);
            }
      if ( ThreadHandle ) {
            NtTerminateProcess(ProcessHandle,STATUS_SUCCESS);
            NtClose(ThreadHandle);
            }
      if ( ProcessHandle ) {
            NtClose(ProcessHandle);
            }
      if ( hSaferAssignmentJob ) {
            NtClose(hSaferAssignmentJob);
            }
      if ( hSaferRestrictedToken ) {
            if (hUserToken == NULL) {
                // CreateProcess case
                NtClose(hSaferRestrictedToken);
                }
            else{
                // CreateProcessAsUser case
                *hRestrictedUserToken = hSaferRestrictedToken;
                }
            }

      if (NULL != pAppCompatData) {
            RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
            }

      if (NULL != pAppCompatSxsData) {
            RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
            }

      RtlFreeUnicodeString(&VdmNameString);
      RtlFreeUnicodeString(&SubSysCommandLine);
      if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer)
            BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv);

      if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){
            BaseUpdateVDMEntry (
                UPDATE_VDM_UNDO_CREATION,
                (HANDLE *)&iTask,
                VDMCreationState,
                VdmBinaryType
                );
            if(VdmWaitHandle) {
                NtClose(VdmWaitHandle);
                }
            }
      }

    if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
      RtlDestroyEnvironment(lpEnvironment);
      }
    return bStatus;
}

BOOL
WINAPI
CreateProcessW(
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
    )

/*++

Routine Description:

    A process and thread object are created and a handle opened to each
    object using CreateProcess.Note that WinExec and LoadModule are
    still supported, but are implemented as a call to CreateProcess.

Arguments:

    lpApplicationName - Supplies an optional pointer to a null terminated
      character string that contains the name of the image file to
      execute.This is a fully qualified DOS path name.If not
      specified, then the image file name is the first whitespace
      delimited token on the command line.

    lpCommandLine - Supplies a null terminated character string that
      contains the command line for the application to be executed.
      The entire command line is made available to the new process
      using GetCommandLine.If the lpApplicationName parameter was
      not specified, then the first token of the command line
      specifies file name of the application (note that this token
      begins at the beginning of the command line and ends at the
      first "white space" character).If the file name does not
      contain an extension (the presence of a "."), then .EXE is
      assumed.If the file name does not contain a directory path,
      Windows will search for the executable file in:

          - The current directory

          - The windows directory

          - The windows system directory

          - The directories listed in the path environment variable

      This parameter is optional onlu if the lpApplicationName
      parameter is specified.In this case the command line the
      application receives will be the application name.

    lpProcessAttributes - An optional parameter that may be used to
      specify the attributes of the new process.If the parameter is
      not specified, then the process is created without a security
      descriptor, and the resulting handle is not inherited on process
      creation:

      SECURITY_ATTRIBUTES Structure:

      DWORD nLength - Specifies the length of this structure.Must be
            set to sizeof( SECURITY_ATTRUBUTES ).

      LPVOID lpSecurityDescriptor - Points to a security descriptor for
            the object (must be NULL for Win32, used on NT/Win32). The
            security descriptor controls the sharing of an object.

      BOOL bInheritHandle - Supplies a flag that indicates whether
            or not the returned handle is to be inherited by a new
            process during process creation.A value of TRUE
            indicates that the new process will inherit the handle.

    lpThreadAttributes - An optional parameter that may be used to specify
      the attributes of the new thread.If the parameter is not
      specified, then the thread is created without a security
      descriptor, and the resulting handle is not inherited on
      process creation.

    dwCreationFlags - Supplies additional flags that control the creation
      of the process.

      dwCreationFlags Flags:

      DEBUG_PROCESS - If this flag bit is set, then the creating
            process is treated as a debugger, and the process being
            created is created as a debugee.All debug events occuring
            in the debugee are reported to the debugger.If this bit is
            clear, but the calling process is a debugee, then the
            process becomes a debugee of the calling processes debugger.
            If this bit is clear and the calling processes is not a
            debugee then no debug related actions occur.

      DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
            DEBUG_PROCESS flag bit must also be set.The calling
            process is is treated as a debugger, and the new process is
            created as its debuggee.If the new process creates
            additional processes, no debug related activities (with
            respect to the debugger) occur.

      CREATE_SUSPENDED - The process is created, but the initial thread
            of the process remains suspended. The creator can resume this
            thread using ResumeThread. Until this is done, code in the
            process will not execute.

      CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
            points to a Unicode environment block.Otherwise, the
            block is ANSI (actually OEM.)

    bInheritHandles - Supplies a flag that specifies whether or not the
      new process is to inherit handles to objects visible to the
      calling process.A value of TRUE causes handles to be inherited
      by the new process.If TRUE was specified, then for each handle
      visible to the calling process, if the handle was created with
      the inherit handle option, the handle is inherited to the new
      process.The handle has the same granted access in the new
      process as it has in the calling process, and the value of the
      handle is the same.

    lpEnvironment - An optional parameter, that if specified, supplies a
      pointer to an environment block.If the parameter is not
      specified, the environment block of the current process is used.
      This environment block is made available to the new process
      using GetEnvironmentStrings.

    lpCurrentDirectory - An optional parameter, that if specified,
      supplies a string representing the current drive and directory
      for the new process.The string must be a fully qualified
      pathname that includes a drive letter.If the parameter is not
      specified, then the new process is created with the same current
      drive and directory as the calling process.This option is
      provided primarily for shells that want to start an application
      and specify its initial drive and working directory.

    lpStartupInfo - Supplies information that specified how the
      applications window is to be shown. This structure is described
      in the Win32 User Interface API Book.

    lpProcessInformation - Returns identification information about the
      new process.

    PROCESS_INFORMATION Structure:

      HANDLE hProcess - Returns a handle to the newly created process.
            Through the handle, all operations on process objects are
            allowed.

      HANDLE hThread - Returns a handle to the newly created thread.
            Through the handle, all operations on thread objects are
            allowed.

      DWORD dwProcessId - Returns a global process id that may be used
            to identify a process.The value is valid from the time the
            process is created until the time the process is terminated.

      DWORD dwThreadId - Returns a global thread id that may be used
            to identify a thread.The value is valid from the time the
            thread is created until the time the thread is terminated.

Return Value:

    TRUE - The operation was successful

    FALSE/NULL - The operation failed. Extended error status is available
      using GetLastError.

--*/

{
    return CreateProcessInternalW(
               NULL, // Create new process with the token on the creator process
               lpApplicationName,
               lpCommandLine,
               lpProcessAttributes,
               lpThreadAttributes,
               bInheritHandles,
               dwCreationFlags,
               lpEnvironment,
               lpCurrentDirectory,
               lpStartupInfo,
               lpProcessInformation,
               NULL// Do not return the restricted token
               );
}
页: [1]
查看完整版本: 【Win32API】关于CreateProcess的第二个参数的研究