Can CreateProcessW's Unicode environment buffer be released immediately after return?

Sudhir Dubey 0 Reputation points
2026-10-18T11:51:16.3333333+00:00

When calling CreateProcessW with an explicit Unicode environment block through lpEnvironment and CREATE_UNICODE_ENVIRONMENT, is the environment input fully consumed or copied before the call returns?

Can the caller free its environment buffer immediately after CreateProcessW returns, including when the new process is created suspended or has not finished initialization?

We retain the buffer for the entire native call and need clarification of the documented post-return lifetime requirement.

Documentation: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw

Windows development | Windows API - Win32
0 comments No comments

2 answers

Sort by: Most helpful
  1. Percy Nguyen (WICLOUD CORPORATION) 160 Reputation points Microsoft External Staff Moderator
    2026-10-19T03:12:01.18+00:00

    Hi @Sudhir Dubey ,

    Thank you for posting your question.

    You can free (or reuse) the buffer you passed in lpEnvironment as soon as CreateProcessW returns. This applies whether the call succeeded or failed, whether you used CREATE_SUSPENDED, and whether the child has finished initializing. 

    What the documentation says:

    • The CreateProcessW reference annotates lpEnvironment as [in, optional]. An [in] parameter is read during the call and isn't kept by the function. The page puts no requirement on the buffer after the call returns. 
    • In the CreateEnvironmentBlock remarks. Once CreateProcessAsUser returns, the new process has its own copy of the environment block, and DestroyEnvironmentBlock can safely be called. That sentence is written for CreateProcessAsUser, but lpEnvironment and CREATE_UNICODE_ENVIRONMENT work the same way in CreateProcessW. 
    • The CreateProcessW page also says the function returns before the new process has finished initialization. That refers to the child's own startup, such as loading DLLs. It doesn't mean your input buffer is still in use. Creating the process suspended only stops the primary thread from running. It doesn't postpone the copy of the environment. 

    Please note this, the CreateProcessW page doesn't say "the environment block is copied before return" in so many words. The conclusion above is based on the [in] contract, the related CreateEnvironmentBlock statement, and how the function behaves.

    What I recommend:

    • Keeping the buffer alive for the whole native call, as you're doing now, is correct and enough. You don't need to wait for ResumeThread, for the child to finish initializing, or for it to exit. 
    • Free the buffer with the function that matches how it was allocated. Use DestroyEnvironmentBlock for blocks from CreateEnvironmentBlock. For your own allocations, use free, delete[], HeapFree and so on. 
    • If CreateProcessW fails, call GetLastError() before you free the buffer. 
    • The only resources that outlive the call are hProcess and hThread in PROCESS_INFORMATION. Close them with CloseHandle when you no longer need them. 
    
    BOOL created = CreateProcessW(app, cmdLine, nullptr, nullptr, FALSE, 
    
                                  CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, 
    
                                  envBlock, nullptr, &si, &pi); 
    
    DWORD err = created ? ERROR_SUCCESS : GetLastError(); 
    
    free(envBlock);   // safe here; use the matching deallocator 
    
    if (created) { 
    
        // ... ResumeThread(pi.hThread) when ready ... 
    
        CloseHandle(pi.hThread); 
    
        CloseHandle(pi.hProcess); 
    
    } 
    

    If you found my support helpful, I’d really appreciate it if you could follow this guide so others with similar issue can benefit as well. 

    Thank you.

    Was this answer helpful?


  2. AI answer

    2026-10-18T11:51:52.7866667+00:00

    Yes—after CreateProcessW returns, the caller may immediately release or reuse the buffer passed through lpEnvironment, regardless of success, CREATE_SUSPENDED, or whether the child has completed initialization.

    lpEnvironment is an [in, optional] parameter, so it must remain valid only for the duration of the call. The environment block must contain null-terminated name=value strings, end with an additional null terminator, and use CREATE_UNICODE_ENVIRONMENT when it contains Unicode data.^1^

    The ownership rule is also stated explicitly for an environment block passed during process creation: after the creation function returns, the new process has a copy and the original block can safely be destroyed.^2^ Creating the process suspended only prevents its primary thread from running; it does not defer consumption of the input parameters.

    BOOL created = CreateProcessW(
        application, commandLine,
        nullptr, nullptr, FALSE,
        CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED,
        environment,
        nullptr, &startupInfo, &processInfo);
    
    // CreateProcessW has returned; it no longer uses this allocation.
    free(environment); // Use the allocator-matching release operation.
    
    if (!created) {
        DWORD error = GetLastError(); // Capture before another Win32 call.
        // Handle error.
    }
    

    If the buffer came from CreateEnvironmentBlock, release it with DestroyEnvironmentBlock, not free, delete[], or HeapFree.^3^

    BOOL created = CreateProcessW(
        application, commandLine, nullptr, nullptr, FALSE,
        CREATE_UNICODE_ENVIRONMENT,
        environment, nullptr, &startupInfo, &processInfo);
    
    DWORD error = created ? ERROR_SUCCESS : GetLastError();
    DestroyEnvironmentBlock(environment);
    

    No wait for ResumeThread, process initialization, or process termination is required. Only the returned process and thread handles have an independent post-return lifetime and should be closed when no longer needed.


    References

    1. CreateProcessW function (processthreadsapi.h)
    2. CreateEnvironmentBlock function (userenv.h)
    3. DestroyEnvironmentBlock function (userenv.h)
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.