// define COBJMACROS so the IUnknown_Release() gets defined
#define COBJMACROS

// disable ddraw COM declarations.  Otherwise ddrawint.h defineds _NO_COM
// and includes ddraw.h.  That causes ddraw.h to define IUnknown as 'void *'
// which obliterates the struct IUnknown in objbase.h.  This all happens
// inside winddi.h
#define _NO_DDRAWINT_NO_COM

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <objbase.h>    
#include <mshtml.h> 
#include <winddi.h>
#include <io.h>

#define PAGE_4K   0x1000

// Assume an error happened.  This variable controls whether a pass or fail
// is logged in the test results.
BOOL g_bError = TRUE;

// Time the test started (only valid in the main process)
time_t TestStartTime;

FILE *fpLogFile;

void __cdecl PrintToLog(char *format, ...)
{
    va_list pArg;
    char buffer[4096];

    va_start(pArg, format);
    _vsnprintf(buffer, sizeof(buffer), format, pArg);
    buffer[sizeof(buffer)-1] = '\0';
    printf("%s", buffer);
    if (fpLogFile) {
        fprintf(fpLogFile, "%s", buffer);
    }
}

////////////  All this code runs in a worker thread in a child process //////
//// Prefix any output by "WOW64BVT1" so it can be identified as coming from
//// the child process.

// This routine is invoked when "childprocess" is passed on the command-line.
// The child runs synchronously with the parent to maximize the test coverage.
int BeAChildProcess(void)
{
    HRESULT hr;
    IUnknown *pUnk;
    CLSID clsid;
    HWND hwnd;

    PrintToLog("WOW64BVT1: Child process running\n");

    // Do some COM stuff here
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED|COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr)) {
        PrintToLog("ERROR: WOW64BVT1: CoInitializeEx failed %x\n", hr);
        return 3;
    }

    // Load and call 32-bit mshtml inproc
    hr = CLSIDFromProgID(L"Shell.Explorer", &clsid);
    if (FAILED(hr)) {
        PrintToLog("ERROR: WOW64BVT1: CLSIDFromProgID for Shell.Explorer failed %x\n", hr);
        return 3;
    }
#if 0   // The cocreate fails on IA64 with 8007000e (E_OUTOFMEMORY)
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (PVOID *)&pUnk);
    if (FAILED(hr)) {
        PrintToLog("ERROR: WOW64BVT1: CoCreateInstance for Shell.Explorer failed %x\n", hr);
        return 3;
    }
    Sleep(1000);
    IUnknown_Release(pUnk);
    pUnk = NULL;
#endif

    // Load and call mplay32.exe out-of-proc
#if 0 // The clsidfromprogid fails on IA64 with 800401f3 (CO_E_CLASSSTRING)
    hr = CLSIDFromProgID(L"MediaPlayer.MediaPlayer.1", &clsid);
    if (FAILED(hr)) {
        PrintToLog("ERROR: WOW64BVT1: CLSIDFromProgID for MediaPlayer.MediaPlayer failed %x\n", hr);
        return 3;
    }
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IUnknown, (PVOID *)&pUnk);
    if (FAILED(hr)) {
        PrintToLog("ERROR: WOW64BVT1: CoCreateInstance for MediaPlayer.MediaPlayer failed %x\n", hr);
        return 3;
    }
    Sleep(5000);
    IUnknown_Release(pUnk);
    pUnk = NULL;
#endif

    // Unfortunately, mplay32 has a refcounting problem and doesn't close
    // when we release it.  Post a quit message to make it close.
    hwnd = FindWindowW(NULL, L"Windows Media Player");
    if (hwnd) {
        PostMessage(hwnd, WM_QUIT, 0, 0);
    }

    // Wrap up COM now
    CoUninitialize();

    PrintToLog("WOW64BVT1: Child process done OK.\n");
    return 0;
}

////////////  All this code runs in a worker thread in the main process //////
//// Prefix any output by "WOW64BVT" so it can be identified as coming from
//// the parent process.

DWORD BeAThread(LPVOID lpParam)
{
    NTSTATUS st;
    LPWSTR lp;

    PrintToLog("WOW64BVT: Worker thread running\n");

    // Call an API close to the end of whnt32.c's dispatch table
    st = NtYieldExecution();
    if (FAILED(st)) {
        PrintToLog("ERROR: WOW64BVT: NtYieldExecution failed %x\n", st);
        exit(1);
    }

    // Call an API close to the end of whwin32.c's dispatch table.  Pass NULL,
    // so it is expected to fail.
    lp = EngGetPrinterDataFileName(NULL);    // calls NtGdiGetDhpdev()
    if (lp) {
        // It succeeded.... it shouldn't have since 
        PrintToLog("ERROR: WOW64BVT: EngGetPrinterDataFileName succeeeded when it should not have.\n");
        exit(1);
    }

    PrintToLog("WOW64BVT: Worker thread done OK.\n");
    return 0;
}

HANDLE CreateTheThread(void)
{
    HANDLE h;
    DWORD dwThreadId;

    PrintToLog("WOW64BVT: Creating child thread\n");
    h = CreateThread(NULL, 0, BeAThread, NULL, 0, &dwThreadId);
    if (h == INVALID_HANDLE_VALUE) {
        PrintToLog("ERROR: WOW64BVT: Error %d creating worker thread.\n", GetLastError());
        exit(2);
    }
    // Sleep a bit here, to try and let the child thread run a bit
    Sleep(10);
    return h;
}

BOOL AllocateStackAndTouch(
    INT Count)
{
    char temp[4096];

    memset(temp, 0, sizeof(temp));

    if (--Count) {
        AllocateStackAndTouch(Count);
    }

    return TRUE;
}

DWORD WINAPI TestGuardPagesThreadProc(
    PVOID lpParam)
{
    
    try {
        AllocateStackAndTouch(PtrToUlong(lpParam));
    } except(EXCEPTION_EXECUTE_HANDLER) {
        PrintToLog("ERROR: WOW64BVT: Error allocating stack. Exception Code = %lx\n",
               GetExceptionCode());
        exit(1);
    }

    return 0;
}

int TestGuardPages(
    VOID)
{
    HANDLE h;
    DWORD dwExitCode, dwThreadId;
    BOOL b;
    ULONG NestedStackCount = 100;
    DWORD StackSize = 4096;
    
    
    PrintToLog("WOW64BVT: Creating worker threads to test guard pages\n");

    while (StackSize < (32768+1))
    {
        h = CreateThread(NULL, 
                         StackSize, 
                         TestGuardPagesThreadProc, 
                         UlongToPtr(NestedStackCount), 
                         0, 
                         &dwThreadId);
        if (h == INVALID_HANDLE_VALUE) {
            PrintToLog("ERROR: WOW64BVT:  Error %d creating worker thread for guard page tests.\n", GetLastError());
            exit(2);
        }
        StackSize += 4096;
        WaitForSingleObject(h, INFINITE);

        b = GetExitCodeThread(h, &dwExitCode);
        if (b) {
             if (dwExitCode) {
                 return (int)dwExitCode;
             }
        } else {
            PrintToLog("ERROR: GetExitCodeThread failed with LastError = %d\n", GetLastError());
            return 1;
        }
    }

    PrintToLog("WOW64BVT: Test guard pages done OK.\n");

    return 0;
}


int TestMemoryMappedFiles(
    VOID)
{
    HANDLE Handle;
    PWCHAR pwc;
    MEMORY_BASIC_INFORMATION mbi;
    BOOL ExceptionHappened = FALSE;
  
    
    PrintToLog("WOW64BVT: Testing memory mapped files\n");

    Handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
                                NULL,
                                SEC_RESERVE | PAGE_READWRITE,
                                0,
                                32 * 1024,
                                L"HelloWorld");

    if (Handle == INVALID_HANDLE_VALUE) {
        PrintToLog("ERROR: WOW64BVT : Error %d creating file mapping\n", GetLastError());
        return 1;
    }

    pwc = (PWCHAR)MapViewOfFile(Handle,
                                FILE_MAP_WRITE,
                                0,
                                0,
                                0);

    if (!pwc) {
        PrintToLog("ERROR: WOW64BVT : Error %d mapping section object\n", GetLastError());
        return 1;
    }

    if (!VirtualQuery(pwc,
                      &mbi,
                      sizeof(mbi))) {
        PrintToLog("ERROR: WOW64BVT : Virtual query failed with last error = %d\n", GetLastError());
        return 1;
    }

    if (mbi.State != MEM_RESERVE) {
        PrintToLog("ERROR: WOW64BVT : Memory attributes have changed since mapped %lx\n", mbi.State);
        return 1;
    }

    try {
        *pwc = *(pwc+1);
    } except(EXCEPTION_EXECUTE_HANDLER) {
        ExceptionHappened = TRUE;
    }

    if (ExceptionHappened == FALSE) {
        PrintToLog("ERROR: WOW64BVT : Memory has been committed while it should have ONLY been reserved.\n");
        return 1;
    }

    if (!VirtualQuery(pwc,
                      &mbi,
                      sizeof(mbi))) {
        PrintToLog("ERROR: WOW64BVT : Virtual query failed with last error = %d\n", GetLastError());
        return 1;
    }

    if (mbi.State != MEM_RESERVE) {
        PrintToLog("ERROR: WOW64BVT : Memory attributes have changed since mapped %lx\n", mbi.State);
        return 1;
    }

    UnmapViewOfFile(pwc);
    CloseHandle(Handle);

    PrintToLog("WOW64BVT: Testing memory mapped files done OK.\n");

    return 0;
}

BOOL ReleasePages(PVOID Address, 
                  DWORD DeAllocationType, 
                  SIZE_T ReleasedPages)
{

    PVOID p = Address;
    SIZE_T nPages = ReleasedPages;
    NTSTATUS NtStatus;

    NtStatus = NtFreeVirtualMemory(NtCurrentProcess(),
                                   &p,
                                   &nPages,
                                   DeAllocationType);

    return NT_SUCCESS(NtStatus);
}

BOOL VerifyPages(PVOID Address,
                 DWORD DeAllocationType,
                 SIZE_T ReleasedPages)
{
    DWORD PagesState;
    BOOL b;
    MEMORY_BASIC_INFORMATION mbi;

    if (DeAllocationType == MEM_DECOMMIT)
    {
        PagesState = MEM_RESERVE;
    }
    else if (DeAllocationType == MEM_RELEASE)
    {
        PagesState = MEM_FREE;
    }
    else
    {
        PagesState = DeAllocationType;
    }


    b = VirtualQuery(Address,
                     &mbi,
                     sizeof(mbi));

    if (b)
    {
        if (mbi.State != PagesState)
        {
            PrintToLog("ERROR: WOW64BVT: Incorrect page protection set at address %p. State = %lx - %lx, RegionSize = %lx - %lx\n",
                        Address, mbi.State, PagesState, mbi.RegionSize, ReleasedPages);
            b = FALSE;
        }
    }
    else
    {
        PrintToLog("ERROR: WOW64BVT: Failed to query virtual memory at address %p - %lx\n",
                    Address, GetLastError());
    }

    return b;
}


BOOL ReleaseVerifyPages(PVOID BaseAllocation,
                       PVOID *Address,
                       SIZE_T *AllocationSize,
                       DWORD AllocationType,
                       DWORD DeAllocationType,
                       DWORD ReleasedPages)
{
    BOOL b;

    if (ReleasedPages > *AllocationSize)
    {
        ReleasedPages = *AllocationSize;
    }
    
    b = ReleasePages(*Address, DeAllocationType, ReleasedPages);
    if (b == FALSE)
    {
        PrintToLog("ERROR: WOW64BVT: Failed to release a page - %lx\n", GetLastError());
        return b;
    }
    
    b = VerifyPages(*Address, 
                    DeAllocationType, 
                    ReleasedPages);

    *AllocationSize -= ReleasedPages;
    *Address  = (PVOID)((ULONG_PTR)*Address + ReleasedPages);

    if (b == FALSE)
    {
        PrintToLog("ERROR: WOW64BVT: Failed to verify pages at address %lx - %lx\n",
                    ((ULONG_PTR)Address + ReleasedPages), GetLastError());
    }

    return b;
}


BOOL TestVadSplitOnFreeHelper(DWORD AllocationType, 
                              DWORD DeAllocationType,
                              SIZE_T TotalAllocation)
{
    BOOL b;
    PVOID Address;
    PVOID BaseAllocation;
    SIZE_T BaseAllocationSize;
    INT n;

    Address = VirtualAlloc(NULL,
                           TotalAllocation,
                           AllocationType,
                           PAGE_READWRITE);

    if (Address == NULL)
    {
        PrintToLog("ERROR: WOW64BVT: Failed to allocate memory - %lx\n", GetLastError());
    }

    n = 1;
    BaseAllocation = Address;
    BaseAllocationSize = TotalAllocation;
    while (TotalAllocation != 0)
    {
        b = ReleaseVerifyPages(BaseAllocation,
                               &Address,
                               &TotalAllocation,
                               AllocationType,
                               DeAllocationType,
                               PAGE_4K * n);

        if (b == FALSE)
        {
            PrintToLog("ERROR: WOW64BVT: ReleaseVerifyPages failed - %lx. %lx-%lx-%lx", 
                        GetLastError(), BaseAllocation, Address, TotalAllocation);
            break;
        }
        b = VerifyPages(BaseAllocation,
                        DeAllocationType,
                        n * PAGE_4K);

        if (b == FALSE)
        {
            PrintToLog("ERROR: WOW64BVT: Verify released pages from address %p with length = %lx failed\n", BaseAllocation, (n * PAGE_4K));
            break;
        }

        if (TotalAllocation > 0)
        {
            b = VerifyPages(Address,
                            AllocationType,
                            TotalAllocation);

            if (b == FALSE)
            {
                PrintToLog("ERROR: WOW64BVT: Verify pages from address %p with length = %lx failed\n", BaseAllocation, TotalAllocation);
                break;
            }
        }

        n += 2;
    }

    return b;
}


int TestVadSplitOnFree()
{
    BOOL b;
    SIZE_T AllocationSize = (PAGE_4K * 10);


    PrintToLog("WOW64BVT: Testing VAD splitting...\n");

    b = TestVadSplitOnFreeHelper(MEM_COMMIT,
                                 MEM_DECOMMIT,
                                 AllocationSize);

    if (b)
    {
        b = TestVadSplitOnFreeHelper(MEM_COMMIT,
                                     MEM_RELEASE,
                                     AllocationSize);
    }

    if (b)
    {
        b = TestVadSplitOnFreeHelper(MEM_RESERVE,
                                     MEM_RELEASE,
                                     AllocationSize);
    }

    if (b != FALSE)
    {
        PrintToLog("WOW64BVT: Testing VAD splitting...OK\n");
    }
    else
    {
        PrintToLog("ERROR: WOW64BVT: Testing VAD splitting\n");
    }

    return (b == FALSE);
}


PVOID GetReadOnlyBuffer()
{
    PVOID pReadOnlyBuffer = NULL;

    if (!pReadOnlyBuffer)
    {
        SYSTEM_INFO SystemInfo;

        // Get system info so that we know the page size
        GetSystemInfo(&SystemInfo);

        // Allocate a whole page.  This is optimal.
        pReadOnlyBuffer = VirtualAlloc(NULL, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READWRITE);
        if (pReadOnlyBuffer)
        {
            // Fill it with know patern
            FillMemory(pReadOnlyBuffer, SystemInfo.dwPageSize, 0xA5);
            
            // Mark the page readonly
            pReadOnlyBuffer = VirtualAlloc(pReadOnlyBuffer, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READONLY);
        }
    }

    return pReadOnlyBuffer;
}

PVOID GetReadOnlyBuffer2()
{
    PVOID pReadOnlyBuffer = NULL;

    DWORD OldP;
    SYSTEM_INFO SystemInfo;

    // Get system info so that we know the page size
    GetSystemInfo(&SystemInfo);

    // Allocate a whole page.  This is optimal.
    pReadOnlyBuffer = VirtualAlloc(NULL, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READWRITE);

    if (pReadOnlyBuffer)
    {
        FillMemory(pReadOnlyBuffer, SystemInfo.dwPageSize, 0xA5);
        lstrcpy((PTSTR)pReadOnlyBuffer, TEXT("xxxxxxxxxxxxxxxxxxxx"));

        if (!VirtualProtect(pReadOnlyBuffer, SystemInfo.dwPageSize, PAGE_READONLY, &OldP))
        {
            PrintToLog("ERROR: WOW64BVT: VirtualProtect() failed inside GetReadOnlyBuffer2()\n");
            VirtualFree(pReadOnlyBuffer, 0, MEM_RELEASE);

            pReadOnlyBuffer = NULL;
        }

    }

    return pReadOnlyBuffer;

}

BOOL TestMmPageProtection()
{
    PTSTR String;
    BOOL AV = FALSE;


    PrintToLog("WOW64BVT: Testing MM Page Protection...\n");

    String = (PTSTR) GetReadOnlyBuffer();
    if (!String) {
        PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer() failed\n");
        return TRUE;
    }

    try {
        *String = TEXT('S');
    } except(EXCEPTION_EXECUTE_HANDLER) {
        AV = TRUE;
    }

    VirtualFree(String, 0, MEM_RELEASE);

    if (AV == TRUE) {
        
        AV = FALSE;
        String = (PTSTR) GetReadOnlyBuffer2();

        if (!String) {
            PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer2() failed\n");
            return TRUE;
        }

        try {
            *String = TEXT('A');
        } except(EXCEPTION_EXECUTE_HANDLER) {
            AV = TRUE;
        }

        VirtualFree(String, 0, MEM_RELEASE);
    } else {
        PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer() failed to make 4K pages read only\n");
    }


    if (AV == TRUE) {
        PrintToLog("WOW64BVT: Testing MM Page Protection...OK\n");
    } else {
        PrintToLog("ERROR: WOW64BVT: Testing MM Page Protection\n");
    }

    return (AV == FALSE);
}

#define STACK_BUFFER  0x300
BOOL TestX86MisalignedLock()
{
    BOOL bError = FALSE;

    PrintToLog("WOW64BVT: Testing X86 Lock on misaligned addresses...\n");
    
    __try
    {
        __asm
        {
        pushad;
        pushfd;

        sub esp, STACK_BUFFER;

        ;;
        ;; make eax  unaliged with respect to an 8-byte cache line
        ;;

        mov eax, esp;
        add eax, 10h;
        mov ecx, 0xfffffff0;
        and eax, ecx;
        add eax, 7;
        mov ebx, eax;

        ;;
        ;; add
        ;;

        mov DWORD PTR [eax], 0x0300;
        lock add WORD PTR [eax], 0x0004;
        cmp DWORD PTR [eax], 0x0304;
        jnz $endwitherrornow;


        mov DWORD PTR [eax], 0x0300;
        lock add DWORD PTR [eax], 0x10000;
        cmp DWORD PTR [eax], 0x10300;
        jnz $endwitherrornow;

        mov ecx, DWORD PTR [eax+8];
        add ecx, 0x10;
        lock add DWORD PTR [eax+8], 0x10;
        cmp DWORD PTR [eax+8], ecx;
        jnz $endwitherrornow;
        
        mov ecx, DWORD PTR fs:[5];
        mov esi, 0x30000;
        lock add DWORD PTR fs:[5], esi;
        add esi, ecx;
        cmp DWORD PTR fs:[5], esi;
        mov DWORD PTR fs:[5], ecx;
        jnz $endwitherrornow;

        mov edi, 5;
        mov ecx, DWORD PTR fs:[edi];
        mov esi, 0x30000;
        lock add DWORD PTR fs:[edi], esi;
        add esi, ecx;
        cmp DWORD PTR fs:[edi], esi;
        mov DWORD PTR fs:[edi], ecx;
        jnz $endwitherrornow;

        mov esi, 0x40;
        mov WORD PTR [eax], 0x3000;
        lock add WORD PTR [eax], si;
        cmp WORD PTR [eax], 0x3040;
        jnz $endwitherrornow;

        mov edi, 0x40;
        mov DWORD PTR [eax], 0x3000;
        lock add DWORD PTR [eax], edi;
        cmp DWORD PTR [eax], 0x3040;
        jnz $endwitherrornow;

        ;;
        ;; adc
        ;;

        pushfd;
        pop ecx;
        or ecx, 1;
        push ecx;
        popfd;
        mov DWORD PTR [eax], 0x030000;
        lock adc DWORD PTR [eax], 0x40000;
        cmp DWORD PTR [eax], 0x70001;
        jnz $endwitherrornow;

        pushfd;
        pop ecx;
        and ecx, 0xfffffffe;
        push ecx;
        popfd;
        mov dx, 0x4000;
        mov WORD PTR [eax], 0x03000;
        lock adc WORD PTR [eax], dx;
        cmp WORD PTR [eax], 0x7000;
        jnz $endwitherrornow;

        pushfd;
        pop ecx;
        or ecx, 0x01;
        push ecx;
        popfd;
        mov WORD PTR [eax], 0x03000;
        lock adc WORD PTR [eax], 0x04000;
        cmp WORD PTR [eax], 0x7001;
        jnz $endwitherrornow;        


        ;;
        ;; and
        ;;

        mov DWORD PTR [eax], 0xffffffff;
        lock and DWORD PTR [eax], 0xffff;
        cmp DWORD PTR [eax], 0xffff;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xffffffff;
        lock and DWORD PTR [eax], 0xff00ff00;
        cmp DWORD PTR [eax], 0xff00ff00;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xffffffff;
        mov esi, 0x00ff00ff
        lock and DWORD PTR [eax], esi;
        cmp DWORD PTR [eax], esi;
        jnz $endwitherrornow;

        mov ecx, 4;
        mov DWORD PTR [eax+ecx*2], 0xffffffff;
        mov esi, 0xffff00ff
        lock and DWORD PTR [eax+ecx*2], esi;
        cmp DWORD PTR [eax+ecx*2], 0xffff00ff;
        jnz $endwitherrornow;

        mov WORD PTR [eax], 0xffff;
        mov si, 0xff
        lock and WORD PTR [eax], si;
        cmp WORD PTR [eax], si;
        jnz $endwitherrornow;

        mov edi, DWORD PTR fs:[5];
        mov DWORD PTR fs:[5], 0xffffffff;
        mov ebx, 5;
        lock and DWORD PTR fs:[ebx], 0xff00ff00;
        cmp DWORD PTR fs:[ebx], 0xff00ff00;
        mov DWORD PTR fs:[ebx], edi;
        jnz $endwitherrornow;

        ;;
        ;; or
        ;;

        mov DWORD PTR [eax], 0x00;
        lock or DWORD PTR [eax], 0xffff;
        cmp DWORD PTR [eax], 0xffff;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xff00ff00;
        lock or DWORD PTR [eax], 0xff00ff;
        cmp DWORD PTR [eax], 0xffffffff;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xff000000;
        mov esi, 0x00ff00ff
        lock or DWORD PTR [eax], esi;
        cmp DWORD PTR [eax], 0xffff00ff;
        jnz $endwitherrornow;

        mov ecx, 4;
        mov DWORD PTR [eax+ecx*2], 0xff000000;
        mov esi, 0x00ff00ff
        lock or DWORD PTR [eax+ecx*2], esi;
        cmp DWORD PTR [eax+ecx*2], 0xffff00ff;
        jnz $endwitherrornow;

        mov WORD PTR [eax], 0xf000;
        mov si, 0xff
        lock or WORD PTR [eax], si;
        cmp WORD PTR [eax], 0xf0ff;
        jnz $endwitherrornow;

        mov edi, DWORD PTR fs:[5];
        mov DWORD PTR fs:[5], 0x00;
        mov ebx, 5;
        lock or DWORD PTR fs:[ebx], 0xff00ff00;
        cmp DWORD PTR fs:[ebx], 0xff00ff00;
        mov DWORD PTR fs:[ebx], edi;
        jnz $endwitherrornow;

        ;;
        ;; xor
        ;;

        mov DWORD PTR [eax], 0x00ffffff;
        lock xor DWORD PTR [eax], 0xffff;
        cmp DWORD PTR [eax], 0x00ff0000;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xff00ff00;
        lock xor DWORD PTR [eax], 0xff00ff;
        cmp DWORD PTR [eax], 0xffffffff;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0xff0000ff;
        mov esi, 0x00ff00ff
        lock xor DWORD PTR [eax], esi;
        cmp DWORD PTR [eax], 0xffff0000;
        jnz $endwitherrornow;

        mov ecx, 4;
        mov DWORD PTR [eax+ecx*2], 0xff000000;
        mov esi, 0xffff00ff
        lock xor DWORD PTR [eax+ecx*2], esi;
        cmp DWORD PTR [eax+ecx*2], 0x00ff00ff;
        jnz $endwitherrornow;

        mov WORD PTR [eax], 0xf000;
        mov si, 0xf0ff
        lock xor WORD PTR [eax], si;
        cmp WORD PTR [eax], 0x00ff;
        jnz $endwitherrornow;

        mov edi, DWORD PTR fs:[5];
        mov DWORD PTR fs:[5], 0x0f;
        mov ebx, 5;
        lock xor DWORD PTR fs:[ebx], 0xff00000f;
        cmp DWORD PTR fs:[ebx], 0xff000000;
        mov DWORD PTR fs:[ebx], edi;
        jnz $endwitherrornow;

        ;;
        ;; inc & dec
        ;;
        mov DWORD PTR [eax], 0xffff;
        lock inc DWORD PTR [eax];
        cmp DWORD PTR [eax], 0x10000;
        jnz $endwitherrornow;
        lock inc WORD PTR [eax];
        cmp WORD PTR [eax], 0x0001;
        lock dec WORD PTR [eax];
        jnz $endwitherrornow;
        cmp WORD PTR [eax], 0x00;
        jnz $endwitherrornow;
        mov DWORD PTR [eax], 0;
        lock dec DWORD PTR [eax];
        cmp DWORD PTR [eax], 0xffffffff;
        jnz $endwitherrornow;
        
        ;;
        ;; not
        ;;
        mov DWORD PTR [eax], 0x10101010;
        lock not DWORD PTR [eax];
        cmp DWORD PTR [eax], 0xefefefef;
        jnz $endwitherrornow;
        mov DWORD PTR [eax+8], 0xffff0000;
        lock not DWORD PTR [eax+8];
        cmp DWORD PTR [eax+8], 0x0000ffff;
        jnz $endwitherrornow;
        mov ecx, 2;
        mov DWORD PTR [eax+ecx*4], 0xffffffff;
        lock not DWORD PTR [eax+ecx*4];
        cmp DWORD PTR [eax+ecx*4], 0x00000000;
        jnz $endwitherrornow;

        ;;
        ;; neg
        ;;
        mov DWORD PTR [eax], 0;
        lock neg DWORD PTR [eax];
        jc $endwitherrornow;
        cmp DWORD PTR [eax], 0;
        jnz $endwitherrornow;
        mov DWORD PTR [eax], 0xffffffff;
        lock neg DWORD PTR [eax];
        jnc $endwitherrornow;
        cmp DWORD PTR [eax], 0x01;
        jnz $endwitherrornow;

        mov WORD PTR [eax], 0xff;
        lock neg WORD PTR [eax];
        jnc $endwitherrornow;
        cmp WORD PTR [eax], 0xff01;
        jnz $endwitherrornow;

        ;;
        ;; bts
        ;;

        mov DWORD PTR [eax], 0x7ffffffe;
        lock bts DWORD PTR [eax], 0;
        jc $endwitherrornow;
        cmp DWORD PTR [eax], 0x7fffffff;
        jnz $endwitherrornow;

        mov ecx, eax;
        sub ecx, 4;
        mov edx, 63;
        lock bts DWORD PTR [ecx], edx;
        jc $endwitherrornow;
        cmp DWORD PTR [eax], 0xffffffff
        jnz $endwitherrornow;

        ;;
        ;; xchg
        ;;
        
        mov DWORD PTR [eax], 0xf0f0f0f0;
        mov edx, 0x11112222;
        lock xchg DWORD PTR [eax], edx;
        cmp DWORD PTR [eax], 0x11112222;
        jnz $endwitherrornow;
        cmp edx, 0xf0f0f0f0;
        jnz $endwitherrornow;

        xchg WORD PTR [eax], dx;
        cmp WORD PTR [eax], 0xf0f0;
        jnz $endwitherrornow;
        cmp dx, 0x2222;
        jnz $endwitherrornow;

        ;;
        ;; cmpxchg
        ;;

        mov ebx, eax;
        mov DWORD PTR [ebx], 0xf0f0f0f0;
        mov eax, 0x10101010;
        mov edx, 0x22332233;
        lock cmpxchg DWORD PTR [ebx], edx;
        jz $endwitherrornow;
        cmp eax, 0xf0f0f0f0;
        jnz $endwitherrornow;

        mov DWORD PTR [ebx], 0xf0f0f0f0;
        mov eax, 0xf0f0f0f0;
        mov edx, 0x12341234;
        lock cmpxchg DWORD PTR [ebx], edx;
        jnz $endwitherrornow;
        cmp DWORD PTR [ebx], 0x12341234;
        jnz $endwitherrornow;

        ;;
        ;; cmpxchg8b
        ;;

        mov DWORD PTR [ebx], 0x11223344;
        mov DWORD PTR [ebx+4], 0x55667788;
        mov edx, 0x12341234;
        mov eax, 0xff00ff00;
        lock cmpxchg8b [ebx];
        jz $endwitherrornow;
        cmp edx, 0x55667788;
        jnz $endwitherrornow;
        cmp eax, 0x11223344;
        jnz $endwitherrornow;

        mov esi, ebx;
        mov DWORD PTR [esi], 0x11223344;
        mov DWORD PTR [esi+4], 0x55667788;
        mov edx, 0x55667788;
        mov eax, 0x11223344;
        mov ecx, 0x10101010;
        mov ebx, 0x20202020;
        lock cmpxchg8b [esi];
        jnz $endwitherrornow;
        cmp DWORD PTR [esi], 0x20202020;
        jnz $endwitherrornow;
        cmp DWORD PTR [esi+4], 0x10101010;
        jnz $endwitherrornow;
        
        mov eax, esi;
        mov ebx, eax;

        ;;
        ;; sub
        ;;

        mov DWORD PTR [eax], 0x0300;
        lock sub WORD PTR [eax], 0x0004;
        cmp DWORD PTR [eax], 0x2fc;
        jnz $endwitherrornow;

        mov DWORD PTR [eax], 0x10000;
        lock sub DWORD PTR [eax], 0x0300;
        cmp DWORD PTR [eax], 0xfd00;
        jnz $endwitherrornow;

        mov ecx, DWORD PTR [eax+8];
        sub ecx, 0x10;
        lock sub DWORD PTR [eax+8], 0x10;
        cmp DWORD PTR [eax+8], ecx;
        jnz $endwitherrornow;
        
        mov ecx, DWORD PTR fs:[5];
        mov esi, 0x3000;
        lock sub DWORD PTR fs:[5], esi;
        mov edi, ecx;
        sub ecx, esi;
        cmp DWORD PTR fs:[5], ecx;
        mov DWORD PTR fs:[5], edi;
        jnz $endwitherrornow;

        mov edi, 5;
        mov ecx, DWORD PTR fs:[edi];
        mov esi, 0x30000;
        lock sub DWORD PTR fs:[edi], esi;
        mov edx, ecx;
        sub ecx, esi;
        cmp DWORD PTR fs:[edi], ecx;
        mov DWORD PTR fs:[edi], edx;
        jnz $endwitherrornow;

        mov si, 0x40;
        mov WORD PTR [eax], 0x3000;
        lock sub WORD PTR [eax], si;
        cmp WORD PTR [eax], 0x2fc0;
        jnz $endwitherrornow;

        mov edi, 0x40;
        mov DWORD PTR [eax], 0x3000;
        lock sub DWORD PTR [eax], edi;
        cmp DWORD PTR [eax], 0x2fc0;
        jnz $endwitherrornow;

        ;;
        ;; sbb
        ;;

        pushfd;
        pop ecx;
        or ecx, 1;
        push ecx;
        popfd;
        mov DWORD PTR [eax], 0x030000;
        lock sbb DWORD PTR [eax], 0x40000;
        cmp DWORD PTR [eax], 0xfffeffff;
        jnz $endwitherrornow;

        pushfd;
        pop ecx;
        and ecx, 0xfffffffe;
        push ecx;
        popfd;
        mov dx, 0x4000;
        mov WORD PTR [eax], 0x03000;
        lock sbb WORD PTR [eax], dx;
        cmp WORD PTR [eax], 0xf000;
        jnz $endwitherrornow;

        pushfd;
        pop ecx;
        or ecx, 0x01;
        push ecx;
        popfd;
        mov WORD PTR [eax], 0x03000;
        lock sbb WORD PTR [eax], 0x04000;
        cmp WORD PTR [eax], 0xefff;
        jnz $endwitherrornow;        

        ;;
        ;; xadd
        ;;

        mov DWORD PTR [eax], 0x12345678;
        mov ecx, 0x1234;
        lock xadd DWORD PTR [eax], ecx;
        cmp ecx, 0x12345678;
        jnz $endwitherrornow;        
        mov edx, 0x1234;
        add edx, 0x12345678;
        cmp DWORD PTR [eax], edx;
        jnz $endwitherrornow;        

        mov WORD PTR [eax], 0x5678;
        mov cx, 0x1234;
        lock xadd WORD PTR [eax], cx;
        cmp cx, 0x5678;
        jnz $endwitherrornow;        
        mov dx, 0x5678;
        add dx, 0x1234;
        cmp WORD PTR [eax], dx;
        jnz $endwitherrornow;        

        ;;
        ;; Update caller with status
        ;;
        mov bError, 0
        jmp $endnow;

$endwitherrornow:

        mov bError, 1
$endnow:
        add esp, STACK_BUFFER;
        popfd;
        popad;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        bError = TRUE;
        printf("ERROR: WOW64BVT: Exception %lx\n", GetExceptionCode());
    }

    if (bError == FALSE) {
        PrintToLog("WOW64BVT: Testing X86 Lock on misaligned addresses...OK\n");
    } else {
        PrintToLog("ERROR: WOW64BVT: Testing X86 Lock on misaligned addresses\n");
    }

    return bError;
}

////////////  All this code runs in the main test driver thread //////

HANDLE CreateTheChildProcess(char *ProcessName, char *LogFileName)
{
    char Buffer[512];
    HANDLE h;
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    BOOL b;

    PrintToLog("WOW64BVT: Creating child process\n");

    strcpy(Buffer, ProcessName);
    strcat(Buffer, " childprocess");
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    if (fpLogFile) {
        // If we're logging, then change the child process' stdout/stderr
        // to be the log file handle, so their output is captured to the file.
        HANDLE hLog = (HANDLE)_get_osfhandle(_fileno(fpLogFile));

        si.dwFlags = STARTF_USESTDHANDLES;
        si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
        si.hStdOutput = hLog;
        si.hStdError = hLog;
    }
    b = CreateProcessA(NULL, Buffer, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    if (!b) {
        PrintToLog("ERROR: WOW64BVT:  Error %d creating child process.\n", GetLastError());
        exit(1);
    }
    CloseHandle(pi.hThread);
    return pi.hProcess;
}

// This is called from within exit() in the main test driver process
void __cdecl AtExitHandler(void)
{
    time_t EndTime;
    struct tm *newtime;
    OSVERSIONINFOW vi;
    BOOL b;
    int year, hour;

    memset(&vi, 0, sizeof(vi));
    vi.dwOSVersionInfoSize = sizeof(vi);
    b = GetVersionExW(&vi);
    if (!b) {
        PrintToLog("\tWARNING: GetVersionExW failed, LastError = %d\n", GetLastError());
        vi.dwBuildNumber = 0;
    }

    // Close the logging bucket.
    PrintToLog(("[/TEST LOGGING OUTPUT]\n"));

    // Print the required data:
    PrintToLog("\tTEST:         wow64bvt\n");
    PrintToLog("\tBUILD:        %d\n", vi.dwBuildNumber);
    PrintToLog("\tMACHINE:      \\\\%s\n", getenv("COMPUTERNAME"));
    PrintToLog("\tRESULT:       %s\n", (g_bError) ? "FAIL" : "PASS");
    PrintToLog("\tCONTACT:      samera\n");
    PrintToLog("\tMGR CONTACT:  samera\n");
    PrintToLog("\tDEV PRIME:    samera\n");
    PrintToLog("\tDEV ALT:      askhalid\n");
    PrintToLog("\tTEST PRIME:   terryla\n");
    PrintToLog("\tTEST ALT:     terryla\n");
    newtime = localtime(&TestStartTime);
    year = (newtime->tm_year >= 100) ? newtime->tm_year-100 : newtime->tm_year;
    if (newtime->tm_hour == 0) {
        hour = 12;
    } else if (newtime->tm_hour > 12) {
        hour = newtime->tm_hour-12;
    } else {
        hour = newtime->tm_hour;
    }
    PrintToLog("\tSTART TIME:   %d/%d/%2.2d %d:%2.2d:%2.2d %s\n", newtime->tm_mon+1,
                                                     newtime->tm_mday,
                                                     year,
                                                     hour,
                                                     newtime->tm_min,
                                                     newtime->tm_sec,
                                                     (newtime->tm_hour < 12) ? "AM" : "PM");

    time(&EndTime);
    newtime = localtime(&EndTime);
    year = (newtime->tm_year >= 100) ? newtime->tm_year-100 : newtime->tm_year;
    if (newtime->tm_hour == 0) {
        hour = 12;
    } else if (newtime->tm_hour > 12) {
        hour = newtime->tm_hour-12;
    } else {
        hour = newtime->tm_hour;
    }
    PrintToLog("\tEND TIME:     %d/%d/%2.2d %d:%2.2d:%2.2d %s\n", newtime->tm_mon+1,
                                                     newtime->tm_mday,
                                                     year,
                                                     hour,
                                                     newtime->tm_min,
                                                     newtime->tm_sec,
                                                     (newtime->tm_hour < 12) ? "AM" : "PM");
    PrintToLog("[/TESTRESULT]\n");
}

//
// This is just used to print out failing exception cases
//
void __cdecl ExceptionWoops(HRESULT want, HRESULT got)
{
	if (got == 0) {
		PrintToLog("==> Exception Skipped. Wanted: 0x%08x, Got: 0x%08x\n", want, got);
	}
	else {
		PrintToLog("==> Unexpected Exception. Wanted: 0x%08x, Got: 0x%08x\n", want, got);
	}
}

#define EXCEPTION_LOOP      10000

// Among other things, this does some divife by zero's (as a test)
// so don't have the compiler stop things just because we're causing an error
#pragma warning(disable:4756)
#pragma warning(disable:4723)

//
// Do some exception checks
//
int __cdecl ExceptionCheck(void)
{
    int failThis;
	int sawException;
    int i;
    char *p = NULL;
	HRESULT code;


    PrintToLog("WOW64BVT: Testing Exception Handling\n");

    // Assume success
    failThis = FALSE;

    // Test a privileged instruction
	sawException = FALSE;
	__try {
		__asm {
			hlt
		}
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_PRIVILEGED_INSTRUCTION) {
            // PrintToLog("Saw privileged instruction\n");
        }
        else {
                PrintToLog("ERROR: Cause a privileged instruction fault\n");
	        ExceptionWoops(STATUS_PRIVILEGED_INSTRUCTION, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
            PrintToLog("ERROR: Cause a privileged instruction fault\n");
        ExceptionWoops(STATUS_PRIVILEGED_INSTRUCTION, code);
        failThis = TRUE;
    }

    // Test an illegal instruction
	sawException = FALSE;
	__try {
		__asm {
			__asm _emit 0xff
			__asm _emit 0xff
			__asm _emit 0xff
			__asm _emit 0xff
		}
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
		if (code == STATUS_ILLEGAL_INSTRUCTION) {
			// PrintToLog("Saw illegal instruction\n");
        }
        else {
            PrintToLog("ERROR: Cause an illegal instruction fault\n");
	        ExceptionWoops(STATUS_ILLEGAL_INSTRUCTION, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
        PrintToLog("ERROR: Cause an illegal instruction fault\n");
        ExceptionWoops(STATUS_ILLEGAL_INSTRUCTION, code);
        failThis = TRUE;
    }

//
// Testing for an int 3 can be a problem for systems that
// are running checked builds. So, don't bother with this
// test. Perhaps in the future, the code can test for a checked
// build and do appropriate.
//
#if 0
    // Test the result of an int 3
	sawException = FALSE;
	__try {
		_asm {
			int 3
		}
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_BREAKPOINT) {
			// PrintToLog("Saw debugger breakpoint\n");
        }
        else {
            PrintToLog("ERROR: Cause an int 3 debugger breakpoint\n");
	        ExceptionWoops(STATUS_BREAKPOINT, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
        PrintToLog("ERROR: Cause an int 3 debugger breakpoint\n");
        ExceptionWoops(STATUS_BREAKPOINT, code);
        failThis = TRUE;
    }
#endif

    // Test the result of an illegal int XX instruction
	sawException = FALSE;
	__try {
		_asm {
			int 66
		}
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_ACCESS_VIOLATION) {
			// PrintToLog("Saw access violation\n");
        }
        else {
            PrintToLog("ERROR: Cause an int 66 unknown interrupt (Access violation)\n");
	        ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
        PrintToLog("ERROR: Cause an int 66 unknown interrupt (Access violation)\n");
        ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
        failThis = TRUE;
    }

    // Test the result of an int divide by zero
	sawException = FALSE;
	__try {
		int i, j, k;

		i = 0;
		j = 4;

		k = j / i;
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_INTEGER_DIVIDE_BY_ZERO) {
			// PrintToLog("Saw int divide by zero\n");
        }
        else {
            PrintToLog("ERROR: Cause an integer divide by zero\n");
	        ExceptionWoops(STATUS_INTEGER_DIVIDE_BY_ZERO, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
        PrintToLog("ERROR: Cause an integer divide by zero\n");
        ExceptionWoops(STATUS_INTEGER_DIVIDE_BY_ZERO, code);
        failThis = TRUE;
    }

    // Test the result of an fp divide by zero
	// PrintToLog("Before div0: Control is 0x%0.4x, Status is 0x%0.4x\n", _control87(0,0), _status87());
	sawException = FALSE;
	__try {
		double x, y;

		y = 0.0;

		x = 1.0 / y ;

		// PrintToLog("x is %lf\n", x);

		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
		// Don't actually get a divide by zero error, get
		// so we should never hit this exception!
        PrintToLog("Try a floating divide by zero\n");
		PrintToLog("Woops! Saw an exception when we shouldn't have!\n");
		sawException = TRUE;
            failThis = TRUE;
	}
	// So you would think you'd get a float divide by zero error... Nope,
	// you get X set to infinity...
	if (code != 0) {
        PrintToLog("ERROR: Try a floating divide by zero\n");
        ExceptionWoops(0, code);
        failThis = TRUE;
    }
	// PrintToLog("After div0: Control is 0x%0.4x, Status is 0x%0.4x\n", _control87(0,0), _status87());


    // Test an int overflow (which actually does not cause an exception)
	sawException = FALSE;
	__try {
		__asm {
			into
		}
		// PrintToLog("into doesn't fault\n");
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_INTEGER_OVERFLOW) {
			// PrintToLog("Saw integer overflow\n");
        }
        else {
            PrintToLog("ERROR: Try an into overflow fault\n");
	        ExceptionWoops(STATUS_INTEGER_OVERFLOW, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	// Looks like integer overflow is ok... Is this a CRT thing?
	if (code != 0) {
        PrintToLog("ERROR: Try an into overflow fault\n");
        ExceptionWoops(0, code);
        failThis = TRUE;
    }

    // Test an illegal access
	sawException = FALSE;
	__try {

		*p = 1;
		code = 0;
	}
	__except((code = GetExceptionCode()), 1 ) {
        if (code == STATUS_ACCESS_VIOLATION) {
			// PrintToLog("Saw access violation\n");
        }
        else {
            PrintToLog("ERROR: Cause an access violation\n");
	        ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
            failThis = TRUE;
        }
		sawException = TRUE;
	}
	if (!sawException) {
        PrintToLog("ERROR: Cause an access violation\n");
        ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
        failThis = TRUE;
    }

    //
    // Finally, try a lot of exceptions (a loop) and verify we don't overflow
    // the stack
    //
    for (i = 0; i < EXCEPTION_LOOP; i++) {
        
        // Test an illegal access
        sawException = FALSE;
        __try {
            *p = 1;
            code = 0;
        }
        __except((code = GetExceptionCode()), 1 ) {
            if (code == STATUS_ACCESS_VIOLATION) {
                // PrintToLog("Saw access violation\n");
            }
            else {
                PrintToLog("ERROR: Cause an access violation\n");
                ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
                failThis = TRUE;
                break;
            }
            sawException = TRUE;
        }
        if (!sawException) {
            PrintToLog("ERROR: Cause an access violation\n");
            ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
            failThis = TRUE;
            break;
        }
    }

	return failThis;
}

// Ok, go back to normal warnings...
#pragma warning(default:4756)
#pragma warning(default:4723)

#if defined(__BUILDMACHINE__)
#if defined(__BUILDDATE__)
#define B2(x, y) "" #x "." #y
#define B1(x, y) B2(x, y)
#define BUILD_MACHINE_TAG B1(__BUILDMACHINE__, __BUILDDATE__)
#else
#define B2(x) "" #x
#define B1(x) B2(x)
#define BUILD_MACHINE_TAG B1(__BUILDMACHINE__)
#endif
#else
#define BUILD_MACHINE_TAG ""
#endif

int __cdecl main(int argc, char *argv[])
{
    NTSTATUS st;
    HANDLE HandleList[2];
    BOOL b;
    DWORD dwExitCode;

    // Disable buffering of the standard output handle
    setvbuf(stdout, NULL, _IONBF, 0);

    // Do some minimal command-line checking
    if (argc < 2 || argc > 3) {
        PrintToLog("Usage:  wow64bvt log_file_name\n\n");
        return 1;
    } else if (strcmp(argv[1], "childprocess") == 0) {
        return BeAChildProcess();
    }
    // We're the main exe

    // Record the start time
    time(&TestStartTime);

    // Open the log file
    fpLogFile = fopen(argv[1], "w");
    if (!fpLogFile) {
        PrintToLog("wow64bvt: Error: unable to create the log file '%s'\n", argv[1]);
        return 1;
    }
    // Disable buffering of the log file handle
    setvbuf(fpLogFile, NULL, _IONBF, 0);

    // Print the initial banner
    PrintToLog("[TESTRESULT]\n");
    PrintToLog("[TEST LOGGING OUTPUT]\n");
    PrintToLog("%s built on %s at %s by %s\n", argv[0], __DATE__, __TIME__, BUILD_MACHINE_TAG);

    // Register the atexit handler - it closes the logging output section
    // and prints the success/fail information as the BVT test exits.
    atexit(AtExitHandler);

    ///////////////////////////// Test starts here //////////////////////////

    // 32-bit child process creation from 32-bit parent.  The parent instance
    // (running now) tested 32-bit child from 64-bit parent
    HandleList[0] = CreateTheChildProcess(argv[0], argv[1]);

    // Create a thread do so some more work
    HandleList[1] = CreateTheThread();

    // Wait for everything to finish
    WaitForMultipleObjects(sizeof(HandleList)/sizeof(HandleList[0]), HandleList, TRUE, INFINITE);

    // Get the return code from the child process
    b=GetExitCodeProcess(HandleList[0], &dwExitCode);
    if (b) {
        if (dwExitCode) {
            // The child failed.  We should fail too.
            return (int)dwExitCode;
        }
    } else {
        PrintToLog("ERROR: GetExitCodeProcess failed with LastError = %d\n", GetLastError());
        return 1;
    }

    // Get the return code from the thread
    b=GetExitCodeThread(HandleList[1], &dwExitCode);
    if (b) {
        if (dwExitCode) {
            // The child failed.  We should fail too.
            return (int)dwExitCode;
        }
    } else {
        PrintToLog("ERROR: GetExitCodeThread failed with LastError = %d\n", GetLastError());
        return 1;
    }

    b = ExceptionCheck();
    if (b) {
        PrintToLog("ERROR: Exception Handling test.\n");
        return 1;
    }

    b = TestGuardPages();
    if (b) {
        PrintToLog("ERROR: TestGuardPages().\n");
        return 1;
    }

    b = TestMemoryMappedFiles();
    if (b) {
        PrintToLog("ERROR: TestMemoryMappedFiles().\n");
        return 1;
    }

    b = TestVadSplitOnFree();
    if (b) {
        PrintToLog("ERROR: TestVadSplitOnFree()\n");
        return 1;
    }

    b = TestMmPageProtection();
    if (b) {
        PrintToLog("ERROR: TestMmPageProtection()\n");
        return 1;
    }

    b = TestX86MisalignedLock();
    if (b) {
        PrintToLog("ERROR: TestX86MisalignedLock()\n");
        return 1;
    }

    // Everything finished OK.  Clear the error flag and exit.  The atexit
    // callback function will finish filling out the log if this is the
    // main thread.
    g_bError = FALSE;
    return 0;
}