/*++

 Copyright (c) 2000 Microsoft Corporation

 Module Name:

   WoWTask.cpp

 Abstract:

   Functions that retrieve process-history related information from
   16-bit environment. This includes the retrieval of the correct
   __PROCESS_HISTORY that was passed in from the parent (32-bit)process
   and tracing the process history through WOW

 Notes:


 History:

   10/26/00 VadimB  Created

--*/


#include "precomp.h"

// This module has been given an official blessing to use the str routines.
#include "LegalStr.h"

IMPLEMENT_SHIM_BEGIN(Win2kPropagateLayer)
#include "ShimHookMacro.h"

#include "Win2kPropagateLayer.h"


typedef struct tagFINDWOWTASKDATA {
    BOOL  bFound;
    DWORD dwProcessId;
    DWORD dwThreadId;
    WORD  hMod16;
    WORD  hTask16;

} FINDWOWTASKDATA, *PFINDWOWTASKDATA;


//
// Dynamically Linked apis
//
//  from WOW32.dll
//

typedef LPVOID (WINAPI *PFNWOWGetVDMPointer)(DWORD vp,
                                             DWORD dwBytes,
                                             BOOL  fProtectedMode);

//
//  from vdmdbg.dll - defined in the header file
//
//

typedef INT (WINAPI *PFNVDMEnumTaskWOW)(DWORD        dwProcessId,
                                        TASKENUMPROC fp,
                                        LPARAM       lparam);

//
// Api importing -- modules
//
WCHAR g_wszWOW32ModName[]  = L"wow32.dll";
WCHAR g_wszVdmDbgModName[] = L"VdmDbg.dll";

//
// Api importing - module handles and function pointers
//

HMODULE g_hWow32;
HMODULE g_hVdmDbg;
BOOL    g_bInitialized;  // set to true when imports are initialized

PFNWOWGetVDMPointer  g_pfnWOWGetVDMPointer;
PFNVDMEnumTaskWOW    g_pfnVDMEnumTaskWOW;

extern BOOL* g_pSeparateWow;


//
// function in this module to import apis
//

BOOL ImportWowApis(VOID);


//
// Marcro to access 16-bit memory
//


#define SEGPTR(seg,off)  ((g_pfnWOWGetVDMPointer)((((ULONG)seg) << 16) | (off), 0, TRUE))

//
// task enum proc, called back from vdmdbg
//

BOOL WINAPI MyTaskEnumProc(
    DWORD dwThreadId,
    WORD  hMod16,
    WORD  hTask16,
    LPARAM lParam
    )
{
    PFINDWOWTASKDATA pFindData = (PFINDWOWTASKDATA)lParam;

    if (dwThreadId == pFindData->dwThreadId) {
        pFindData->hMod16  = hMod16;
        pFindData->hTask16 = hTask16;
        pFindData->bFound  = TRUE;
        return TRUE;
    }

    return FALSE;
}


BOOL FindWowTask(
    DWORD dwProcessId,
    DWORD dwThreadId,
    PFINDWOWTASKDATA pFindData
    )
{
    RtlZeroMemory(pFindData, sizeof(*pFindData));

    pFindData->dwProcessId = dwProcessId;
    pFindData->dwThreadId  = dwThreadId;

    g_pfnVDMEnumTaskWOW(dwProcessId, (TASKENUMPROC)MyTaskEnumProc, (LPARAM)pFindData);

    return pFindData->bFound;
}


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


PTDB
GetTDB(
    WORD wTDB
    )
{
    PTDB pTDB;

    pTDB = (PTDB)SEGPTR(wTDB, 0);
    if (NULL == pTDB || TDB_SIGNATURE != pTDB->TDB_sig) {
        LOGN(
            eDbgLevelError,
            "[GetTDB] TDB is invalid for task 0x%x",
            (DWORD)wTDB);
        return NULL;
    }

    return pTDB;
}


//
// 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
GetModName(
    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;
}


//
// ShimGetTaskFileName
//      IN wTask - 16-bit task handle
// Returns:
//      Fully qualified exe that is running in this task's context
//


PSZ
ShimGetTaskFileName(
    WORD wTask
    )
{
    PSZ pszFileName = NULL;
    PTDB pTDB;

    pTDB = GetTDB(wTask);
    if (NULL == pTDB) {
        // this is really bad -- the module is invalid, debug output is generated by GetTDB
        return pszFileName;
    }

    if (NULL == pTDB->TDB_pModule) {
        LOGN(
            eDbgLevelError,
            "[ShimGetTaskFileName] module pointer is NULL for 0x%x",
            (DWORD)wTask);
        return pszFileName;
    }

    pszFileName = (PSZ)SEGPTR(pTDB->TDB_pModule, (*(WORD *)SEGPTR(pTDB->TDB_pModule, 10)) + 8);
    return pszFileName;
}


PSZ 
ShimGetTaskEnvptr(
    WORD hTask16
    )
{
    PTDB pTDB = GetTDB(hTask16);
    PSZ  pszEnv = NULL;
    PDOSPDB pPSP;

    if (NULL == pTDB) {
        LOGN( eDbgLevelError, 
            "[ShimGetTaskEnvptr] Bad TDB entry 0x%x",  hTask16);
        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;
}
    

// 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
IsWOWExec(
    WORD wTDB
    )
{
    PTDB pTDB;
    PTDB pTDBParent;
    CHAR szModName[9];

    pTDB = GetTDB(wTDB);
    if (NULL == pTDB) {
        LOGN(
            eDbgLevelError,
            "[IsWOWExec] Bad TDB entry 0x%x",
            (DWORD)wTDB);
        return FALSE;
    }

    if (!GetModName(wTDB, szModName)) { // can we get modname ?
        LOGN(
            eDbgLevelError,
            "[IsWOWExec] GetModName failed.");
        return FALSE;
    }

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

//
// ImportWowApis
//      Function imports necessary apis from wow32.dll and vdmdbg.dll
//
//

BOOL
ImportWowApis(
    VOID
    )
{
    g_hWow32 = LoadLibraryW(g_wszWOW32ModName);
    
    if (g_hWow32 == NULL) {
        LOGN(
            eDbgLevelError,
            "[ImportWowApis] Failed to load wow32.dll Error 0x%x",
            GetLastError());
        goto Fail;
    }

    g_pfnWOWGetVDMPointer = (PFNWOWGetVDMPointer)GetProcAddress(g_hWow32, "WOWGetVDMPointer");
    
    if (g_pfnWOWGetVDMPointer == NULL) {
        LOGN(
            eDbgLevelError,
            "[ImportWowApis] Failed to get address of WOWGetVDMPointer Error 0x%x",
            GetLastError());
        goto Fail;
    }

    g_hVdmDbg = LoadLibraryW(g_wszVdmDbgModName);
    
    if (g_hVdmDbg == NULL) {
        LOGN(
            eDbgLevelError,
            "[ImportWowApis] Failed to load vdmdbg.dll Error 0x%x",
            GetLastError());
        goto Fail;
    }

    g_pfnVDMEnumTaskWOW = (PFNVDMEnumTaskWOW)GetProcAddress(g_hVdmDbg, "VDMEnumTaskWOW");
    
    if (g_pfnVDMEnumTaskWOW == NULL) {
        LOGN(
            eDbgLevelError,
            "[ImportWowApis] Failed to get address of VDMEnumTaskWOW Error 0x%x",
            GetLastError());
        goto Fail;
    }

    g_bInitialized = TRUE;

    return TRUE;

Fail:

    if (g_hWow32) {
        FreeLibrary(g_hWow32);
        g_hWow32 = NULL;
    }
    if (g_hVdmDbg) {
        FreeLibrary(g_hVdmDbg);
        g_hVdmDbg = NULL;
    }
    g_pfnWOWGetVDMPointer = NULL;
    g_pfnVDMEnumTaskWOW   = NULL;

    return FALSE;

}


/////////////////////////////////////////////////////////////////////////////////////////////
//
//
// WOWTaskList
//
// We maintain a shadow list of running wow tasks complete with respective process history and
// inherited process history
//
//

typedef struct tagWOWTASKLISTITEM* PWOWTASKLISTITEM;

typedef struct tagWOWTASKLISTITEM {

    WORD  hTask16;                 // 16-bit tdb entry

    DWORD dwThreadId;              // thread id of the task

    WOWENVDATA EnvData;            // environment data (process history, compat layer, etc)

    PWOWTASKLISTITEM pTaskNext;

} WOWTASKLISTITEM;

PWOWTASKLISTITEM g_pWowTaskList;


/*++

    FindWowTaskInfo

        IN hTask16                   16-bit task's handle
        IN dwThreadId OPTIONAL       32-bit thread id of the task, might be 0

    Returns: pointer to the task information structure

--*/

PWOWTASKLISTITEM
FindWowTaskInfo(
    WORD  hTask16,
    DWORD dwThreadId
    )
{
    PWOWTASKLISTITEM pTask = g_pWowTaskList;

    while (NULL != pTask) {

        if (hTask16 == pTask->hTask16) {

            if (dwThreadId == 0 || dwThreadId == pTask->dwThreadId) {
                break;
            }
        }

        pTask = pTask->pTaskNext;
    }

    return pTask;
}

/*++

    UpdateWowTaskList

        IN hTask16              16-bit task's handle

    Returns: True if the task was added successfully
    Note:    wowexec is not among the "legitimate" tasks

--*/


BOOL
UpdateWowTaskList(
    WORD hTask16
    )
{
    PTDB             pTDB;
    WORD             wTaskParent;
    PWOWTASKLISTITEM pTaskParent = NULL;
    LPSTR            lpszFileName;
    PSZ              pszEnv;
    WOWENVDATA       EnvData;
    PWOWENVDATA      pData = NULL;
    DWORD            dwLength;
    PWOWTASKLISTITEM pTaskNew;
    PCH              pBuffer;
    PDOSPDB          pPSP;
    BOOL             bSuccess;

    //
    // see that we are initialized, import apis
    //
    if (!g_bInitialized) { // first call, link apis
        bSuccess = ImportWowApis();
        if (!bSuccess) {
            LOGN(
                eDbgLevelError,
                "[UpdateWowTaskList] Failed to import apis.");
            return FALSE;
        }
    }

    //
    // If this task is WOWEXEC -- just return, it's not an error condition, but we don't need
    // wowexec in our list
    //

    if (IsWOWExec(hTask16)) {  // this is ok, we don't want wowexec
        return FALSE;
    }

    //
    // next, see what the parent item is, to do so -- access it through TDB
    //

    pTDB = GetTDB(hTask16);
    if (NULL == pTDB) {
        LOGN(
            eDbgLevelError,
            "[UpdateWowTaskList] Bad TDB entry 0x%x",
            hTask16);
        return FALSE;
    }

    //
    // 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)
    //

    RtlZeroMemory(&EnvData, sizeof(EnvData));
    pData = &EnvData;

    wTaskParent = pTDB->TDB_Parent;
    
    if (IsWOWExec(wTaskParent) || GetTDB(wTaskParent) == NULL) {
        //
        // Root task, extract process history, compat layer, etc
        //
        pszEnv = NULL;
        pPSP   = (PDOSPDB)SEGPTR(pTDB->TDB_PDB, 0); // psp
        
        if (pPSP != NULL) {
            pszEnv = (PCH)SEGPTR(pPSP->PDB_environ, 0);
        }

        //
        // we have a pointer to the current environment here, pData is initialized
        //
        if (pszEnv != NULL) {
            pData->pszProcessHistory = ShimFindEnvironmentVar(g_szProcessHistoryVar,
                                                              pszEnv,
                                                              &pData->pszProcessHistoryVal);
            
            pData->pszCompatLayer    = ShimFindEnvironmentVar(g_szCompatLayerVar,
                                                              pszEnv,
                                                              &pData->pszCompatLayerVal);
            
            pData->pszShimFileLog    = ShimFindEnvironmentVar(g_szShimFileLogVar,
                                                              pszEnv,
                                                              &pData->pszShimFileLogVal);
        }

    } else {
        //
        // Not a root task, find parent process
        //
        pTaskParent = FindWowTaskInfo(wTaskParent, 0); // we can't determine which thread owns the task

        if (pTaskParent == NULL) {
            //
            // something is very wrong
            // we can't inherit
            //

            LOGN(
                eDbgLevelError,
                "[UpdateWowTaskList] Task 0x%x is not root but parent not listed 0x%x",
                (DWORD)hTask16,
                (DWORD)wTaskParent);
            //
            // we still allow building up process history. The initial variables will be empty
            //


        } else {
            //
            // inherit everything from the parent and add it's module name (later)
            //

            pData = &pTaskParent->EnvData;
        }
    }

    //
    // Get the filename involved
    //
    //

    lpszFileName = ShimGetTaskFileName(hTask16);

    //
    // now calculate how much space is required to hold all of the data
    //

    dwLength = sizeof(WOWTASKLISTITEM) +
               (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)) +
               (NULL == lpszFileName                    ? 0 : (strlen(lpszFileName) + 1) * sizeof(CHAR));


    pTaskNew = (PWOWTASKLISTITEM)ShimMalloc(dwLength);
    
    if (pTaskNew == NULL) {
        LOGN(
            eDbgLevelError,
            "[UpdateWowTaskList] failed to allocate 0x%x bytes",
            dwLength);
        return FALSE;
    }

    RtlZeroMemory(pTaskNew, dwLength);

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

    pBuffer = (PCH)(pTaskNew + 1);

    pTaskNew->hTask16    = hTask16;
    pTaskNew->dwThreadId = GetCurrentThreadId();

    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

        pTaskNew->EnvData.pszProcessHistory = pBuffer;
        strcpy(pTaskNew->EnvData.pszProcessHistory, pData->pszProcessHistory);
        pTaskNew->EnvData.pszProcessHistoryVal = pTaskNew->EnvData.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) {
        pTaskNew->EnvData.pszCompatLayer = pBuffer;
        strcpy(pTaskNew->EnvData.pszCompatLayer, pData->pszCompatLayer);
        pTaskNew->EnvData.pszCompatLayerVal = pTaskNew->EnvData.pszCompatLayer +
                                              (INT)(pData->pszCompatLayerVal - pData->pszCompatLayer);
        pBuffer += strlen(pData->pszCompatLayer) + 1;
    }

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

    if (pData->pszCurrentProcessHistory != NULL || lpszFileName != NULL) {
        //
        // Now process history
        //
        pTaskNew->EnvData.pszCurrentProcessHistory = pBuffer;
        
        if (pData->pszCurrentProcessHistory != NULL) {
            strcpy(pTaskNew->EnvData.pszCurrentProcessHistory, pData->pszCurrentProcessHistory);
            strcat(pTaskNew->EnvData.pszCurrentProcessHistory, ";");
        }
        
        if (lpszFileName != NULL) {
            strcat(pTaskNew->EnvData.pszCurrentProcessHistory, lpszFileName);
        }
    }

    LOGN(
        eDbgLevelInfo,
        "[UpdateWowTaskList] Running           : \"%s\"",
        lpszFileName);
    
    LOGN(
        eDbgLevelInfo,
        "[UpdateWowTaskList] ProcessHistory    : \"%s\"",
        pTaskNew->EnvData.pszCurrentProcessHistory);
    
    LOGN(
        eDbgLevelInfo,
        "[UpdateWowTaskList] BaseProcessHistory: \"%s\"",
        pTaskNew->EnvData.pszProcessHistory);
    
    LOGN(
        eDbgLevelInfo,
        "[UpdateWowTaskList] CompatLayer       : \"%s\"",
        pTaskNew->EnvData.pszCompatLayer);


    //
    // We are done, link the entry into the list
    //
    pTaskNew->pTaskNext = g_pWowTaskList;

    g_pWowTaskList = pTaskNew;

    return TRUE;
}


/*++

    CleanupWowTaskList

        IN hTask16  16-bit task handle that is to be removed from the list of running tasks

    Returns : TRUE if the function succeeds

--*/

BOOL
CleanupWowTaskList(
    WORD hTask16
    )
{
    PWOWTASKLISTITEM pTask = g_pWowTaskList;
    PWOWTASKLISTITEM pTaskPrev = NULL;

    while (pTask != NULL) {

        if (pTask->hTask16 == hTask16) {
            // this is the item
            break;
        }

        pTaskPrev = pTask;
        pTask = pTask->pTaskNext;
    }

    if (pTask == NULL) {
        LOGN(
            eDbgLevelError,
            "[CleanupWowTaskList] Failed to locate task information for 0x%x",
            (DWORD)hTask16);
        return FALSE;
    }


    if (pTaskPrev == NULL) {

        g_pWowTaskList = pTask->pTaskNext;

    } else {

        pTaskPrev->pTaskNext = pTask->pTaskNext;

    }

    ShimFree(pTask);

    return TRUE;

}

/*++

    ShimRetrieveVariablesEx

        IN pData        Structure that receives pointers to all the relevant environment information
                        for the calling thread. The threads are scheduled non-preemptively by user and
                        threadid is used to identify the calling 16-bit task
                        All the real work on information retrieval is done in UpdateWowTaskList

    Returns: TRUE if success
--*/

BOOL
ShimRetrieveVariablesEx(
    PWOWENVDATA pData
    )
{
    DWORD            dwProcessId = GetCurrentProcessId();
    DWORD            dwThreadId  = GetCurrentThreadId();
    PWOWTASKLISTITEM pTask;
    FINDWOWTASKDATA  FindData;
    WORD             hTask;
    BOOL             bSuccess;

    RtlZeroMemory(pData, sizeof(*pData));

    if (!g_bInitialized) { // first call, link apis
        bSuccess = ImportWowApis();
        if (!bSuccess) {
            LOGN(
                eDbgLevelError,
                "[ShimRetrieveVariablesEx] Failed to import apis.");
            return FALSE;
        }
    }

    if (!FindWowTask(dwProcessId, dwThreadId, &FindData)) {
        LOGN(
            eDbgLevelError,
            "[ShimRetrieveVariablesEx] Task not found ProcessId 0x%x ThreadId 0x%x",
            dwProcessId,
            dwThreadId);
        return FALSE;
    }

    hTask = FindData.hTask16;

    pTask = FindWowTaskInfo(hTask, dwThreadId);
    
    if (pTask == NULL) {
        LOGN(
            eDbgLevelError,
            "[ShimRetrieveVariablesEx] Failed to locate wow task.");
        return FALSE;
    }

    //
    // Found this one. Copy the info.
    //
    RtlMoveMemory(pData, &pTask->EnvData, sizeof(*pData));

    return TRUE;
}

/*++ 
    ShimThisProcess

    Function invokes Shim Engine for dynamic shimming of the current process 
    Which happens to be ntvdm, naturally. This ntvdm is a separate ntvdm 
    (which is insured through various checks in CheckAndShimNTVDM) 

--*/

BOOL
ShimThisProcess(
    HMODULE hModShimEngine,
    HSDB    hSDB,
    SDBQUERYRESULT* pQueryResult
    )
{
    typedef BOOL    (WINAPI *PFNDynamicShim)(LPCWSTR , HSDB , SDBQUERYRESULT*, LPCSTR);
    PFNDynamicShim  pfnDynamicShim = NULL;
    WCHAR wszFileName[MAX_PATH];
    DWORD dwLength;

    pfnDynamicShim = (PFNDynamicShim) GetProcAddress(hModShimEngine, "SE_DynamicShim");
    if (NULL == pfnDynamicShim) {
        LOGN( eDbgLevelError, 
            "[ShimThisProcess] failed to obtain dynamic shim proc address\n");
        return FALSE;
    }

    dwLength = GetModuleFileNameW(GetModuleHandle(NULL), wszFileName, CHARCOUNT(wszFileName));
    if (!dwLength || dwLength > CHARCOUNT(wszFileName)) {
        LOGN( eDbgLevelError, 
            "[ShimThisProcess] failed to obtain module file name\n");
        return FALSE;
    }

    return pfnDynamicShim(wszFileName, hSDB, pQueryResult, NULL);
}

/*++ 
    CheckAndShimNTVDM
    
    Procedure checks ntvdm application for having to be shimmed. If an application is located in 
    appcompat database, this ntvdm would have to be running as a separate ntvdm (explorer is shimmed as 
    well, as a result it will have checked the binary first and set the separate vdm flag in CreateProcess)

    Further, this call comes through InitTask (intercepted between ntvdm and user32) -- as a parameter it 
    takes hTask16 - which we're able to use to retrieve application's environment and other important 
    information.
--*/


BOOL
CheckAndShimNTVDM(
    WORD hTask16
    )
{
    HMODULE  hModShimEngine;
    PSZ      pszTaskFileName = NULL;
    CString  csTaskFileName;
    DWORD    dwExeCount;
    PSZ      pszEnv  = NULL;
    PTDB     pTDB    = NULL;
    PVOID    pEnvNew = NULL;
    PDOSPDB  pPSP;
    BOOL     bSuccess = FALSE;
    BOOL     bMatch;
    BOOL     bNewEnv  = FALSE;
    HSDB     hSDB;
    NTSTATUS Status;
    SDBQUERYRESULT QueryResult;
    DWORD    dwFlags;

    hModShimEngine = GetModuleHandle(TEXT("shim.dll"));
    if (hModShimEngine == NULL) {
        // impossible -- shim.dll is not injected!!!
        return FALSE;
    }
    
    if (g_pSeparateWow != NULL && *g_pSeparateWow == FALSE) {
        // 
        // not a separate wow
        //
        LOGN( eDbgLevelError,
            "[CheckAndShimNTVDM] running in shared wow, no shimming\n");
        return FALSE;
    }

    if (!g_bInitialized) { // first call, link apis
        bSuccess = ImportWowApis();
        if (!bSuccess) {
            LOGN( eDbgLevelError,
                "[CheckAndShimNTVDM] Failed to import apis.\n");
            return FALSE;
        }
    }

    if (IsWOWExec(hTask16)) {
        LOGN( eDbgLevelError,
            "[CheckAndShimNTVDM] not touching wowexec\n");
        
        return FALSE;
    }

    csTaskFileName = ShimGetTaskFileName(hTask16);
    if (csTaskFileName.IsEmpty()) {
        LOGN( eDbgLevelError,
            "[CheckAndShimNTVDM] failed to get the filename for task 0x%lx\n", hTask16);
        return FALSE;
    }
    
    //
    // init database
    //
    hSDB = SdbInitDatabase(0, NULL);

    if (hSDB == NULL) {
        LOGN( eDbgLevelError, 
            "[CheckAndShimNTVDM] failed to init shim database\n");
        return FALSE;
    } 


    //
    // process history please -- 
    // if we end up here, we are a separate ntvdm 
    // running with a process history in the env, was retrieved in init
    //

    pTDB = GetTDB(hTask16);
    if (NULL == pTDB) {
        LOGN( eDbgLevelError, "[UpdateWowTaskList] Bad TDB entry 0x%x",  hTask16);
        return FALSE;
    }

    //
    // 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)
    //

    pszEnv = ShimGetTaskEnvptr(hTask16);
    if (NULL != pszEnv) {
        Status = ShimCloneEnvironment(&pEnvNew, (LPVOID)pszEnv, FALSE);
        if (!NT_SUCCESS(Status)) {
            LOGN( eDbgLevelError, 
                "[CheckAndShimNTVDM] cannot clone environment 0x%lx\n", Status);
            pEnvNew = NULL;
            bNewEnv = TRUE;
        }

        // 
        // if this call has come the way of VDM - we need to carry over our environment stuff
        // which is stored separately in this shim
        //
        // should the call to ShimCloneEnvironment fail, we will have pEnvNew == NULL
        // and bNewEnv = TRUE, as a result, we shall try again to clone the environment

        
        dwFlags = CREATE_UNICODE_ENVIRONMENT;
        pEnvNew = ShimCreateWowEnvironment_U(pEnvNew, &dwFlags, bNewEnv);
    }
    
    //
    // run detection please
    //
    
    bMatch = SdbGetMatchingExe(hSDB,
                               (LPCWSTR)csTaskFileName,
                               NULL, // we can give out module name as well -- but WHY?
                               (LPCWSTR)pEnvNew,
                               0,
                               &QueryResult);
                                  
    if (bMatch) {
        bSuccess = ShimThisProcess(hModShimEngine, hSDB, &QueryResult);
    }
                     
    if (pEnvNew != NULL) {
        ShimFreeEnvironment(pEnvNew);
    }
    
    return bSuccess;
}
    


IMPLEMENT_SHIM_END

