/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WKMAN.C
 *  WOW32 16-bit Kernel API support (manually-coded thunks)
 *
 *  History:
 *  Created 16-Apr-2001 jarbats
 *
--*/

#include "precomp.h"
#pragma hdrstop



/*
 *  shimdb has ..,.. which conflicts with the typedef TAG used in winuserp.h
 *  so we redfine it here and put all the code which uses shimdb interfaces
 *  in this file.
 *
 */

#ifdef TAG
#undef TAG
#endif
#define TAG _SHIMDB_WORDTAG
#include "shimdb.h"

#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "zwapi.h"

#define _wshimdb
#include "wshimdb.h"


MODNAME(wshimdb.c);




CHAR g_szCompatLayerVar[]    = "__COMPAT_LAYER";
CHAR g_szProcessHistoryVar[] = "__PROCESS_HISTORY";
CHAR g_szShimFileLogVar[]    = "SHIM_FILE_LOG";

UNICODE_STRING g_ustrProcessHistoryVar = RTL_CONSTANT_STRING(L"__PROCESS_HISTORY");
UNICODE_STRING g_ustrCompatLayerVar    = RTL_CONSTANT_STRING(L"__COMPAT_LAYER"   );
UNICODE_STRING g_ustrShimFileLogVar    = RTL_CONSTANT_STRING(L"SHIM_FILE_LOG"    );

BOOL CheckAppHelpInfo(PTD pTD,PSZ szFileName,PSZ szModName) {

BOOL           fReturn = TRUE;
NTVDM_FLAGS    NtVdmFlags = { 0 };

WCHAR          wszFileName[256];
WCHAR          wszModName[16];

WCHAR          *pwszTempEnv   = NULL;
PSZ            pszEnvTemplate = NULL;
PWOWENVDATA    pWowEnvData    = NULL;
PTD            pTDParent      = NULL;

WCHAR          wszCompatLayer[COMPATLAYERMAXLEN];

APPHELP_INFO   AHInfo;

HANDLE         hProcess;

        ghTaskAppHelp = NULL;

        RtlOemToUnicodeN(
                         wszFileName,
                         sizeof(wszFileName),
                         NULL,
                         szFileName,
                         strlen(szFileName) + 1
                        );

        RtlOemToUnicodeN(
                         wszModName,
                         sizeof(wszModName),
                         NULL,
                         szModName,
                         strlen(szModName) + 1
                         );

        //
        // find parent TD -- it contains the environment we need to
        // pass into the detection routine plus wowdata
        //

        pTDParent = GetParentTD(pTD->htask16);

        if (NULL != pTDParent) {
            pWowEnvData = pTDParent->pWowEnvDataChild;
        }

        pszEnvTemplate = GetTaskEnvptr(pTD->htask16);
        pwszTempEnv    = WOWForgeUnicodeEnvironment(pszEnvTemplate, pWowEnvData) ;


        wszCompatLayer[0] = UNICODE_NULL;
        AHInfo.tiExe      = 0;

        fReturn = ApphelpGetNTVDMInfo(wszFileName,
                                      wszModName,
                                      pwszTempEnv,
                                      wszCompatLayer,
                                      &NtVdmFlags,
                                      &AHInfo);

        if (pwszTempEnv != NULL) {
            WOWFreeUnicodeEnvironment(pwszTempEnv);
        }

        if (fReturn && AHInfo.tiExe) {
            fReturn = ApphelpShowDialog(&AHInfo,&hProcess);
            if (fReturn && hProcess) {
                ghTaskAppHelp = hProcess;
            }
        }

        WOWInheritEnvironment(pTD, pTDParent, wszCompatLayer, szFileName);

        pTD->dwWOWCompatFlags     = NtVdmFlags.dwWOWCompatFlags;
        pTD->dwWOWCompatFlagsEx   = NtVdmFlags.dwWOWCompatFlagsEx;
        pTD->dwUserWOWCompatFlags = NtVdmFlags.dwUserWOWCompatFlags;
        pTD->dwWOWCompatFlags2    = NtVdmFlags.dwWOWCompatFlags2;
#ifdef FE_SB
        pTD->dwWOWCompatFlagsFE   = NtVdmFlags.dwWOWCompatFlagsFE;
#endif // FE_SB

        // Clean up starts here
        return fReturn;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Welcome to Win2kPropagateLayer which manages everything in the environment
// related to shims/detection
//

//
// this function is called during the parent tasks pass_environment call
// we are still in the context of the parent, which means that:
// CURRENTPTD() gives us parents' TD
// *pCurTDB     gives us parents' TDB
//

//
// get the pointer to task database block from hTask
//


PTDB
GetTDB(
    HAND16 hTask
    )
{
    PTDB pTDB;

    pTDB = (PTDB)SEGPTR(hTask, 0);
    if (NULL == pTDB || TDB_SIGNATURE != pTDB->TDB_sig) {
        return NULL;
    }

    return pTDB;
}

PSZ
GetTaskEnvptr(
    HAND16 hTask
    )
{
    PTDB pTDB = GetTDB(hTask);
    PSZ  pszEnv = NULL;
    PDOSPDB pPSP;

    if (NULL == pTDB) {
        return NULL;
    }

    //
    // Prepare environment data - this buffer is used when we're starting a new task from the
    // root of the chain (as opposed to spawning from an existing 16-bit task)
    //

    pPSP   = (PDOSPDB)SEGPTR(pTDB->TDB_PDB, 0); // psp

    if (pPSP != NULL) {
        pszEnv = (PCH)SEGPTR(pPSP->PDB_environ, 0);
    }

    return pszEnv;
}


PTD
GetParentTD(
    HAND16 hTask
    )
{
    PTDB pTDB = GetTDB(hTask);
    PTDB pTDBParent;
    PTD  ptdCur = NULL;
    HAND16 hTaskParent;

    if (NULL == pTDB) {
        return NULL; // cannot get that task
    }

    //
    // now, retrieve the TD for the parent.
    //

    hTaskParent = pTDB->TDB_Parent;

    pTDBParent = GetTDB(hTaskParent);

    if (NULL == pTDBParent) {
        // can's see the parent
        return NULL;
    }

    //
    // so we can see what the parent is up to
    //
    // pTDBParent->TDB_ThreadID and
    // hTaskParent are the dead giveaway
    //

    ptdCur = gptdTaskHead;
    while (NULL != ptdCur) {
        if (ptdCur->dwThreadID == pTDBParent->TDB_ThreadID &&
            ptdCur->htask16    == hTaskParent) {

            break;
        }
        ptdCur = ptdCur->ptdNext;
    }

    //
    // if ptdCur == NULL -- we have not been able to locate the parent tasks' ptd
    //

    return ptdCur;
}



BOOL
IsWowExec(
    WORD wTask
    )
{
    return (ghShellTDB == wTask);
}


//
// This function is called in the context of pass_environment
//
//
//


BOOL
CreateWowChildEnvInformation(
    PSZ           pszEnvParent
    )
{
    PTD         pTD; // parent TD

    WOWENVDATA  EnvData;
    PWOWENVDATA pData = NULL;
    PWOWENVDATA pEnvChildData = NULL;
    DWORD       dwLength;
    PCH         pBuffer;


    RtlZeroMemory(&EnvData, sizeof(EnvData));

    //
    // check where we should inherit process history and layers from
    //
    pTD = CURRENTPTD();

    if (pTD->pWowEnvDataChild) {
        free_w(pTD->pWowEnvDataChild);
        pTD->pWowEnvDataChild = NULL;
    }

    //
    // check whether we are starting the root task (meaning that this task IS wowexec)
    // if so, we shall inherit things from pParamBlk->envseg
    // else we use TD of the parent task (this TD that is) to
    // inherit things
    // How to detect that this is wowexec:
    // ghShellTDB could we compared to *pCurTDB
    // gptdShell->hTask16 could be compared to *pCurTDB
    // we check for not having wowexec -- if gptdShell == NULL then we're doing boot
    //

    if (pCurTDB == NULL || IsWowExec(*pCurTDB)) {
        //
        // presumably we are wowexec
        // use current environment ptr to get stuff (like pParamBlk->envseg)
        // or the original ntvdm environment
        //
        pData  = &EnvData;

        pData->pszProcessHistory = WOWFindEnvironmentVar(g_szProcessHistoryVar,
                                                         pszEnvParent,
                                                         &pData->pszProcessHistoryVal);
        pData->pszCompatLayer    = WOWFindEnvironmentVar(g_szCompatLayerVar,
                                                         pszEnvParent,
                                                         &pData->pszCompatLayerVal);
        pData->pszShimFileLog    = WOWFindEnvironmentVar(g_szShimFileLogVar,
                                                         pszEnvParent,
                                                         &pData->pszShimFileLogVal);

    } else {

        //
        // this current task is not a dastardly wowexec
        // clone current + enhance process history
        //

        pData = pTD->pWowEnvData; // if this is NULL
        if (pData == NULL) {
            pData = &EnvData; // all the vars are empty
        }

    }

    //
    //
    //
    //

    dwLength = sizeof(WOWENVDATA) +
               (NULL == pData->pszProcessHistory        ? 0 : (strlen(pData->pszProcessHistory)        + 1) * sizeof(CHAR)) +
               (NULL == pData->pszCompatLayer           ? 0 : (strlen(pData->pszCompatLayer)           + 1) * sizeof(CHAR)) +
               (NULL == pData->pszShimFileLog           ? 0 : (strlen(pData->pszShimFileLog)           + 1) * sizeof(CHAR)) +
               (NULL == pData->pszCurrentProcessHistory ? 0 : (strlen(pData->pszCurrentProcessHistory) + 1) * sizeof(CHAR));


    pEnvChildData = (PWOWENVDATA)malloc_w(dwLength);

    if (pEnvChildData == NULL) {
        return FALSE;
    }

    RtlZeroMemory(pEnvChildData, dwLength);

    //
    // now this entry has to be setup
    // process history is first
    //

    pBuffer = (PCH)(pEnvChildData + 1);

    if (pData->pszProcessHistory != NULL) {

        //
        // Copy process history. The processHistoryVal is a pointer into the buffer
        // pointed to by pszProcessHistory: __PROCESS_HISTORY=c:\foo;c:\docs~1\install
        // then pszProcessHistoryVal will point here ---------^
        //
        // we are copying the data and moving the pointer using the calculated offset

        pEnvChildData->pszProcessHistory = pBuffer;
        strcpy(pEnvChildData->pszProcessHistory, pData->pszProcessHistory);
        pEnvChildData->pszProcessHistoryVal = pEnvChildData->pszProcessHistory +
                                                 (INT)(pData->pszProcessHistoryVal - pData->pszProcessHistory);
        //
        // There is enough space in the buffer to accomodate all the strings, so
        // move pointer past current string to point at the "empty" space
        //

        pBuffer += strlen(pData->pszProcessHistory) + 1;
    }

    if (pData->pszCompatLayer != NULL) {
        pEnvChildData->pszCompatLayer = pBuffer;
        strcpy(pEnvChildData->pszCompatLayer, pData->pszCompatLayer);
        pEnvChildData->pszCompatLayerVal = pEnvChildData->pszCompatLayer +
                                              (INT)(pData->pszCompatLayerVal - pData->pszCompatLayer);
        pBuffer += strlen(pData->pszCompatLayer) + 1;
    }

    if (pData->pszShimFileLog != NULL) {
        pEnvChildData->pszShimFileLog = pBuffer;
        strcpy(pEnvChildData->pszShimFileLog, pData->pszShimFileLog);
        pEnvChildData->pszShimFileLogVal = pEnvChildData->pszShimFileLog +
                                              (INT)(pData->pszShimFileLogVal - pData->pszShimFileLog);
        pBuffer += strlen(pData->pszShimFileLog) + 1;
    }

    if (pData->pszCurrentProcessHistory != NULL) {
        //
        // Now process history
        //
        pEnvChildData->pszCurrentProcessHistory = pBuffer;

        if (pData->pszCurrentProcessHistory != NULL) {
            strcpy(pEnvChildData->pszCurrentProcessHistory, pData->pszCurrentProcessHistory);
        }

    }

    //
    // we are done, environment cloned
    //

    pTD->pWowEnvDataChild = pEnvChildData;

    return TRUE;
}

//
// In : pointer to environment(oem)
// out: pointer to unicode environment
//

NTSTATUS
WOWCloneEnvironment(
    LPVOID* ppEnvOut,
    PSZ     lpEnvironment
    )
{
    NTSTATUS Status    = STATUS_INVALID_PARAMETER;
    DWORD    dwEnvSize = 0;
    LPVOID   lpEnvNew  = NULL;

    MEMORY_BASIC_INFORMATION MemoryInformation;

    if (NULL == lpEnvironment) {
        Status = RtlCreateEnvironment(TRUE, &lpEnvNew);
    } else {
        dwEnvSize = WOWGetEnvironmentSize(lpEnvironment, NULL);

        MemoryInformation.RegionSize = (dwEnvSize + 2) * sizeof(UNICODE_NULL);
        Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
                                         &lpEnvNew,
                                         0,
                                         &MemoryInformation.RegionSize,
                                         MEM_COMMIT,
                                         PAGE_READWRITE);
    }

    if (NULL != lpEnvironment) {

        UNICODE_STRING UnicodeBuffer;
        OEM_STRING     OemBuffer;

        OemBuffer.Buffer = (CHAR*)lpEnvironment;
        OemBuffer.Length = OemBuffer.MaximumLength = (USHORT)dwEnvSize; // size in bytes = size in chars, includes \0\0

        UnicodeBuffer.Buffer        = (WCHAR*)lpEnvNew;
        UnicodeBuffer.Length        = (USHORT)dwEnvSize * sizeof(UNICODE_NULL);
        UnicodeBuffer.MaximumLength = (USHORT)(dwEnvSize + 2) * sizeof(UNICODE_NULL); // leave room for \0

        Status = RtlOemStringToUnicodeString(&UnicodeBuffer, &OemBuffer, FALSE);
    }

    if (NT_SUCCESS(Status)) {
        *ppEnvOut = lpEnvNew;
    } else {
        if (NULL != lpEnvNew) {
            RtlDestroyEnvironment(lpEnvNew);
        }
    }

    return Status;
}

NTSTATUS
WOWFreeUnicodeEnvironment(
    LPVOID lpEnvironment
    )
{
    NTSTATUS Status;

    Status = RtlDestroyEnvironment(lpEnvironment);

    return Status;
}

//
// Set environment variable, possibly create or clone provided environment
//

NTSTATUS
WOWSetEnvironmentVar_U(
    LPVOID* ppEnvironment,
    WCHAR*  pwszVarName,
    WCHAR*  pwszVarValue
    )
{
    UNICODE_STRING ustrVarName;
    UNICODE_STRING ustrVarValue;
    NTSTATUS       Status;

    RtlInitUnicodeString(&ustrVarName, pwszVarName);

    if (NULL != pwszVarValue) {
        RtlInitUnicodeString(&ustrVarValue, pwszVarValue);
    }

    Status = RtlSetEnvironmentVariable(ppEnvironment,
                                       &ustrVarName,
                                       (NULL == pwszVarValue) ? NULL : &ustrVarValue);

    return Status;
}

NTSTATUS
WOWSetEnvironmentVar_Oem(
    LPVOID*         ppEnvironment,
    PUNICODE_STRING pustrVarName,     // pre-made (cheap)
    PSZ             pszVarValue
    )
{
    OEM_STRING OemString = { 0 };
    UNICODE_STRING ustrVarValue = { 0 };
    NTSTATUS Status;

    if (pszVarValue != NULL) {
        RtlInitString(&OemString, pszVarValue);

        Status = RtlOemStringToUnicodeString(&ustrVarValue, &OemString, TRUE);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }
    }

    Status = RtlSetEnvironmentVariable(ppEnvironment,
                                       pustrVarName,
                                       (NULL == pszVarValue) ? NULL : &ustrVarValue);

    if (NULL != pszVarValue) {
        RtlFreeUnicodeString(&ustrVarValue);
    }

    return Status;
}


//
// Call this function to produce a "good" unicode environment
//
//


LPWSTR
WOWForgeUnicodeEnvironment(
    PSZ pEnvironment,     // this task's sanitized environment
    PWOWENVDATA pEnvData    // parent-made environment data
    )
{
    NTSTATUS Status;
    LPVOID   lpEnvNew = NULL;

    DWORD    dwProcessHistoryLength = 0;
    PSZ      pszFullProcessHistory = NULL;


    Status = WOWCloneEnvironment(&lpEnvNew, pEnvironment);
    if (!NT_SUCCESS(Status)) {
        return NULL;
    }

    //
    // we do have an env to work with
    //
    RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrProcessHistoryVar, NULL);
    RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrCompatLayerVar,    NULL);
    RtlSetEnvironmentVariable(&lpEnvNew, &g_ustrShimFileLogVar,    NULL);

    //
    // get stuff from envdata
    //

    if (pEnvData == NULL) {
        goto Done;
    }

    if (pEnvData->pszProcessHistory != NULL || pEnvData->pszCurrentProcessHistory != NULL) {

        //
        // Convert the process history which consists of 2 strings.
        //
        // The length is the existing process history length + 1 (for ';') +
        // new process history length + 1 (for '\0')
        //
        dwProcessHistoryLength = ((pEnvData->pszProcessHistory        == NULL) ? 0 : (strlen(pEnvData->pszProcessHistoryVal) + 1)) +
                                 ((pEnvData->pszCurrentProcessHistory == NULL) ? 0 :  strlen(pEnvData->pszCurrentProcessHistory)) + 1;

        //
        // Allocate process history buffer and convert it, allocating resulting unicode string.
        //
        pszFullProcessHistory = (PCHAR)malloc_w(dwProcessHistoryLength);

        if (NULL == pszFullProcessHistory) {
            Status = STATUS_NO_MEMORY;
            goto Done;
        }

        *pszFullProcessHistory = '\0';

        if (pEnvData->pszProcessHistory != NULL) {
            strcpy(pszFullProcessHistory, pEnvData->pszProcessHistoryVal);
        }

        if (pEnvData->pszCurrentProcessHistory != NULL) {

            //
            // Append ';' if the string was not empty.
            //
            if (*pszFullProcessHistory) {
                strcat(pszFullProcessHistory, ";");
            }

            strcat(pszFullProcessHistory, pEnvData->pszCurrentProcessHistory);
        }

        Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
                                          &g_ustrProcessHistoryVar,
                                          pszFullProcessHistory);
        if (!NT_SUCCESS(Status)) {
            goto Done;
        }

    }

    //
    // deal with compatLayer
    //
    if (pEnvData->pszCompatLayerVal != NULL) {

        Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
                                          &g_ustrCompatLayerVar,
                                          pEnvData->pszCompatLayerVal);
        if (!NT_SUCCESS(Status)) {
            goto Done;
        }

    }

    if (pEnvData->pszShimFileLog != NULL) {
        Status = WOWSetEnvironmentVar_Oem(&lpEnvNew,
                                          &g_ustrShimFileLogVar,
                                          pEnvData->pszShimFileLogVal);
        if (!NT_SUCCESS(Status)) {
            goto Done;
        }
    }




Done:

    if (pszFullProcessHistory != NULL) {
        free_w(pszFullProcessHistory);
    }


    if (!NT_SUCCESS(Status) && lpEnvNew != NULL) {
        //
        // This points to the cloned environment ALWAYS.
        //
        RtlDestroyEnvironment(lpEnvNew);
        lpEnvNew = NULL;
    }

    return(LPWSTR)lpEnvNew;
}

//
// GetModName
//   wTDB      - TDB entry
//   szModName - pointer to the buffer that receives module name
//               buffer should be at least 9 characters long
//
// returns FALSE if the entry is invalid


BOOL
GetWOWModName(
    WORD wTDB,
    PCH  szModName
    )
{
    PTDB pTDB;
    PCH  pch;

    pTDB = GetTDB(wTDB);
    if (NULL == pTDB) {
        return FALSE;
    }

    RtlCopyMemory(szModName, pTDB->TDB_ModName, 8 * sizeof(CHAR)); // we have modname now
    szModName[8] = '\0';

    pch = &szModName[8];
    while (*(--pch) == ' ') {
        *pch = 0;
    }

    return TRUE;
}


// IsWowExec
//      IN wTDB - entry into the task database
// Returns:
//      TRUE if this particular entry points to WOWEXEC
//
// Note:
//      WOWEXEC is a special stub module that always runs on NTVDM
//      new tasks are spawned by wowexec (in the most typical case)
//      it is therefore the "root" module and it's environment's contents
//      should not be counted, since we don't know what was ntvdm's parent process
//

BOOL
IsWOWExecBoot(
    WORD wTDB
    )
{
    PTDB pTDB;
    CHAR szModName[9];

    pTDB = GetTDB(wTDB);
    if (NULL == pTDB) {
        return FALSE;
    }

    if (!GetWOWModName(wTDB, szModName)) { // can we get modname ?
        return FALSE;
    }

    return (0 == _strcmpi(szModName, "wowexec")); // is the module named WOWEXEC ?
}


BOOL
WOWInheritEnvironment(
    PTD     pTD,          // this TD
    PTD     pTDParent,    // parent TD
    LPCWSTR pwszLayers,   // new layers var
    LPCSTR  pszFileName   // exe filename
    )
{
    UNICODE_STRING ustrLayers = { 0 };
    OEM_STRING     oemLayers  = { 0 };
    PWOWENVDATA    pEnvData       = NULL;
    PWOWENVDATA    pEnvDataParent = NULL;
    DWORD          dwLength = sizeof(WOWENVDATA);
    BOOL           bSuccess = FALSE;
    PCH            pBuffer;


    // assert (pszFileName != NULL)

    // check if this is dreaded wowexec
    if (IsWOWExecBoot(pTD->htask16)) {
        return TRUE;
    }


    if (NULL != pwszLayers) {
        RtlInitUnicodeString(&ustrLayers, pwszLayers);
        RtlUnicodeStringToOemString(&oemLayers, &ustrLayers, TRUE);
    }

    if (pTDParent != NULL) {
        pEnvDataParent = pTDParent->pWowEnvDataChild; // from parent, created for our consumption
    }

    //
    // inherit process history (normal that is)
    //
    if (pEnvDataParent != NULL) {
       dwLength += pEnvDataParent->pszProcessHistory        == NULL ? 0 : strlen(pEnvDataParent->pszProcessHistory) + 1;
       dwLength += pEnvDataParent->pszShimFileLog           == NULL ? 0 : strlen(pEnvDataParent->pszShimFileLog)    + 1;
       dwLength += pEnvDataParent->pszCurrentProcessHistory == NULL ? 0 : strlen(pEnvDataParent->pszCurrentProcessHistory) + 1;
    }

    dwLength += oemLayers.Length != 0 ? oemLayers.Length + 1 + strlen(g_szCompatLayerVar) + 1 : 0; // length for layers
    dwLength += strlen(pszFileName) + 1;

    //
    // now all components are done, allocate
    //

    pEnvData = (PWOWENVDATA)malloc_w(dwLength);
    if (pEnvData == NULL) {
        goto out;
    }

    RtlZeroMemory(pEnvData, dwLength);

    pBuffer = (PCH)(pEnvData + 1);

    if (pEnvDataParent != NULL) {
        if (pEnvDataParent->pszProcessHistory) {
            pEnvData->pszProcessHistory = pBuffer;
            strcpy(pBuffer, pEnvDataParent->pszProcessHistory);
            pEnvData->pszProcessHistoryVal = pEnvData->pszProcessHistory +
                                             (INT)(pEnvDataParent->pszProcessHistoryVal - pEnvDataParent->pszProcessHistory);
            pBuffer += strlen(pBuffer) + 1;
        }

        if (pEnvDataParent->pszShimFileLog) {
            pEnvData->pszShimFileLog = pBuffer;
            strcpy(pBuffer, pEnvDataParent->pszShimFileLog);
            pEnvData->pszShimFileLogVal = pEnvData->pszShimFileLog +
                                             (INT)(pEnvDataParent->pszShimFileLogVal - pEnvDataParent->pszShimFileLog);
            pBuffer += strlen(pBuffer) + 1;
        }

   }

    if (oemLayers.Length) {
        pEnvData->pszCompatLayer = pBuffer;
        strcpy(pBuffer, g_szCompatLayerVar); // __COMPAT_LAYER
        strcat(pBuffer, "=");
        pEnvData->pszCompatLayerVal = pBuffer + strlen(pBuffer);
        strcpy(pEnvData->pszCompatLayerVal, oemLayers.Buffer);

        pBuffer += strlen(pBuffer) + 1;
    }

    //
    // Process History HAS to be the last item
    //

    pEnvData->pszCurrentProcessHistory = pBuffer;
    *pBuffer = '\0';

    if (pEnvDataParent != NULL) {
        if (pEnvDataParent->pszCurrentProcessHistory) {
            pEnvData->pszCurrentProcessHistory = pBuffer;
            strcat(pBuffer, pEnvDataParent->pszCurrentProcessHistory);
            strcat(pBuffer, ";");
        }
    }

    strcat(pEnvData->pszCurrentProcessHistory, pszFileName);

    bSuccess = TRUE;

out:
    RtlFreeOemString(&oemLayers);

    pTD->pWowEnvData = pEnvData;

    return bSuccess;
}