#include "stdinc.h" // actually from dll\whistler directory
/*-----------------------------------------------------------------------------
Side X ("by") Side Test
-----------------------------------------------------------------------------*/
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "fusionlastwin32error.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#pragma warning(push)
#pragma warning(disable: 4511)
#pragma warning(disable: 4512)
#pragma warning(disable: 4663)
#include <yvals.h>
#pragma warning(disable: 4663)
#include <string>
#include <deque>
#include <vector>
#pragma warning(pop)
#include "FusionBuffer.h"
#include "fusion.h"
#include "sxsasmname.h"
#include "util.h"
#include "filestream.cpp"
#include "sxsapi.h"
#include "fusiontrace.h"
#include "cresourcestream.cpp"
#include "cmemorystream.cpp"
#include "wintrust.h"
#include "softpub.h"
#include "perfclocking.h"
#include "strongname.h"
#include "fusionversion.h"
#include <setupapi.h>
#include <string>
#include "commctrl.h"
#include <algorithm>
#include "fusionsha1.h"
#include "cguid.h"
#include "winbasep.h"
#undef LoadLibraryA
#undef LoadLibraryW
#undef LoadLibraryExA
#undef LoadLibraryExW
#undef InitCommonControls

BOOL IamExe;
BOOL IamDll;
extern "C" { void (__cdecl * _aexit_rtn)(int); }

void __cdecl Trace(const char* FormatString, ...)
{
    char buffer[2000];
    va_list args;

    va_start(args, FormatString);
    _vsnprintf(buffer, RTL_NUMBER_OF(buffer), FormatString, args);
    buffer[RTL_NUMBER_OF(buffer) - 1] = 0;
    for (PSTR s = buffer ; *s != 0 ; )
    {
        PSTR t = strchr(s, '\n');

        if (t != NULL)
            *t = 0;

                    printf("stdout  : %s\n", s);
        OutputDebugStringA("debugger: ");
        OutputDebugStringA(s);
        OutputDebugStringA("\n");

        if (t != NULL)
            s = t + 1;
    }
    va_end(args);
}

typedef BOOL (WINAPI * PSXSPGENERATEMANIFESTPATHONASSEMBLYIDENTITY)(
    PWSTR str,         // input string, must have name, version, langid and processorarchitecture
    PWSTR psz,         // output string, like x86_cards_strongname,.......
    SIZE_T * pCch,     // IN : length of psz, OUT : used
    PASSEMBLY_IDENTITY *ppAssemblyIdentity  // could be NULL
   );

/*-----------------------------------------------------------------------------
work around the fact that SxsHeap doesn't let us use a non debug operator new
-----------------------------------------------------------------------------*/
template <typename T>
class Allocator : public std::allocator<T>
{
public:
    pointer allocate(size_type n, const void*)
    {
        return reinterpret_cast<pointer>(malloc(n * sizeof(T)));
    }

    char* _Charalloc(size_type n)
    {
        return reinterpret_cast<char*>(malloc(n));
    }

    void deallocate(void* p, size_type)
    {
        free(p);
    }
};

#define SXSTEST_BEGIN_INSTALL          (0x4000000000000000i64)
#define SXSTEST_INSTALL                (0x2000000000000000i64)
#define SXSTEST_END_INSTALL            (0x1000000000000000i64)
#define SXSTEST_INSTALL_MY_QUEUE       (0x0800000000000000i64)
#define SXSTEST_INSTALL_SETUPAPI_QUEUE (0x0400000000000000i64)
#define SXSTEST_END_OF_FLAGS           (0x0200000000000000i64)
#define SXSTEST_THREADS                (0x0100000000000000i64)
#define SXSTEST_CREATEPROCESS          (0x0080000000000000i64)

inline int PRINTABLE(int ch) { return isprint(ch) ? ch : '.'; }

void PrintIfAnyCriticalSectionsHeld(const char* file, int line, const char* function)
#define PrintIfAnyCriticalSectionsHeld() PrintIfAnyCriticalSectionsHeld(__FILE__, __LINE__, __FUNCTION__)
{
    const DWORD dwLastError = ::GetLastError();
    PTEB teb;
    teb = NtCurrentTeb();
    if (teb->CountOfOwnedCriticalSections != 0)
    {
        DbgPrint("%s(%d):%s teb->CountOfOwnedCriticalSections %d\n", file, line, function, teb->CountOfOwnedCriticalSections);
    }
    SetLastError(dwLastError);
}

static
VOID
PrintBlob(
    FILE *pf,
    PVOID Data,
    SIZE_T Length,
    PCWSTR PerLinePrefix
   );

BOOL TestLeakMemory(DWORD Amount);
BOOL TestAssemblyProbing(int argc, wchar_t **argv, int *piNext);
BOOL TestDirectoryChangeWatcher(int argc, wchar_t **argv, int *piNext);
BOOL TestXMLParsing(int argc, wchar_t **argv, int *piNext);
BOOL TestMultiAct(int argc, wchar_t **argv);
BOOL TestManifestSchema(int argc, wchar_t **argv, int *piNext);
BOOL TestDirect(int argc, wchar_t **argv, int *piNext);
void TestWin32(wchar_t** argv);
BOOL TestAct(int argc, wchar_t **argv, int *piNext);
BOOL TestInstall(PCWSTR manifest, __int64 flags, DWORD beginInstallFlags, DWORD installFlags, DWORD endInstallFlags);
int  TestDiffDir(PCWSTR dir1, PCWSTR dir2);
BOOL TestSearchPath(int argc, wchar_t** argv, int* piNext);
BOOL TestMSIInstall(int argc, wchar_t** argv, int* piNext);
int  TestDirWalk(PCWSTR root, PWSTR filter);
BOOL TestLoadLibrary(int argc, wchar_t** argv, int* piNext);
int  TestAssemblyName(VOID);
int  TestPrecomiledManifest(PCWSTR szFileName);
int  TestPCMTime(PCWSTR manifestFilename);
int  TestCreateProcess(wchar_t** argv);
int  TestCreateProcess2(wchar_t** argv);
BOOL TestInstallPrivateAssembly(int argc, wchar_t** argv, int* piNext);
BOOL TestManifestProbing(int argc, wchar_t** argv, int* piNext);
int  TestCreateMultiLevelDirectory(PCWSTR dirs);
BOOL TestXMLDOM(PCWSTR xmlfilename);
BOOL TestFusionArray(PCWSTR, PCWSTR);
BOOL TestGeneratePathFromIdentityAttributeString(PCWSTR str);
BOOL TestRefreshAssembly(PCWSTR wsAssembly);
BOOL TestInstallWithInstallInfo(PCWSTR wsAssemblyManifest, PCWSTR wsReference);
BOOL TestOpeningStuff(PCWSTR wsSourceName, PCWSTR wsType, PCWSTR wsCount);
BOOL TestVerifyFileSignature(PCWSTR wsFilename);
BOOL TestInstallLikeWindowsSetup(PCWSTR szDirectory, PCWSTR szCodebase);
BOOL TestDumpContainedManifests(PCWSTR wsFilename);
BOOL TestGenerateStringWithIdenticalHash(WCHAR iString[33]);
BOOL TestAssemblyIdentityHash();
void TestInherit();
void TestNoInherit();
void TestEmpty();
BOOL TestMessagePerf(int argc, wchar_t **arg, int *piNext);
LRESULT CALLBACK TestMessagePerfWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void TestTrickyMultipleAssemblyCacheItems(PCWSTR);
void TestSfcScanKickoff();
void GenerateStrongNameAndPublicKey(PCWSTR wsCertificate);
VOID TestCreateActctxLeakHandles(DWORD num);
BOOL TestSystemDefaultActivationContextGeneration();
BOOL TestAsyncIO(int argc, wchar_t **argv, int *piNext);
void TestRefCount();
void TestGuidSort();
void TestStringSort();
BOOL TestNewCatalogSignerThingy(PCWSTR pcwszCatalog);
void TestExeDll();
int TestThreadInheritLeak();
BOOL TestSxsSfcUI();
void TestGetModuleHandleEx();
void TestGetFullPathName(PCWSTR);
void TestCreateFile(PCWSTR);
void TestGetPathBaseName(LPCWSTR Path);
PCSTR PrintPathToString(RTL_PATH_TYPE);
void TestPathType(PCWSTR*);
void TestVersion();
void TestGetProcessImageFileName();
void TestErrorInfra();
void TestQueryActCtx();
void TestQueryActCtx2();
void Test64k();
void TestDotLocalSingleInstancing();
void TestCreateActCtx(int nCreations, wchar_t **rgCreations);
void TestCreateActctxLikeCreateProcess();
void TestCreateActctxAdminOverride();
void TestQueryManifestInformationBasic(PCWSTR pszManifest);
void TestCreateActctxWindowsShellManifest();
void TestCreateGlobalEvent();
void TestHandleLeaks(void);
void TestCRuntimeAsms(void);
BOOL TestMfcCreateAndMarshal(void);
void TestAtlCreate(void);
void TestAlignment(void);
BOOL TestPrivateSha1Impl(PCWSTR pcwszDirName);
BOOL TestNewSxsInstallAPI(PCWSTR pcwszManifest);
void TestImage(void);
void TestInterlockedAlignment(void);
void TestCreateActCtx_PE_flags0(void);
void TestUninstall(PCWSTR ManifestPath, PCWSTR ReferenceString);
PCWSTR GetLastErrorMessage();
BOOL SimpleTestFindAndUseSurrogateInformation(PCWSTR filename, PCWSTR GuidToDisplay);
BOOL TestSxsExportedSurrogateStuff(PCWSTR pcwszManifest, PCWSTR pcwszWhat, PCWSTR pcwszData);


DWORD LastError;

static void LoadSxs();
static int Main(int argc, wchar_t** argv);
static void SetLastOperation(const wchar_t* format, ...);
static const wchar_t* GetLastOperation(const wchar_t* format, ...);
static int Usage(const char* argv0);
template <typename PFN> void GetSxsProc(PCSTR name, PFN* ppfn);
template <typename PFN> void GetSxsProc(int name, PFN* ppfn);

SXSP_DEBUG_FUNCTION pfnSxspDebug = NULL;

BOOL ParseProcessorArchitecture(int argc, wchar_t** argv, int* piCurrent);
BOOL ParseLangId(int argc, wchar_t** argv, int* piCurrent);

PCWSTR FusionpThreadUnsafeGetLastWin32ErrorMessageW()
{
    CSxsPreserveLastError ple;
    static WCHAR LastErrorMessage[4096];

    LastErrorMessage[0] = 0;

    ::FormatMessageW(
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        ::FusionpGetLastWin32Error(),
        0,
        LastErrorMessage,
        RTL_NUMBER_OF(LastErrorMessage),
        NULL);
    if (LastErrorMessage[0] != 0)
    {
        PWSTR p = LastErrorMessage + ::StringLength(LastErrorMessage) - 1;
        while (p != LastErrorMessage && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
        {
            *p-- = 0;
        }
    }
    ple.Restore();
    return LastErrorMessage;
}


void __stdcall ThrowLastError(DWORD error = ::GetLastError())
{
    RaiseException(error, 0, 0, NULL);
    //throw HRESULT_FROM_WIN32(error);
}

void __stdcall ThrowWin32(ULONG_PTR error = ::GetLastError())
{
    ThrowLastError(static_cast<DWORD>(error));
}

void __stdcall CheckHresult(HRESULT hr)
{
    if (FAILED(hr))
        throw hr;
}

void SetDllBitInPeImage(PCWSTR Path)
/*++
.exes and .dlls are the same format except one bit in the headers distinguishes them.
--*/
{
    CFusionFile File;
    CFileMapping FileMapping;
    CMappedViewOfFile MappedViewOfFile;

    if (!File.Win32CreateFile(Path, GENERIC_READ | GENERIC_WRITE, 0, OPEN_EXISTING))
        ThrowLastError();
    if (!FileMapping.Win32CreateFileMapping(File, PAGE_READWRITE))
        ThrowLastError();
    if (!MappedViewOfFile.Win32MapViewOfFile(FileMapping, FILE_MAP_WRITE))
        ThrowLastError();

    PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(static_cast<PVOID>(MappedViewOfFile));
    if (NtHeaders == NULL)
        ThrowLastError(ERROR_BAD_EXE_FORMAT);

    // This is correct for PE32 or PE32+.
    NtHeaders->FileHeader.Characteristics |= IMAGE_FILE_DLL;

    if (!MappedViewOfFile.Win32Close())
        ThrowLastError();
    if (!FileMapping.Win32Close())
        ThrowLastError();
    if (!File.Win32Close())
        ThrowLastError();
}

PCSTR PrintPathToString(RTL_PATH_TYPE PathType)
{
    switch (PathType)
    {
#define X(x) case x: return #x;
        X(RtlPathTypeUnknown)
        X(RtlPathTypeUncAbsolute)
        X(RtlPathTypeDriveAbsolute)
        X(RtlPathTypeDriveRelative)
        X(RtlPathTypeRooted)
        X(RtlPathTypeRelative)
        X(RtlPathTypeLocalDevice)
        X(RtlPathTypeRootLocalDevice)
#undef X
    default:
        return "unknown";
    }
}

void TestPathType(const PCWSTR* argv)
{
    if (*argv != NULL)
    {
        while (*argv != NULL)
        {
            RTL_PATH_TYPE PathType = SxspDetermineDosPathNameType(*argv);
            printf("%ls -> %s\n", *argv, PrintPathToString(PathType));
            argv += 1;
        }
    }
    else
    {
        const static PCWSTR args[] =
        {
            L"a",
            L"\\a",
            L"\\\\a",
            L"\\\\\\a",
            L"a:",
            L"a:\\",
            L"\\?",
            L"\\.",
            L"\\\\?",
            L"\\\\.",
            L"\\\\?\\",
            L"\\\\.\\",
            L"\\\\?\\a",
            L"\\\\.\\a",
            L"\\\\?\\a:",
            L"\\\\.\\a:",
            L"\\\\?\\a:\\",
            L"\\\\.\\a:\\",
            L"\\\\?\\unc",
            L"\\\\.\\unc",
            L"\\\\?\\unc\\",
            L"\\\\.\\unc\\",
            NULL
        };
        TestPathType(args);
    }
}

class CUnknown : public IUnknown
{
public:
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv)
    {
        return E_NOINTERFACE;
    }
    virtual ULONG __stdcall AddRef()
    {
        return 1;
    }
    virtual ULONG __stdcall Release()
    {
        return 1;
    }
};

struct G
{
    HINSTANCE sxsDll;
    USHORT wProcessorArchitecture;
    LANGID wLangId;
    HANDLE MainThread;
    INT    NumberOfThreads;
    HANDLE Threads[MAXIMUM_WAIT_OBJECTS];
    HANDLE ThreadExitEvent;
    wchar_t lastOperation[256];
    CUnknown unknown;

    CSmartRef<IGlobalInterfaceTable> GlobalInterfaceTable;

    //
    // the start of some automation / record keeping..
    //
    ULONG Failures;
    ULONG Successes;
} g;

const static struct
{
    DWORD  (WINAPI* GetModuleFileNameW)(HMODULE, LPWSTR, DWORD);
    SIZE_T (WINAPI* VirtualQuery)(LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
}
Kernel32 =
{
    GetModuleFileNameW,
    VirtualQuery
};

const static struct
{
    HRESULT (WINAPI* IIDFromString)(LPOLESTR, LPIID);
    HRESULT (WINAPI* CLSIDFromString)(LPOLESTR, LPIID);
    HRESULT (WINAPI* CoCreateInstance)(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID*);
    HRESULT (WINAPI* CoInitialize)(LPVOID);
    void (WINAPI* CoUninitialize)();
}
Ole32 =
{
    IIDFromString,
    CLSIDFromString,
    CoCreateInstance,
    CoInitialize,
    CoUninitialize
};

void
ManifestStringToTempFile(
    PCWSTR ManifestString,
    CBaseStringBuffer &rTempFilePath
    )
{
    CFusionFile File;
    WCHAR xTempFilePath[MAX_PATH];
    WCHAR TempDirectory[MAX_PATH];
    const static WCHAR NativeUnicodeByteOrderMark = 0xfeff;
    DWORD BytesWritten;

    //if (!::GetTempPathW(NUMBER_OF(TempDirectory), TempDirectory))
    //   ThrowLastError();

    ::GetModuleFileNameW(NULL, TempDirectory, NUMBER_OF(TempDirectory));
    *wcsrchr(TempDirectory, '\\') = 0;
    ::Trace("TempDirectory:%ls\n", TempDirectory);
	
    if (!::GetTempFileNameW(TempDirectory, L"", 0, xTempFilePath))
        ::ThrowLastError();
    rTempFilePath.Win32Assign(xTempFilePath, wcslen(xTempFilePath));

    ::Trace("xTempFilePath:%ls\n", xTempFilePath);
    ::Trace("TempFilePath:%ls\n", static_cast<PCWSTR>(xTempFilePath));

    if (!File.Win32CreateFile(rTempFilePath, GENERIC_WRITE, 0, CREATE_ALWAYS))
        ::ThrowLastError();

    if (!::WriteFile(File, &NativeUnicodeByteOrderMark, sizeof(NativeUnicodeByteOrderMark), &BytesWritten, NULL))
        ::ThrowLastError();

    if (!::WriteFile(File, ManifestString, static_cast<DWORD>(sizeof(*ManifestString) * StringLength(ManifestString)), &BytesWritten, NULL))
        ::ThrowLastError();
}

HANDLE
CreateActivationContextFromStringW(
    PCWSTR ManifestString
    )
{
    CStringBuffer TempFilePath;

    ::ManifestStringToTempFile(ManifestString, TempFilePath);

    ACTCTXW ActivationContextCreate = { sizeof(ActivationContextCreate) };
    ActivationContextCreate.lpSource = TempFilePath;
    HANDLE ActivationContextHandle = ::CreateActCtxW(&ActivationContextCreate);
    DWORD Error = ::GetLastError();
    ::DeleteFileW(TempFilePath);
    if (ActivationContextHandle == INVALID_HANDLE_VALUE)
        ::ThrowLastError(Error);
    return ActivationContextHandle;
}

int Usage(const wchar_t* argv0)
{
    std::wstring strargv0 = argv0;
    fprintf(stderr,
        "%ls",
        (
        L"Usage: \n"
        L"   " + strargv0 + L" [install-flags] manifest-or-image-with-manifest-resource-path\n"
        L"   " + strargv0 + L" [-pa processor-architecture] [-langid langid] -d manifest-path ...\n"
        L"   " + strargv0 + L" [-pa processor-architecture] [-langid langid] -p manifest-path ...\n"
        L"   " + strargv0 + L" [-pa processor-architecture] [-langid langid] -w32 manifest-path ...\n"
        L"   " + strargv0 + L" [-pa processor-architecture] [-langid langid] -msi msi-script...\n"
        L"   " + strargv0 + L" -tcreateprocess ...\n"
        L"   " + strargv0 + L" -tsearchpath ...\n"
        L"   " + strargv0 + L" -tcreateprocess ...\n"
        L"   " + strargv0 + L" -tempty test pushing a special empty context ...\n"
        L"   " + strargv0 + L" -tinherit test the usual default inheritance ...\n"
        L"   " + strargv0 + L" -tnoinherit test the noinherit bit ...\n"
        L"   " + strargv0 + L" [-threads n] create n threads for some tests ...\n"
        L"   " + strargv0 + L" probably other choices, use the source\n"
        L"\n"
        L"install-flags:\n"
        L"   -i\n"
        L"   -install\n"
        L"   -install-my-queue\n"
        L"   -install-setupapi-queue\n"
        L"   -install-from-resource\n"
        L"   -install-move\n"
        L"   -install-my-queue\n"
        L"   -install-setupapi-queue [requires debugger]\n"
        L"   -install-from-resource\n"
        ).c_str()
        );

    return EXIT_FAILURE;
}

const wchar_t* GetLastOperation()
{
    return g.lastOperation;
}

void SetLastOperation(const wchar_t* format, ...)
{
    va_list args;

    g.lastOperation[0] = 0;
    g.lastOperation[NUMBER_OF(g.lastOperation) - 1] = 0;

    va_start(args, format);
    _vsnwprintf(g.lastOperation, NUMBER_OF(g.lastOperation) - 1, format, args);
    va_end(args);
}

HANDLE DuplicateHandle(HANDLE handle)
{
    HANDLE newHandle = NULL;
    if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
    {
        ThrowLastError();
    }
    return newHandle;
}

__int64 IsFlag(PCWSTR arg)
{
const static struct
{
    WCHAR   name[32];
    __int64 value;
} flags[] =
{
    { L"i",                              SXSTEST_BEGIN_INSTALL},
    { L"install",                        SXSTEST_BEGIN_INSTALL},

    { L"install-my-queue",               SXSTEST_INSTALL_MY_QUEUE },
    { L"install-setupapi-queue",         SXSTEST_INSTALL_SETUPAPI_QUEUE },

    { L"install-from-resource",          SXS_INSTALL_ASSEMBLY_FLAG_FROM_RESOURCE            | SXSTEST_INSTALL},
    { L"install-move",                   SXS_INSTALL_ASSEMBLY_FLAG_MOVE                     | SXSTEST_INSTALL },
    { L"install-dir",                    SXS_INSTALL_ASSEMBLY_FLAG_FROM_DIRECTORY           | SXSTEST_INSTALL},
    { L"install-dir-recursive",          SXS_INSTALL_ASSEMBLY_FLAG_FROM_DIRECTORY_RECURSIVE | SXSTEST_INSTALL},
    { L"install-no-verify",              SXS_INSTALL_ASSEMBLY_FLAG_NO_VERIFY                | SXSTEST_INSTALL},
    { L"install-no-transact",            SXS_INSTALL_ASSEMBLY_FLAG_NOT_TRANSACTIONAL        | SXSTEST_INSTALL},
    { L"install-replace-existing",       SXS_INSTALL_ASSEMBLY_FLAG_REPLACE_EXISTING         | SXSTEST_INSTALL},

    { L"begin-install-replace-existing", SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_REPLACE_EXISTING   | SXSTEST_BEGIN_INSTALL},
    { L"begin-install-from-resource",    SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_FROM_RESOURCE      | SXSTEST_BEGIN_INSTALL},
    { L"begin-install-move",             SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_MOVE               | SXSTEST_BEGIN_INSTALL },
    { L"begin-install-dir",              SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_FROM_DIRECTORY     | SXSTEST_BEGIN_INSTALL},
    { L"begin-install-dir-recursive",    SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE | SXSTEST_BEGIN_INSTALL},
    { L"begin-install-no-verify",        SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NO_VERIFY          | SXSTEST_BEGIN_INSTALL},
    { L"begin-install-no-transact",      SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NOT_TRANSACTIONAL  | SXSTEST_BEGIN_INSTALL},

    { L"end-install-no-verify",          SXS_END_ASSEMBLY_INSTALL_FLAG_NO_VERIFY            | SXSTEST_END_INSTALL},

    { L"threads",                        SXSTEST_THREADS },

    { L"-",                              SXSTEST_END_OF_FLAGS }
};
    if (*arg == '-')
    {
        arg += 1;
        for (ULONG i = 0 ; i != NUMBER_OF(flags) ; ++i)
        {
            if (_wcsicmp(flags[i].name, arg) == 0)
                return flags[i].value;
        }
    }
    return 0;
}

DWORD __stdcall ThreadMain(PVOID)
{
//
// We run stuff in other threads via QueueUserAPC.
//
    __try
    {
        WaitForSingleObjectEx(g.ThreadExitEvent, INFINITE, TRUE);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
#if DBG
        if (IsDebuggerPresent())
        {
            FUSION_DEBUG_BREAK();
        }
#endif
        QueueUserAPC(ThrowWin32, g.MainThread, GetExceptionCode());
    }
    return 0;
}

void CreateThreads()
{
    INT i;
    g.MainThread = DuplicateHandle(GetCurrentThread());
    g.ThreadExitEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    if (g.ThreadExitEvent == NULL)
    {
        ThrowLastError();
    }
    for (i = 0 ; i < g.NumberOfThreads ; i++)
    {
        g.Threads[i] = CreateThread(NULL, 0, ThreadMain, NULL, 0, NULL);
        if (g.Threads[i] == NULL)
        {
            int error = ::GetLastError();
            if (i > 2)
            {
                fprintf(stderr, "Only able to created %d threads, error=%d, continuing\n", i, error);
                g.NumberOfThreads = i;
                break;
            }
            fprintf(stderr, "Unable to create threads, error=%d, terminating\n", error);
            ThrowWin32(error);
        }
    }
}

void
GetFlags(
    wchar_t**& argv,
    __int64& flags,
    DWORD& beginInstallFlags,
    DWORD& installFlags,
    DWORD& endInstallFlags
    )
{
    __int64 flag;
    while (flag = IsFlag(argv[1]))
    {
        ++argv;
        if (flag & SXSTEST_END_OF_FLAGS)
        {
            break;
        }
        else if (flag & SXSTEST_BEGIN_INSTALL)
        {
            beginInstallFlags |= flag;
        }
        else if (flag & SXSTEST_INSTALL)
        {
            installFlags |= flag;
        }
        else if (flag & SXSTEST_END_INSTALL)
        {
            endInstallFlags |= flag;
        }
        else if (flag & SXSTEST_THREADS)
        {
            g.NumberOfThreads = _wtoi(*++argv);
            if (g.NumberOfThreads > NUMBER_OF(g.Threads))
            {
                g.NumberOfThreads = NUMBER_OF(g.Threads);
            }
        }

        // always set flags because normal installation is 0 now
        flags |= flag;
   }
}

VOID
FusionpSetSystemSetupInProgress(bool f)
{
    CFusionRegKey Regkey;
    CFusionRegKey RegkeyLocalMachine(HKEY_LOCAL_MACHINE);

    if (!RegkeyLocalMachine.OpenSubKey(Regkey, L"System\\Setup", KEY_ALL_ACCESS))
        return;
    Regkey.SetValue(L"SystemSetupInProgress", f ? 1 : 0);
}

extern "C"
BOOL
WINAPI
SxsDllMain(
    HINSTANCE hInst,
    DWORD dwReason,
    PVOID pvReserved
    );

int Main(int argc, wchar_t** argv)
{
    int i = 0;
    __int64 flags = 0;
    //__int64 flag  = 0;
    DWORD beginInstallFlags = 0;
    DWORD installFlags = 0;
    DWORD endInstallFlags = 0;
    wchar_t* argv0 = argv[0];

    g.wProcessorArchitecture = SxspGetSystemProcessorArchitecture();
    g.wLangId = ::GetUserDefaultLangID();
    if (argc > 1)
    {
        FusionpSetSystemSetupInProgress(false);
        __try
        {
        __try
        {
            if (!SxsDllMain(GetModuleHandle(NULL), DLL_PROCESS_ATTACH, NULL))
                ThrowLastError();
            GetFlags(argv, flags, beginInstallFlags, installFlags, endInstallFlags);

            i = 1;

            // consume global flags...
            for (;;)
            {
                if (::FusionpStrCmpI(argv[i], L"-pa") == 0)
                {
                    if (!ParseProcessorArchitecture(argc, argv, &i))
                        goto Exit;
                }
                else if (::FusionpStrCmpI(argv[i], L"-langid") == 0)
                {
                    if (!ParseLangId(argc, argv, &i))
                        goto Exit;
                }
                else
                    break;
            }

            if (false) { }
            else if (::FusionpStrCmpI(argv[i], L"-id") == 0)
            {
                DWORD index = 0;
                if (argv[3]){ // have an index present
                    index = argv[3][0] - L'0';
                }
                i = TestGeneratePathFromIdentityAttributeString(argv[2]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-tPathType") == 0)
            {
                TestPathType(argv + i + 1);
            }
            else if (::FusionpStrCmpI(argv[i], L"-systemdefault") == 0)
            {
                i = TestSystemDefaultActivationContextGeneration();
            }
            else if (::FusionpStrCmpI(argv[i], L"-dom") == 0)
            {
                i = TestXMLDOM(argv[2]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-hash") == 0)
            {
                i = TestGenerateStringWithIdenticalHash(argv[2]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-tasyncio") == 0)
            {
                i++;
                i = TestAsyncIO(argc, argv, &i);
            }
            else if (::FusionpStrCmpI(argv[i], L"-assemblyidentityhash") == 0)
            {
                i = TestAssemblyIdentityHash();
            }
            else if (::FusionpStrCmpI(argv[i], L"-array") == 0)
            {
                i = TestFusionArray(argv[2], argv[3]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-diffdir") == 0)
            {
                i = TestDiffDir(argv[i + 1], argv[i + 2]);
            }
            else if (::FusionpStrCmpI(argv[1], L"-pcm") == 0)
            {
                i = TestPrecomiledManifest(argv[2]);
            }
            else if (::FusionpStrCmpI(argv[1], L"-testpcm") == 0)
            {
                i = TestPCMTime(argv[2]);
            }
            else if (::FusionpStrCmpI(argv[1], L"-cd") == 0)
            {
                i = TestCreateMultiLevelDirectory(argv[2]);
            }
            else if (!::FusionpStrCmpI(argv[i], L"-manifests"))
            {
                TestDumpContainedManifests(argv[++i]);
            }
            else if (::FusionpStrCmpI(argv[1], L"-dirwalk") == 0)
            {
                i = TestDirWalk(argv[i + 1], argv[i + 2]);
            }
            else if (::FusionpStrCmpI(argv[1], L"-tmultiact") == 0)
            {
                i = TestMultiAct(argc, argv);
            }
            else if (flags)
            {
                PrintIfAnyCriticalSectionsHeld();
                i = TestInstall(argv[i], flags, beginInstallFlags, installFlags, endInstallFlags);
                PrintIfAnyCriticalSectionsHeld();
            }
            else if (!::FusionpStrCmpI(argv[i], L"-sfcui"))
            {
                if ( !TestSxsSfcUI() )
                    goto Exit;
                i++;
            }
            else if ( !FusionpStrCmpI( argv[i], L"-installwithinfo" ) )
            {
                TestInstallWithInstallInfo(
                    ( i + 1 < argc ) ? argv[i + 1] : NULL,
                    ( i + 2 < argc ) ? argv[i+2] : NULL);
                i += 2;
            }
            else if (!::FusionpStrCmpI(argv[i], L"-multicache"))
            {
                TestTrickyMultipleAssemblyCacheItems(argv[i + 1]);
                i++;
            }
            else if (::FusionpStrCmpI(argv[i], L"-d") == 0)
            {
                if (!TestDirect(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-probe") == 0)
            {
                i++;
                argv[i] = L"foo,type=\"win32\",processorArchitecture=\"x86\",version=\"6.0.0.0\",publicKeyToken=\"6595b64144ccf1df\"";
                if (!TestAssemblyProbing(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-dirchanges") == 0)
            {
                if (!TestDirectoryChangeWatcher(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-newinstall") == 0)
            {
                if (!TestNewSxsInstallAPI(argv[++i]))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-tuninstall") == 0
                  || ::FusionpStrCmpI(argv[i], L"-uninstall") == 0)
            {
                TestUninstall(argv[i + 1], argv[i + 2]);
                goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-probemanifest") == 0)
            {
                if (!TestManifestProbing(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-p") == 0)
            {
                if (!TestXMLParsing(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-w32") == 0)
            {
                TestWin32(argv + i + 1);
            }
            else if (::FusionpStrCmpI(argv[i], L"-msi") == 0)
            {
                if (!TestMSIInstall(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-mp") == 0)
            {
                if (!TestManifestSchema(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-act") == 0)
            {
                if (!TestAct(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-shatest") == 0)
            {
                if (!TestPrivateSha1Impl(argv[++i]))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[1], L"-am") == 0)
            {
                i = TestAssemblyName();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tsurrogates") == 0)
            {
                if (!SimpleTestFindAndUseSurrogateInformation(argv[i+1], argv[i+2]))
                    goto Exit;
                i += 2;
            }
            else if (::FusionpStrCmpI(argv[i], L"-clrhelpers") == 0)
            {
                if (!TestSxsExportedSurrogateStuff(argv[i+1], argv[i+2], argv[i+3]))
                    goto Exit;
                i += 3;
            }            
            else if (::FusionpStrCmpI(argv[i], L"-tsearchpath") == 0)
            {
                if (!TestSearchPath(argc, argv, &i))
                    goto Exit;
            }
            else if (!::FusionpStrCmpI(argv[i], L"-testmapping"))
            {
                if (!TestOpeningStuff(argv[i+1], argv[i+2], argv[i+3]))
                    goto Exit;
                i += 3;
            }
            else if (!::FusionpStrCmpI(argv[i], L"-validatefile"))
            {
                if (!TestVerifyFileSignature(argv[++i]))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-tloadlibrary") == 0)
            {
                if (!TestLoadLibrary(argc, argv, &i))
                    goto Exit;
            }
            else if (!::FusionpStrCmpI(argv[i], L"-refresh"))
            {
                if (!TestRefreshAssembly(argv[i+1]))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-leak") == 0)
            {
                //
                // We dump a little bit of memory
                //
                UINT iAmount = 0;
                iAmount = _wtoi(argv[++i]);
                if (!TestLeakMemory(iAmount))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-tcreateprocess") == 0)
            {
                if (!TestCreateProcess(argv + i + 1))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-tcreateprocess2") == 0)
            {
                if (!TestCreateProcess2(argv + i + 1))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-tinherit") == 0)
            {
                TestInherit();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tnoinherit") == 0)
            {
                TestNoInherit();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tempty") == 0)
            {
                TestEmpty();
            }
            else if (::FusionpStrCmpI(argv[i], L"-ttsappcmp") == 0)
            {
                TestCreateGlobalEvent();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tmsgperf") == 0)
            {
                i++;
                if (!TestMessagePerf(argc, argv, &i))
                    goto Exit;
            }
            else if (::FusionpStrCmpI(argv[i], L"-twinsetup") == 0)
            {
                FusionpSetSystemSetupInProgress(true);
                if (!TestInstallLikeWindowsSetup(argv[i + 1], (argv[i + 2] != NULL) ? argv[i + 2] : argv[i + 1]))
                    goto Exit;
                i += 3;
            }
            else if (!::FusionpStrCmpI(argv[i], L"-sfcscan"))
            {
                TestSfcScanKickoff();
            }
            else if (!::FusionpStrCmpI(argv[i], L"-certinfo"))
            {
                GenerateStrongNameAndPublicKey(argv[++i]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-thandle") == 0)
            {
                DWORD iAmount = 0;
                iAmount = _wtoi(argv[++i]);

                TestCreateActctxLeakHandles(iAmount);
            }
            else if (::FusionpStrCmpI(argv[i], L"-catsigner") == 0)
            {
                TestNewCatalogSignerThingy(argv[++i]);
            }
            else if (::FusionpStrCmpI(argv[i], L"-trefcount") == 0)
            {
                TestRefCount();
            }
            else if (::FusionpStrCmpI(argv[i], L"-ttileak") == 0)
            {
                TestThreadInheritLeak();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tguidsort") == 0)
            {
                TestGuidSort();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tstringsort") == 0)
            {
                TestStringSort();
            }
            else if (::FusionpStrCmpI(argv[i], L"-tExeDll") == 0)
            {
                TestExeDll();
            }
            else if (FusionpStrCmpI(argv[i], L"-tExitProcess") == 0)
            {
                LoadSxs();
                GetSxsProc(SXSP_DEBUG_ORDINAL, &pfnSxspDebug);
                pfnSxspDebug(SXS_DEBUG_EXIT_PROCESS, 0, 0, NULL);
            }
            else if (FusionpStrCmpI(argv[i], L"-tTerminateProcess") == 0)
            {
                LoadSxs();
                GetSxsProc(SXSP_DEBUG_ORDINAL, &pfnSxspDebug);
                pfnSxspDebug(SXS_DEBUG_TERMINATE_PROCESS, 0, 0, NULL);
            }
            else if (FusionpStrCmpI(argv[i], L"-tLastError") == 0)
            {
                ::SetLastError(123);
                printf("%lu\n", FusionpGetLastWin32Error());
                printf("%lu\n", ::GetLastError());
                ::FusionpSetLastWin32Error(456);
                printf("%lu\n", FusionpGetLastWin32Error());
                printf("%lu\n", ::GetLastError());
            }
            else if (FusionpStrCmpI(argv[i], L"-tGetModuleHandleEx") == 0)
            {
                TestGetModuleHandleEx();
            }
            else if (FusionpStrCmpI(argv[i], L"-tGetFullPathName") == 0)
            {
                TestGetFullPathName(argv[i + 1]);
            }
            else if (FusionpStrCmpI(argv[i], L"-tCreateFile") == 0)
            {
                TestCreateFile(argv[i + 1]);
            }
            else if (FusionpStrCmpI(argv[i], L"-tGetPathBaseName") == 0)
            {
                TestGetPathBaseName(argv[i + 1]);
            }
            else if (FusionpStrCmpI(argv[i], L"-tVersion") == 0)
            {
                TestVersion();
            }
            else if (FusionpStrCmpI(argv[i], L"-tGetProcessImageFileName") == 0)
            {
                TestGetProcessImageFileName();
            }
            else if (FusionpStrCmpI(argv[i], L"-tErrorInfra") == 0)
            {
                TestErrorInfra();
            }
            else if (FusionpStrCmpI(argv[i], L"-tQueryActCtx") == 0)
                TestQueryActCtx();
            else if (FusionpStrCmpI(argv[i], L"-tQueryActCtx2") == 0)
                TestQueryActCtx2();
            else if (FusionpStrCmpI(argv[i], L"-tqmib") == 0)
            {
                TestQueryManifestInformationBasic(argv[i+1]);
            }
            else if (FusionpStrCmpI(argv[i], L"-t64k") == 0)
            {
                Test64k();
            }
            else if (FusionpStrCmpI(argv[i], L"-tcreateactctx") == 0)
            {
                TestCreateActCtx(argc - (i + 1), &argv[i+1]);
            }
            else if (FusionpStrCmpI(argv[i], L"-TestCreateActCtx_PE_flags0") == 0)
            {
                TestCreateActCtx_PE_flags0();
            }
            else if (FusionpStrCmpI(argv[i], L"-tDotLocalSingleInstancing") == 0)
            {
                TestDotLocalSingleInstancing();
            }
            else if (FusionpStrCmpI(argv[i], L"-tCreateActctxLikeCreateProcess") == 0)
            {
                TestCreateActctxLikeCreateProcess();
            }
            else if (FusionpStrCmpI(argv[i], L"-tCreateActctxLikeCreateProcess") == 0)
            {
                TestCreateActctxLikeCreateProcess();
            }
            else if (FusionpStrCmpI(argv[i], L"-tCreateActctxAdminOverride") == 0)
            {
                TestCreateActctxAdminOverride();
            }
            else if (FusionpStrCmpI(argv[i], L"-tCreateActctxWindowsShellManifest") == 0)
            {
                TestCreateActctxWindowsShellManifest();
            }
            else if (FusionpStrCmpI(argv[i], L"-tHandleLeak") == 0)
            {
                //for (ULONG i = 0 ; i != 5 ; i += 1)
                    TestHandleLeaks();
            }
            else if (FusionpStrCmpI(argv[i], L"-tMfcCreateAndMarshal") == 0)
            {
                TestMfcCreateAndMarshal();
            }
            else if (FusionpStrCmpI(argv[i], L"-tAtlCreate") == 0)
            {
                TestAtlCreate();
            }
            else if (FusionpStrCmpI(argv[i], L"-TestAlignment") == 0)
            {
                TestAlignment();
            }
            else if (FusionpStrCmpI(argv[i], L"-DoNothingJustSeeIfItRuns") == 0)
            {
                printf("%wZ ran successfully\n", &NtCurrentPeb()->ProcessParameters->ImagePathName);
            }
            else if (FusionpStrCmpI(argv[i], L"-TestImage") == 0)
            {
                TestImage();
            }
            else if (FusionpStrCmpI(argv[i], L"-TestInterlockedAlignment") == 0)
            {
                TestInterlockedAlignment();
            }
            else
            {
                i = Usage(argv0);
            }
            if (g.ThreadExitEvent)
            {
                SetEvent(g.ThreadExitEvent);
                WaitForMultipleObjectsEx(g.NumberOfThreads, g.Threads, TRUE, INFINITE, TRUE);
            }
            if (g.sxsDll != NULL)
            {
                FreeLibrary(g.sxsDll);
            }
        }
        __finally
        {
            FusionpSetSystemSetupInProgress(false);
        }
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
#if DBG
            if (IsDebuggerPresent())
            {
                FUSION_DEBUG_BREAK();
            }
#endif
            i = GetExceptionCode();
            WCHAR message[128];
            DWORD flags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
            FormatMessageW(flags, NULL, i, 0, message, NUMBER_OF(message), NULL);
            PWSTR end = message + wcslen(message);
            while (end != message && isspace(*(end - 1)))
            {
                --end;
            }
            *end = 0;
            ::Trace("%ls failed, %d, %#x, %ls", g.lastOperation, i, i, message);
        }
        PTEB teb;
        teb = NtCurrentTeb();
        if (teb->CountOfOwnedCriticalSections != 0)
        {
            DbgPrint("teb->CountOfOwnedCriticalSections %d\n", teb->CountOfOwnedCriticalSections);
            //ASSERT(teb->CountOfOwnedCriticalSections == 0);
        }

        return i ? EXIT_SUCCESS : EXIT_FAILURE;
    }
    return Usage(argv[0]);

Exit:
    return ::GetLastError();
}

extern "C" int __cdecl wmain(int argc, wchar_t** argv)
{
//  NtCurrentTeb()->CountOfOwnedCriticalSections = 0;
    PrintIfAnyCriticalSectionsHeld();
    int i = Main(argc, argv);
    PrintIfAnyCriticalSectionsHeld();
    return i;
}

int TestDiffDir(PCWSTR dir1, PCWSTR dir2)
{
    CFusionDirectoryDifference diff;
    CStringBuffer buf1;
    CStringBuffer buf2;
    BOOL fSuccess = FALSE;

    if (!buf1.Win32Assign(dir1, ::wcslen(dir1)))
        goto Exit;
    if (!buf2.Win32Assign(dir2, ::wcslen(dir2)))
        goto Exit;

    if (!::FusionpCompareDirectoriesSizewiseRecursively(&diff, buf1, buf2))
        goto Exit;

    diff.DbgPrint(buf1, buf2);

    fSuccess = TRUE;
Exit:
    return fSuccess ? EXIT_SUCCESS : EXIT_FAILURE;
}

PCWSTR GetUser()
{
    static bool fInited;
    static WCHAR userName[MAX_PATH];
    if (!fInited)
    {
        DWORD size = NUMBER_OF(userName);
        userName[0] = 0;
        userName[1] = 0;
        GetUserNameW(userName, &size);
        if (userName[1] == '-')
        {
            wmemcpy(userName, 2+userName, 1+wcslen(2+userName));
        }
        fInited = true;
    }
    return userName;
}

void UserBreakPoint(PCWSTR user)
{
    if (::IsDebuggerPresent() && _wcsicmp(GetUser(), user) == 0)
    {
        ASSERT2_NTC(FALSE, __FUNCTION__);
    }
}

void LoadSxs()
{
    WCHAR ExePath[MAX_PATH];
    if (g.sxsDll == NULL)
    {
        SetLastOperation(L"LoadLibrary(SXS.DLL)");
    // sxstest is often run from nt\base\win32\fusion\tests\sxstest\whistler\obj\i386\sxstest.exe; try loading sxs.dll
    // via a relative path first
        g.sxsDll = ::LoadLibraryW(L"..\\..\\..\\..\\..\\dll\\whistler\\obj\\i386\\SXS.DLL");
        if (g.sxsDll == NULL)
        {
            GetModuleFileNameW(NULL, ExePath, NUMBER_OF(ExePath));
            wcscpy(1 + wcsrchr(ExePath, '\\'), L"SXS.DLL");
            g.sxsDll = ::LoadLibraryW(ExePath);
        }
        if (g.sxsDll == NULL)
            g.sxsDll = ::LoadLibraryW(L"SXS.DLL");
        if (g.sxsDll == NULL)
            ThrowLastError();
        UserBreakPoint(L"JayKrell");
    }
}

template <typename PFN>
void GetSxsProc(int name, PFN* ppfn)
{
    SetLastOperation(L"GetProcAddress(#%d)", name);
    if (!(*ppfn = reinterpret_cast<PFN>(GetProcAddress(g.sxsDll, reinterpret_cast<PCSTR>(IntToPtr(name))))))
    {
        ThrowLastError();
    }
}

template <typename PFN>
void GetSxsProc(PCSTR name, PFN* ppfn)
{
    SetLastOperation(L"GetProcAddress(%hs)", name);
    if (!(*ppfn = reinterpret_cast<PFN>(GetProcAddress(g.sxsDll, name))))
    {
        ThrowLastError();
    }
}

class CCopyQueueElement : public std::pair<std::wstring, std::wstring>
{
    typedef std::pair<std::wstring, std::wstring> Base;
public:
    CCopyQueueElement() { }

    CCopyQueueElement(const first_type& source, const second_type& target)
        :
    Base(source, target)
    {
    }

          first_type& Source()       { return first; }
    const first_type& Source() const { return first; }
          second_type& Target()       { return second; }
    const second_type& Target() const { return second; }
};

template <typename T> class StdDeque : public std::deque<T, Allocator<T> > { };
template <typename T> class StdVector : public std::vector<T, Allocator<T> > { };

class CCopyQueue : public StdDeque<CCopyQueueElement>
{
    typedef StdDeque<CCopyQueueElement> Base;
public:
    BOOL Callback(
        PSXS_INSTALLATION_FILE_COPY_CALLBACK_PARAMETERS parameters
        )
    {
        push_back(value_type(parameters->pSourceFile, parameters->pDestinationFile));

        parameters->nDisposition = SXS_INSTALLATION_FILE_COPY_DISPOSITION_FILE_QUEUED;
        return TRUE;
    }

    static BOOL WINAPI StaticCallback(
        PSXS_INSTALLATION_FILE_COPY_CALLBACK_PARAMETERS parameters
        )
    {
        CCopyQueue* pThis = reinterpret_cast<CCopyQueue*>(parameters->pvContext);
        BOOL fResult = pThis->Callback(parameters);
        return fResult;
    }

    BOOL Flush()
    {
        BOOL fSuccess = FALSE;
        for (; !empty() ; pop_back())
        {
            const std::wstring& target = back().Target();
            const std::wstring targetDir = target.substr(0, target.find_last_of('\\'));
            FusionpCreateDirectories(targetDir.c_str(), ::wcslen(targetDir.c_str()));
            if (!CopyFileW(back().Source().c_str(), target.c_str(), TRUE))
            {
                goto Exit;
            }
        }
        fSuccess = TRUE;
Exit:
        return fSuccess;
    }
};

int
TestInstall(
    PCWSTR manifest,
    __int64 flags,
    DWORD beginInstallFlags,
    DWORD installFlags,
    DWORD endInstallFlags
    )
{
    PrintIfAnyCriticalSectionsHeld();

    BOOL                        fSuccess = FALSE;
    PVOID                       installCookie = NULL;
    PSXS_BEGIN_ASSEMBLY_INSTALL sxsBeginAssemblyInstall = NULL;
    PSXS_INSTALL_ASSEMBLY_W     sxsInstallAssemblyW = NULL;
    PSXS_END_ASSEMBLY_INSTALL   sxsEndAssemblyInstall = NULL;
    CCopyQueue                  copyQueue;
    BOOL                        fCleanup = FALSE;
    SXS_INSTALL_SOURCE_INFO     SxsInstallInfo = {0};


    PSXS_INSTALLATION_FILE_COPY_CALLBACK    callback = NULL;
    PVOID                                   context = NULL;
    HSPFILEQ                                hSetupCopyQueue = INVALID_HANDLE_VALUE;
    PVOID                                   pDefaultSetupCopyQueueContext = NULL;
    
    LoadSxs();
    GetSxsProc("SxsBeginAssemblyInstall",   &sxsBeginAssemblyInstall);
    GetSxsProc("SxsInstallAssemblyW",       &sxsInstallAssemblyW);
    GetSxsProc("SxsEndAssemblyInstall",     &sxsEndAssemblyInstall);

    if (flags & SXSTEST_INSTALL_MY_QUEUE)
    {
        callback = &CCopyQueue::StaticCallback;
        context = &copyQueue;
    }
    else if (flags & SXSTEST_INSTALL_SETUPAPI_QUEUE)
    {
        callback = SXS_INSTALLATION_FILE_COPY_CALLBACK_SETUP_COPY_QUEUE;
        if (INVALID_HANDLE_VALUE == (hSetupCopyQueue = SetupOpenFileQueue()))
        {
            goto Exit;
        }
        context = hSetupCopyQueue; // random non NULL, step through, then stop debugging
#if DBG
        FUSION_DEBUG_BREAK();
#endif
    }

    PrintIfAnyCriticalSectionsHeld();

    if (!(*sxsBeginAssemblyInstall)(
        beginInstallFlags,
        callback,
        context,
        NULL, // ImpersonationCallback,
        NULL, // ImpersonationContext,
        &installCookie))
    {
        goto Exit;
    }
    fCleanup = TRUE;

    PrintIfAnyCriticalSectionsHeld();

    memset(&SxsInstallInfo, 0, sizeof(SxsInstallInfo));
    SxsInstallInfo.cbSize = sizeof(SxsInstallInfo);
    SxsInstallInfo.dwFlags = SXSINSTALLSOURCE_INSTALLING_SETUP | SXSINSTALLSOURCE_HAS_CODEBASE;
    SxsInstallInfo.pcwszCodebaseName = manifest;

    fSuccess = sxsInstallAssemblyW(
        installCookie,
        installFlags | SXS_INSTALL_ASSEMBLY_FLAG_FROM_DIRECTORY_RECURSIVE | SXS_INSTALL_ASSEMBLY_FLAG_INCLUDE_CODEBASE,
        manifest,
        &SxsInstallInfo);

    PrintIfAnyCriticalSectionsHeld();

    fSuccess = copyQueue.Flush();
Exit:
    if (fCleanup)
    {
        if (flags & SXSTEST_INSTALL_SETUPAPI_QUEUE)
        {
            if (!(pDefaultSetupCopyQueueContext = ::SetupInitDefaultQueueCallback(NULL)))
            {
                fSuccess = FALSE;
            }
            if (fSuccess && !::SetupCommitFileQueue(NULL,
                    hSetupCopyQueue,
                    SetupDefaultQueueCallback,
                    pDefaultSetupCopyQueueContext))
            {
                fSuccess = FALSE;
            }
            if (hSetupCopyQueue != INVALID_HANDLE_VALUE)
            {
                ::SetupCloseFileQueue(hSetupCopyQueue);
                hSetupCopyQueue = NULL;
            }
        }

        sxsEndAssemblyInstall(installCookie, endInstallFlags | (fSuccess ? SXS_END_ASSEMBLY_INSTALL_FLAG_COMMIT : SXS_END_ASSEMBLY_INSTALL_FLAG_ABORT), NULL);
    }

    PrintIfAnyCriticalSectionsHeld();

    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

BOOL
TestManifestSchema(
    int argc,
    wchar_t** argv,
    int* piNext
    )
{
    SXSP_DEBUG_FUNCTION& pfn = pfnSxspDebug;
    BOOL fSuccess = FALSE;

    int i = (*piNext) + 1;

    if (i >= argc)
    {
        fprintf(stderr, "%S: Missing parameter after \"%S\"\n", argv[0], argv[i-1]);
        goto Exit;
    }

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_CHECK_MANIFEST_SCHEMA, 0, argv[i++], NULL);

    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        goto Exit;
    }

    *piNext = i;
    fSuccess = TRUE;

Exit:
    return fSuccess;
}

BOOL
TestXMLParsing(
    int argc,
    wchar_t** argv,
    int* piNext)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    int i = (*piNext) + 1;

    if (i >= argc)
    {
        fprintf(stderr, "%S: missing parameter after \"%S\"\n", argv[0], argv[i-1]);
        goto Exit;
    }

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_XML_PARSER, 0, argv[i], NULL);

    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        goto Exit;
    }

    *piNext = i + 1;
    fSuccess = TRUE;
Exit:
    return fSuccess;
}

BOOL
TestDirect(
    int argc,
    wchar_t** argv,
    int* piNext)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    int i = (*piNext) + 1;

    if (i >= argc)
    {
        fprintf(stderr, "%S: missing parameter after \"%S\"\n", argv[0], argv[i-1]);
        goto Exit;
    }

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_DLL_REDIRECTION, 0, argv[i], NULL);

    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        goto Exit;
    }

    *piNext = i + 1;
    fSuccess = TRUE;
Exit:
    return fSuccess;

}

VOID
PrintBlob(
    FILE *pf,
    PVOID Data,
    SIZE_T Length,
    PCWSTR PerLinePrefix
    )
{
    ULONG Offset = 0;

    if (PerLinePrefix == NULL)
        PerLinePrefix = L"";

    // we'll output in 8-byte chunks as shown:
    //
    //  [prefix]Binary section %p (%d bytes)
    //  [prefix]   00000000: xx-xx-xx-xx-xx-xx-xx-xx (........)
    //  [prefix]   00000008: xx-xx-xx-xx-xx-xx-xx-xx (........)
    //  [prefix]   00000010: xx-xx-xx-xx-xx-xx-xx-xx (........)
    //

    while (Length >= 8)
    {
        BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);

        fprintf(
            pf,
            "%S   %08lx: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x (%c%c%c%c%c%c%c%c)\n",
            PerLinePrefix,
            Offset,
            pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7],
            PRINTABLE(pb[0]),
            PRINTABLE(pb[1]),
            PRINTABLE(pb[2]),
            PRINTABLE(pb[3]),
            PRINTABLE(pb[4]),
            PRINTABLE(pb[5]),
            PRINTABLE(pb[6]),
            PRINTABLE(pb[7]));

        Offset += 8;
        Length -= 8;
    }

    if (Length != 0)
    {
        CStringBuffer buffTemp;
        bool First = true;
        ULONG i;
        BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);

        buffTemp.Win32ResizeBuffer(48, eDoNotPreserveBufferContents);

        buffTemp.Win32Format(L"   %08lx: ", Offset);

        for (i=0; i<8; i++)
        {
            if (Length > 0)
            {
                if (!First)
                    buffTemp.Win32Append("-", 1);
                else
                    First = false;

                buffTemp.Win32FormatAppend(L"%02x", pb[i]);

                Length--;
            }
            else
            {
                buffTemp.Win32Append("   ", 3);
            }
        }

        buffTemp.Win32Append(" (", 2);

        i = 0;

        while (Length != 0)
        {
            CHAR chTemp = static_cast<CHAR>(PRINTABLE(pb[i]));
            i++;
            buffTemp.Win32Append(&chTemp, 1);
            Length--;
        }

        buffTemp.Win32Append(L")", 1);

        fprintf(
            pf,
            "%S%S\n",
            PerLinePrefix,
            static_cast<PCWSTR>(buffTemp));
    }
}

void __stdcall TestWin32Apc(ULONG_PTR arg)
{
    ACTCTXW ac = {sizeof(ac)};
    int     error = 0;
    PWSTR   source = reinterpret_cast<PWSTR>(arg);
    HANDLE  hActCtx = NULL;
    BOOL    fSuccess = FALSE;

    ac.lpSource = source;
    PWSTR pound = wcschr(source, '#');
    if (pound != NULL)
    {
        *pound = 0;
        ac.lpResourceName = pound + 1;
        ac.dwFlags |= ACTCTX_FLAG_RESOURCE_NAME_VALID;
    }
    ac.wProcessorArchitecture = g.wProcessorArchitecture;
    ac.wLangId = g.wLangId;
    hActCtx = ::CreateActCtxW(&ac);
    if (hActCtx == INVALID_HANDLE_VALUE)
    {
        error = ::GetLastError();
        fwprintf(stderr, L"CreateActCtxW(%ls) failed; ::GetLastError() = %d\n", source, error);
        goto Exit;
    }
    //fSuccess = ::ReleaseActCtx(hActCtx);
    fSuccess = TRUE;
    hActCtx = NULL;
    if (!fSuccess)
    {
        error = ::GetLastError();
        goto Exit;
    }
Exit:
    if (error)
        ThrowWin32(error);
}

void
TestWin32(
    wchar_t** argv
    )
{
    CreateThreads();
    int i = 0;

    for (i = 0 ; argv[i] ; ++i)
    {
        if (g.NumberOfThreads)
        {
            if (!QueueUserAPC(TestWin32Apc, g.Threads[i % g.NumberOfThreads], reinterpret_cast<ULONG_PTR>(argv[i])))
            {
                fprintf(stderr, "QueueUserAPC() failed\n");
                ThrowWin32(((ULONG_PTR) (LONG_PTR) -1));
            }
        }
        else
        {
            TestWin32Apc(reinterpret_cast<ULONG_PTR>(argv[i]));
        }
    }
}
#if BETA1
const static WCHAR InheritManifest[] =
L"<assembly manifestversion=\"1.0\" name=\"InheritManifest\">"
L"<dependency assemblyname=\"Microsoft-Visual-CPlusPlus-Runtime-Libraries\" version=\"6.0.0.0\" language=\"0000\"/>"
L"</assembly>"
;
#else   // suppose to be BETA2 or later
const static WCHAR InheritManifest[] =
L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
L"<assemblyIdentity type=\"win32\" name=\"Microsoft.Windows.SystemCompatibleAssembly\" version=\"1.0.0.0\" processorArchitecture=\"x86\" />"
L"<description>System Compatible Default</description> "
L"<dependency> <dependentAssembly>"
L"<assemblyIdentity type=\"win32\" name=\"Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries\" version=\"6.0.0.0\" language=\"*\" processorArchitecture=\"x86\" publicKeyToken=\"6595b64144ccf1df\" />"
L"</dependentAssembly> </dependency></assembly>"
;
#endif


const static WCHAR NoInheritManifest[] =
L"<assembly manifestversion=\"1.0\" name=\"InheritManifest\">"
L"<dependency assemblyname=\"Microsoft-Visual-CPlusPlus-Runtime-Libraries\" version=\"6.0.0.0\" language=\"0000\"/>"
L"<noinherit/>"
L"</assembly>"
;

const static WCHAR RefCountManifest[] =
L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
L"<assemblyIdentity type=\"win32\" name=\"Microsoft.Windows.SxsTest.RefCount\" version=\"1.0.0.0\" processorArchitecture=\"x86\" />"
L"<description>blah</description> "
L"<dependency><dependentAssembly>"
//L"<assemblyIdentity type=\"win32\" name=\"Microsoft.Windows.SxsTest1\" version=\"1.0.0.0\" language=\"*\" processorArchitecture=\"x86\" publicKeyToken=\"6595b64144ccf1df\" />"
L"<assemblyIdentity type=\"win32\" name=\"Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries\" version=\"6.0.0.0\" language=\"*\" processorArchitecture=\"x86\" publicKeyToken=\"6595b64144ccf1df\" />"
L"</dependentAssembly> </dependency></assembly>"
;

// to test the empty actctx, we push this, probe, push empty, probe
const static PCWSTR DependentOnMsvc6Manifest = InheritManifest;

WCHAR SearchPathResult[MAX_PATH];

void ProbeContext(const char* Function, PCWSTR Dll = L"msvcrt.dll")
{
    SearchPathResult[0] = 0;
    SearchPathW(NULL, Dll, NULL, NUMBER_OF(SearchPathResult), SearchPathResult, NULL);

    DbgPrint("%s %ls\n", Function, SearchPathResult);
}

DWORD CALLBACK InheritThreadMain(VOID*)
{
    ProbeContext(__FUNCTION__);
    return 0;
}

DWORD CALLBACK NoinheritThreadMain(VOID*)
{
    ProbeContext(__FUNCTION__);
    return 0;
}

void TestInherit()
{
    ProbeContext(__FUNCTION__);
    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(InheritManifest);
    ULONG_PTR Cookie;
    ActivateActCtx(ActivationContextHandle, &Cookie);
    ProbeContext(__FUNCTION__);
    DWORD ThreadId;
    WaitForSingleObject(CreateThread(NULL, 0, InheritThreadMain, NULL, 0, &ThreadId), INFINITE);
}

void TestNoInherit()
{
    ProbeContext(__FUNCTION__);
    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(NoInheritManifest);
    ULONG_PTR Cookie;
    ActivateActCtx(ActivationContextHandle, &Cookie);
    ProbeContext(__FUNCTION__);
    DWORD ThreadId;
    WaitForSingleObject(CreateThread(NULL, 0, NoinheritThreadMain, NULL, 0, &ThreadId), INFINITE);
}

void TestEmpty()
{
    ProbeContext(__FUNCTION__);
    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(DependentOnMsvc6Manifest);
    ULONG_PTR Cookie1, Cookie2;
    ActivateActCtx(ActivationContextHandle, &Cookie1);
    ProbeContext(__FUNCTION__);
    ActivateActCtx(ACTCTX_EMPTY, &Cookie2);
    ProbeContext(__FUNCTION__);
    DeactivateActCtx(0, Cookie2);
    ProbeContext(__FUNCTION__);
    DeactivateActCtx(0, Cookie1);
    ProbeContext(__FUNCTION__);
}

#if 0

#define SXSTEST_NTOPENFILE_RET  NTSTATUS
#define SXSTEST_NTOPENFILE_CALL NTAPI
#define SXSTEST_NTOPENFILE_ARG_NAMES_TYPES \
 (\
    OUT PHANDLE FileHandle, \
    IN ACCESS_MASK DesiredAccess, \
    IN POBJECT_ATTRIBUTES ObjectAttributes, \
    OUT PIO_STATUS_BLOCK IoStatusBlock, \
    IN ULONG ShareAccess, \
    IN ULONG OpenOptions \
)

#define SXSTEST_NTCREATEFILE_RET  NTSTATUS
#define SXSTEST_NTCREATEFILE_CALL NTAPI
#define SXSTEST_NTCREATEFILE_ARG_NAMES_TYPES \
 (\
    OUT PHANDLE FileHandle, \
    IN ACCESS_MASK DesiredAccess, \
    IN POBJECT_ATTRIBUTES ObjectAttributes, \
    OUT PIO_STATUS_BLOCK IoStatusBlock, \
    IN PLARGE_INTEGER AllocationSize OPTIONAL, \
    IN ULONG FileAttributes, \
    IN ULONG ShareAccess, \
    IN ULONG CreateDisposition, \
    IN ULONG CreateOptions, \
    IN PVOID EaBuffer OPTIONAL, \
    IN ULONG EaLength \
)

SXSTEST_NTCREATEFILE_RET (SXSTEST_NTCREATEFILE_CALL* SxsTestDynamicNtCreateFileTrampoline)(...);

SXSTEST_NTCREATEFILE_RET
DETOUR_TRAMPOLINE(
    SXSTEST_NTCREATEFILE_RET SXSTEST_NTCREATEFILE_CALL SxsTestNtCreateFileTrampoline SXSTEST_NTCREATEFILE_ARG_NAMES_TYPES,
    NtCreateFile
    );

DETOUR_TRAMPOLINE(
    SXSTEST_NTOPENFILE_RET SXSTEST_NTOPENFILE_CALL SxsTestNtOpenFileTrampoline SXSTEST_NTCREATEFILE_ARG_NAMES_TYPES,
    NtOpenFile
    );

#endif

void TestRefCount()
{
    //
    // 1) newly created actctx has refcount==1
    // 2) activated actctx has refcount==1
    // 3) load a library with no deps with actctx, refcount==2
    // 4) freelibrary, refcount==1
    //    directory of library is closed
    // 5) release actctx refcount==0
    //
    // First order, just step through the code to look at the refcount.
    // Second order, "detour" like crazy and look at the memory
    //  (including detouring away RtlFreeHeap, NtUnmapViewOfSection)
    //

    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(RefCountManifest);
    ULONG_PTR Cookie1;
    ActivateActCtx(ActivationContextHandle, &Cookie1);
    FreeLibrary(LoadLibraryW(L"msvcrt.dll"));
    DeactivateActCtx(0, Cookie1);
    ReleaseActCtx(ActivationContextHandle);

#if 0
    WCHAR RenameDirectorySource[MAX_PATH];
    WCHAR RenameDirectoryDestination[MAX_PATH];

    //
    // Try to rename the directory.
    //
    DbgPrint("dll was loaded from %ls, now see if we can rename that directory\n", SearchPathResult);

    wcscpy(RenameDirectorySource, SearchPathResult);
    *wcsrchr(SearchPathResult, '\\') = 0;
    wcscpy(RenameDirectoryDestination, RenameDirectorySource);
    wcscpy(wcsrchr(RenameDirectoryDestination, '\\'), L"blah");

    BOOL Success = MoveFileW(RenameDirectorySource, RenameDirectoryDestination);
    int Error = ::GetLastError();
    DbgPrint("MoveFile(%ls -> %ls) %s, Error=%d\n",
        RenameDirectorySource, RenameDirectoryDestination, Success ? "succeeded" : "failed", Error);
#endif
}

GUID Guids[100];
WCHAR GuidStrings[100][64];

void FormatGuid(WCHAR* String, const GUID& Guid)
{
    swprintf(
        String,
        L"%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
        Guid.Data1,
        Guid.Data2,
        Guid.Data3,
        Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3],
        Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]);
}

extern "C" HRESULT __stdcall
DllGetClassObject(
    REFCLSID rclsid,  //CLSID for the class object
    REFIID riid,      //Reference to the identifier of the interface
                    // that communicates with the class object
    LPVOID * ppv      //Address of output variable that receives the
                    // interface pointer requested in riid
    )
{
    WCHAR GuidString[64];

    FormatGuid(GuidString, rclsid);
    printf("%s : {%ls}\n", __FUNCTION__, GuidString);

    if (riid == IID_IUnknown)
    {
        *ppv = &g.unknown;
        return S_OK;
    }
    else
    {
        return E_NOINTERFACE;
    }
}

void TestGuidSort()
{
    const int MAX = 100;
    FN_TRACE_SMART_TLS();

    CStringBuffer Manifest;
    int i = 0;

    Ole32.CoInitialize(NULL);

    Manifest.Win32ResizeBuffer(1 << 15, eDoNotPreserveBufferContents);
    Manifest.Win32Format(
        L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\"> \
                <assemblyIdentity \
                    type=\"win32\" \
                    name=\"Microsoft.Windows.SxsTest.GuidSort\" \
                    version=\"1.0.0.0\" \
                    processorArchitecture=\"x86\" \
                    publicKeyToken=\"6595b64144ccf1df\" \
                /> \
            <file name=\"sxstest.exe\">");

    for (i = 0 ; i < MAX ; ++i)
    {
        GUID& Guid = Guids[i];
        CoCreateGuid(&Guid);

        FormatGuid(GuidStrings[i], Guid);
        if (!Manifest.Win32FormatAppend(
            L"\n<comClass description=\"a%d\" clsid=\"{%ls}\"/>",
            i,
            static_cast<PCWSTR>(GuidStrings[i])))
            ThrowLastError();
    }

    if (!Manifest.Win32FormatAppend(L"\n</file>\n</assembly>"))
        ThrowLastError();

    //LoadSxs();
    //GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);
    //fSuccess = (*pfn)(SXS_DEBUG_DLL_REDIRECTION, 0, argv[i], NULL);

    printf("%ls\n", static_cast<PCWSTR>(Manifest));

    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(Manifest);
    ULONG_PTR Cookie1;
    ActivateActCtx(ActivationContextHandle, &Cookie1);
    for (i = 0 ; i < MAX ; ++i)
    {
        HRESULT hr;
        PVOID   pv = NULL;

        hr = Ole32.CoCreateInstance(Guids[i], NULL, CLSCTX_INPROC, IID_IUnknown, &pv);
        printf("CoCreateInstance({%ls}): %08lx%s%s%s\n",

            GuidStrings[i],

            static_cast<unsigned long>(hr),

            ( (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
            || hr == REGDB_E_CLASSNOTREG
            || (hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))
            || (hr == E_NOINTERFACE)
           ) ? "(" : "",

            (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
                ? "ERROR_FILE_NOT_FOUND"
                : (hr == REGDB_E_CLASSNOTREG)
                ? "REGDB_E_CLASSNOTREG"
                : (hr == ERROR_INVALID_PARAMETER)
                ? "ERROR_INVALID_PARAMETER"
                : (hr == E_NOINTERFACE)
                ? "E_NOINTERFACE (ok)"
                : "",

            ( hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
            || hr == REGDB_E_CLASSNOTREG
            || hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))
            || hr == E_NOINTERFACE
            ? ")" : "");
    }
    DeactivateActCtx(0, Cookie1);
    ReleaseActCtx(ActivationContextHandle);
}

void TestStringSort()
{
    /*
    Mike says this takes between 2 and 7 to visit the code.
    */
    const int MAX = 50;
    FN_TRACE_SMART_TLS();
    WCHAR ExePath[MAX_PATH];
    CStringBuffer DllPaths[MAX];

    CStringBuffer Manifest;
    int i = 0;

    if (!Kernel32.GetModuleFileNameW(NULL, ExePath, RTL_NUMBER_OF(ExePath)))
        ThrowLastError();

    if (!Manifest.Win32ResizeBuffer(1 << 15, eDoNotPreserveBufferContents))
        ThrowLastError();
    if (!Manifest.Win32Format(
        L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\"> \
                <assemblyIdentity \
                    type=\"win32\" \
                    name=\"Microsoft.Windows.SxsTest.StringSort\" \
                    version=\"1.0.0.0\" \
                    processorArchitecture=\"x86\" \
                    publicKeyToken=\"6595b64144ccf1df\" \
                /> \
            <file name=\"sxstest.exe\"/>"))
        ThrowLastError();
    for (i = 0 ; i < MAX ; ++i)
    {
        if (!DllPaths[i].Win32Format(L"%ls.%d.dll", ExePath, i))
            ThrowLastError();

        if (!::CopyFileW(ExePath, DllPaths[i], FALSE))
            ThrowLastError();

        SetDllBitInPeImage(DllPaths[i]);

        if (!Manifest.Win32FormatAppend(L"\n<file name=\"%ls\"/>\n", 1 + wcsrchr(static_cast<PCWSTR>(DllPaths[i]), '\\')))
            ThrowLastError();
    }
    if (!Manifest.Win32FormatAppend(L"\n</assembly>"))
        ThrowLastError();

    //LoadSxs();
    //GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);
    //fSuccess = (*pfn)(SXS_DEBUG_DLL_REDIRECTION, 0, argv[i], NULL);

    printf("%ls\n", static_cast<PCWSTR>(Manifest));

    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(Manifest);
    ULONG_PTR Cookie1;
    ActivateActCtx(ActivationContextHandle, &Cookie1);
    for (i = 0 ; i < MAX ; ++i)
    {
        HMODULE h;
        PCWSTR DllName = 1 + wcsrchr(DllPaths[i], '\\');

        h = ::LoadLibraryW(DllName);
        printf("LoadLibrary(%ls):%p, LastError=%d\n",
            DllName,
            h,
            ::GetLastError());
        //FreeLibrary(h);
        ::DeleteFileW(DllPaths[i]);
    }
    DeactivateActCtx(0, Cookie1);
    ReleaseActCtx(ActivationContextHandle);
}

int
TestSearchPathHelper1(
    PCSTR RunId,
    PCWSTR Path,
    PCWSTR File,
    PCWSTR Extension,
    bool GetFilePart,
    ULONG cch
    )
{
    WCHAR Buffer[65536]; // we know that the underlying code can never use a buffer this big
    PWSTR FilePart = NULL;
    PWSTR *lpFilePart = (GetFilePart ? &FilePart : NULL);
    SetLastError(ERROR_GEN_FAILURE);
    ULONG Result = ::SearchPathW(
        Path,
        File,
        Extension,
        0,
        NULL,
        lpFilePart);

    printf("SearchPath() RunId = %s (Path %s; File %s; Extension %s; GetFilePart %s; cch = %lu; buffer=null) result = %lu ::GetLastError() = %u; FilePart = %s %u\n",
        RunId,
        Path ? "present" : "null",
        File ? "present" : "null",
        Extension ? "present" : "null",
        GetFilePart ? "true" : "false",
        0,
        Result,
        ::GetLastError(),
        FilePart ? "present" : "null",
        FilePart ? (FilePart - Buffer) : 0);

    SetLastError(ERROR_GEN_FAILURE);
    FilePart = NULL;
    ULONG NewResult = ::SearchPathW(
        Path,
        File,
        Extension,
        Result,
        Buffer,
        lpFilePart);

    printf("SearchPath() RunId = %s (Path %s; File %s; Extension %s; GetFilePart %s; cch = %lu) result = %lu ::GetLastError() = %u; FilePart = %s %u\n",
        RunId,
        Path ? "present" : "null",
        File ? "present" : "null",
        Extension ? "present" : "null",
        GetFilePart ? "true" : "false",
        Result,
        NewResult,
        ::GetLastError(),
        FilePart ? "present" : "null",
        FilePart ? (FilePart - Buffer) : 0);

    SetLastError(ERROR_GEN_FAILURE);
    FilePart = NULL;
    Result = ::SearchPathW(
        Path,
        File,
        Extension,
        cch,
        Buffer,
        lpFilePart);

    printf("SearchPath() RunId = %s (Path %s; File %s; Extension %s; GetFilePart %s; cch = %lu) result = %lu ::GetLastError() = %u; FilePart = %s %u\n",
        RunId,
        Path ? "present" : "null",
        File ? "present" : "null",
        Extension ? "present" : "null",
        GetFilePart ? "true" : "false",
        cch,
        Result,
        ::GetLastError(),
        FilePart ? "present" : "null",
        FilePart ? (FilePart - Buffer) : 0);

    return EXIT_SUCCESS;
}

int
TestSearchPathHelper2(
    PCSTR RunId,
    PCWSTR Path,
    PCWSTR File,
    PCWSTR Extension,
    ULONG cch
    )
{
    TestSearchPathHelper1(RunId, NULL, File, NULL, true, cch);
    TestSearchPathHelper1(RunId, NULL, File, NULL, false, cch);
    TestSearchPathHelper1(RunId, Path, File, NULL, true, cch);
    TestSearchPathHelper1(RunId, Path, File, NULL, false, cch);
    TestSearchPathHelper1(RunId, NULL, File, Extension, true, cch);
    TestSearchPathHelper1(RunId, NULL, File, Extension, false, cch);
    TestSearchPathHelper1(RunId, Path, File, Extension, true, cch);
    TestSearchPathHelper1(RunId, Path, File, Extension, false, cch);

    return EXIT_SUCCESS;
}

int
TestSearchPathHelper3(
    PCSTR RunId,
    PCWSTR Path,
    PCWSTR File,
    PCWSTR Extension
    )
{
    ULONG i;

    for (i=0; i<5; i++)
        TestSearchPathHelper2(RunId, Path, File, Extension, i);

    for (i=MAX_PATH-5; i<(MAX_PATH+5); i++)
        TestSearchPathHelper2(RunId, Path, File, Extension, i);

    for (i=32760; i<32770; i++)
        TestSearchPathHelper2(RunId, Path, File, Extension, i);

    return EXIT_SUCCESS;
}

BOOL
TestSearchPath(
    int argc,
    wchar_t** argv,
    int* piNext
    )
{
    ULONG i;
    PWSTR PathToSearch = (PWSTR) malloc(100000* sizeof(WCHAR));
    int iNext = (*piNext) + 1;

    PathToSearch[0] = L'C';
    PathToSearch[1] = L'\\';
    for (i=2; i<60000; i++)
        PathToSearch[i] = L'X';
    wcscpy(&PathToSearch[i], L";C:\\");

    TestSearchPathHelper3("1.0", L"C:\\DirectoryDoesNotExist;C:\\", L"boot.ini", L".ini");
    TestSearchPathHelper3("1.1", L"C:\\DirectoryDoesNotExist;C:\\", L"boot.", L".ini");
    TestSearchPathHelper3("1.2", L"C:\\DirectoryDoesNotExist;C:\\", L"boot", L".ini");
    TestSearchPathHelper3("1.3", L"C:\\DirectoryDoesNotExist;C:\\", L"doesnotexist.doesnotexist", L".ini");
    TestSearchPathHelper3("1.4", L"C:\\DirectoryDoesNotExist;C:\\", L"d:\\readme.txt", L".ini");
    TestSearchPathHelper3("1.5", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32.dll", L".dll");
    TestSearchPathHelper3("1.6", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32.", L".dll");
    TestSearchPathHelper3("1.7", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32", L".dll");
    TestSearchPathHelper3("1.8", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32.dll", L".ini");
    TestSearchPathHelper3("1.9", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32.", L".ini");
    TestSearchPathHelper3("1.10", L"C:\\DirectoryDoesNotExist;C:\\", L"kernel32", L".ini");

    TestSearchPathHelper3("2.0", L"C:\\;C:\\DirectoryDoesNotExist", L"boot.ini", L".ini");
    TestSearchPathHelper3("2.1", L"C:\\;C:\\DirectoryDoesNotExist", L"boot.", L".ini");
    TestSearchPathHelper3("2.2", L"C:\\;C:\\DirectoryDoesNotExist", L"boot", L".ini");
    TestSearchPathHelper3("2.3", L"C:\\;C:\\DirectoryDoesNotExist", L"doesnotexist.doesnotexist", L".ini");
    TestSearchPathHelper3("2.4", L"C:\\;C:\\DirectoryDoesNotExist", L"d:\\readme.txt", L".ini");
    TestSearchPathHelper3("2.5", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32.dll", L".dll");
    TestSearchPathHelper3("2.6", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32.", L".dll");
    TestSearchPathHelper3("2.7", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32", L".dll");
    TestSearchPathHelper3("2.8", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32.dll", L".ini");
    TestSearchPathHelper3("2.9", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32.", L".ini");
    TestSearchPathHelper3("2.10", L"C:\\;C:\\DirectoryDoesNotExist", L"kernel32", L".ini");

    TestSearchPathHelper3("3.0", PathToSearch, L"boot.ini", L".ini");
    TestSearchPathHelper3("3.1", PathToSearch, L"boot", L".ini");
    TestSearchPathHelper3("3.1", PathToSearch, L"boot.", L".ini");
    TestSearchPathHelper3("3.2", PathToSearch, L"doesnotexist.doesnotexist", L".ini");
    TestSearchPathHelper3("3.3", PathToSearch, L"d:\\readme.txt", L".ini");

    *piNext = iNext;

    return EXIT_SUCCESS;
}

BOOL
TestAct(
    int argc,
    wchar_t** argv,
    int* piNext)
{
    ULONG i, c;
    ULONG_PTR *prgCookies = NULL;
    HANDLE hActCtx = NULL;
    CHAR buffer[1024];
    int iNext = (*piNext + 1);
    BOOL fSuccess = FALSE;

    if (iNext >= argc)
    {
        fprintf(stderr, "%S: missing parameter after \"%S\"\n", argv[0], argv[iNext - 1]);
        goto Exit;
    }

    WideCharToMultiByte(CP_ACP, 0, argv[iNext++], -1, buffer, NUMBER_OF(buffer), NULL, NULL);

    ACTCTXA ac;

    ac.cbSize = sizeof(ac);
    ac.dwFlags = 0;
    ac.lpSource = buffer;
    ac.wProcessorArchitecture = g.wProcessorArchitecture;
    ac.wLangId = g.wLangId;

    hActCtx = ::CreateActCtxA(&ac);
    if (hActCtx == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "CreateActCtxA() failed; ::GetLastError() = %d\n", ::GetLastError());
        return EXIT_FAILURE;
    }

    c = 0;

    if (argc > iNext)
        c = _wtoi(argv[iNext++]);

    if (c == 0)
        c = 100;

    prgCookies = NEW(ULONG_PTR[c]);
    if (prgCookies == NULL)
    {
        fprintf(stderr, "Unable to allocate %lu cookies.\n", c);
        goto Exit;
    }

    for (i=0; i<c; i++)
    {
        if (!ActivateActCtx(hActCtx, &prgCookies[i]))
        {
            fprintf(stderr, "Attempt to activate to depth %lu failed; ::GetLastError() = %d\n", i, ::GetLastError());
            goto Exit;
        }
    }

    for (i=0; i<c; i++)
    {
        ULONG j = (c - i) - 1;
        DeactivateActCtx(0, prgCookies[j]);
    }

    ReleaseActCtx(hActCtx);

    fSuccess = TRUE;
    *piNext = iNext;

Exit:
    delete []prgCookies;

    return fSuccess;
}

HRESULT Helper_WriteStream(CBaseStringBuffer * pFileNameBuf,
                           IStream *pStream)
{
    HRESULT     hr          = NOERROR;
    LPBYTE      pBuf[0x4000];
    DWORD       cbBuf       = 0x4000;
    DWORD       dwWritten   = 0;
    DWORD       cbRead      = 0;
    HANDLE      hf          = INVALID_HANDLE_VALUE;

    hf = ::CreateFileW(static_cast<PCWSTR>(*pFileNameBuf), GENERIC_READ, FILE_SHARE_READ,
            NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hf == INVALID_HANDLE_VALUE){
        hr = HRESULT_FROM_WIN32 (GetLastError());
        goto Exit;
    }

    while (::ReadFile(hf, pBuf, cbBuf, &cbRead, NULL) && cbRead){
        hr = pStream->Write(pBuf, cbRead, &dwWritten);
        if (FAILED(hr))
            goto Exit;
    }

    if (! SUCCEEDED(hr =pStream->Commit(0)))
        goto Exit;

    CloseHandle(hf);

Exit:
    return hr;
}

BOOL
TestMSIInstall(
    int argc,
    wchar_t** argv,
    int* piNext
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    CSmartRef<IAssemblyCache>     pCache;
    CSmartRef<IAssemblyCacheItem> ppCacheItem[4];
    CSmartRef<IStream>            pStream;
    PFNCreateAssemblyCache      pCreateAssemblyCache = NULL;
    CStringBuffer               SourceFilePathBuf;
    CStringBuffer               SourceFileNameBuf;

    CHAR                        buf[1024];
    FILE*                       fp = NULL;
    LPSTR                       p1, pbuf ;
    int                         i, cAsm;

    int iArg = (*piNext) + 1;
    if (iArg >= argc)
    {
        fprintf(stderr, "%S: missing parameter after \"%S\"\n", argv[0], argv[iArg-1]);
        goto Exit;
    }

    LoadSxs();
    GetSxsProc("CreateAssemblyCache", &pCreateAssemblyCache);
    IFCOMFAILED_EXIT(pCreateAssemblyCache(&pCache, 0));


    WideCharToMultiByte(CP_ACP, 0, argv[iArg], -1, buf, NUMBER_OF(buf), NULL, NULL);
    fp = fopen(buf, "r");
    if (!fp)  {
        fprintf(stderr, "%S: Error opening script file \"%S\"\n", argv[0], argv[iArg]);
        goto Exit;
    }
    cAsm = 0;
    while (! feof(fp)) {
        if (! fgets(buf, 80, fp)) { // end of file or error
            if (ferror(fp)){ // error occur
                fprintf(stderr, "%S: Error occur while reading the script file\n", argv[0]);
                goto Exit;
            }
            else{ // end of file
                fprintf(stderr, "%S: end of script file\n", argv[0]);
                break;
            }
        }
        // trim the string
        i = 0 ;
        while (buf[i] == ' ') i++; // skip the whitespace at the beginning of the line
        pbuf = buf + i ; // pointer to the first un-whitespace char in the line
        i = 0 ;
        while (pbuf[i] != '\n') i++;
        pbuf[i] = '\0';

        p1 = NULL;
        p1 = strchr(pbuf, ' ');
        if (p1 == NULL) { // instruction line
            if (strcmp(pbuf, "BeginAsmCacheItem") == 0) {
                IFCOMFAILED_EXIT(pCache->CreateAssemblyCacheItem(0, NULL, &ppCacheItem[cAsm], NULL));
            }else
            if (strcmp(pbuf, "EndAsmCacheItem") == 0) {
                IFCOMFAILED_EXIT(ppCacheItem[cAsm]->Commit(0, NULL));
                cAsm ++;
            }
        }
        else
        { // get the first word of the line
            *p1 = '\0';
            p1++; // p1 points to the filename now
            IFW32FALSE_EXIT(SourceFileNameBuf.Win32Assign(p1, ::strlen(p1)));
            if (strcmp(pbuf,"SourceFilePath") == 0) { // fullpath of source files, which would be in a CD
                IFW32FALSE_EXIT(SourceFilePathBuf.Win32Assign(p1, ::strlen(p1)));
                SourceFilePathBuf.Win32EnsureTrailingPathSeparator();
            }else
            if (strcmp(pbuf, "FILE") == 0) {
                IFCOMFAILED_EXIT(ppCacheItem[cAsm]->CreateStream(0, SourceFileNameBuf, STREAM_FORMAT_WIN32_MODULE, 0, &pStream, NULL));
                IFW32FALSE_EXIT(SourceFileNameBuf.Win32Assign(SourceFilePathBuf, ::wcslen(SourceFilePathBuf)));
                IFW32FALSE_EXIT(SourceFileNameBuf.Win32Append(p1, ::strlen(p1))); // containing full-path of the source file
                IFCOMFAILED_EXIT(Helper_WriteStream(&SourceFileNameBuf, pStream));
                pStream.Release(); // stream should be released since writtingfile has been done

            }else
            if (strcmp(pbuf, "MANIFEST") == 0) {
                IFCOMFAILED_EXIT(ppCacheItem[cAsm]->CreateStream(0, SourceFileNameBuf, STREAM_FORMAT_WIN32_MANIFEST, 0, &pStream, NULL));
                IFW32FALSE_EXIT(SourceFileNameBuf.Win32Assign(SourceFilePathBuf, SourceFilePathBuf.Cch())); // containing full-path of the source file
                IFW32FALSE_EXIT(SourceFileNameBuf.Win32Append(p1, ::strlen(p1)));
                IFCOMFAILED_EXIT(Helper_WriteStream(&SourceFileNameBuf, pStream));
                pStream.Release(); // stream should be released since writtingfile has been done
            }
        } // end of else
    }// end of while

    fSuccess = TRUE;
    *piNext = iArg;
Exit:
    fp ? fclose(fp) : 0;
    return fSuccess;
}

CDirWalk::ECallbackResult
DirWalkCallback(
    CDirWalk::ECallbackReason reason,
    CDirWalk*                 dirWalk,
    DWORD                     dwFlags
    )
{
    PCWSTR parent = dirWalk->m_strParent;
    PCWSTR leaf   = dirWalk->m_fileData.cFileName;
    if (reason == CDirWalk::eFile)
        printf("file %lu, %ls, %ls\n", reason, parent, leaf);
    else
        printf("directory %lu, %ls\n", reason, parent);
    return CDirWalk::eKeepWalking;
}

int
TestDirWalk(
    PCWSTR  root,
    PWSTR   filter
    )
{
    CDirWalk dirWalk;
    StdVector<std::wstring> vstrFilter;
    StdVector<PCWSTR> vstrFilter2;
    PWSTR filterTok;

    if (filterTok = wcstok(filter, L";"))
    {
        do
        {
            vstrFilter.push_back(filterTok);
            vstrFilter2.push_back(vstrFilter.back().c_str());
        } while (filterTok = wcstok(NULL, L";"));
    }
    dirWalk.m_fileFiltersBegin = &*vstrFilter2.begin();
    dirWalk.m_fileFiltersEnd = &*vstrFilter2.end();
    dirWalk.m_strParent.Win32Assign(root, ::wcslen(root));
    dirWalk.m_callback = DirWalkCallback;
    dirWalk.Walk();
    return 0;
}

int
TestMultiAct(
    int argc,
    wchar_t **argv
    )
{
    HANDLE h1, h2;
    ACTCTXW acw;

    memset(&acw, 0, sizeof(acw));

    acw.cbSize = sizeof(acw);
    acw.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
    acw.lpSource = argv[2];
    acw.lpResourceName = MAKEINTRESOURCEW(1);

    h1 = ::CreateActCtxW(&acw);
    if (h1 == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "1st CreateActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());
        return EXIT_FAILURE;
    }

    h2 = ::CreateActCtxW(&acw);
    if (h2 == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "2nd CreateActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

BOOL
ParseProcessorArchitecture(
    int argc,
    wchar_t** argv,
    int* piCurrent
    )
{
    BOOL fSuccess = FALSE;

    int i = *piCurrent;

    i++;

    if (i >= argc)
    {
        fprintf(stderr, "%S: missing parameter after %S\n", argv[0], argv[i - 1]);
        goto Exit;
    }

    if (::FusionpStrCmpI(argv[i], L"x86") == 0)
        g.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
    else if (::FusionpStrCmpI(argv[i], L"i386") == 0)
        g.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
    else if (::FusionpStrCmpI(argv[i], L"ia64") == 0)
        g.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
    else if (::FusionpStrCmpI(argv[i], L"alpha64") == 0)
        g.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA64;
    else
    {
        fprintf(stderr, "%S: invalid -pa value \"%S\"\n", argv[0], argv[i]);
        goto Exit;
    }

    *piCurrent = i + 1;

    fSuccess = TRUE;
Exit:
    return fSuccess;
}

BOOL
ParseLangId(
    int argc,
    wchar_t** argv,
    int* piCurrent
    )
{
    BOOL fSuccess = FALSE;

    int i = *piCurrent;

    i++;

    if (i >= argc)
    {
        fprintf(stderr, "%S: missing parameter after %S\n", argv[0], argv[i - 1]);
        goto Exit;
    }

    swscanf(argv[i], L"%hx", &g.wLangId);

    *piCurrent = i + 1;

    fSuccess = TRUE;
Exit:
    return fSuccess;
}

BOOL
TestLoadLibrary(
    int argc,
    wchar_t** argv,
    int* piNext
    )
{
    int i = (*piNext) + 1;
    HINSTANCE hInstance = NULL;
    BOOL fExpectedToFail = FALSE;
    BOOL fSuccess = FALSE;

    while (i < argc)
    {
        if (::FusionpStrCmpI(argv[i], L"-fail-") == 0)
            fExpectedToFail = TRUE;
        else if (::FusionpStrCmpI(argv[i], L"-succeed-") == 0)
            fExpectedToFail = FALSE;
        else
        {
            hInstance = LoadLibraryW(argv[i]);
            if (hInstance == NULL)
            {
                if (!fExpectedToFail)
                {
                    fprintf(stderr, "%S: Failed to LoadLibraryW(\"%S\"); ::GetLastError() = %d\n", argv[0], argv[i], ::GetLastError());
                }
            }
            else
            {
                if (fExpectedToFail)
                {
                    WCHAR LibraryPath[4096];

                    Kernel32.GetModuleFileNameW(hInstance, LibraryPath, NUMBER_OF(LibraryPath));

                    fprintf(stderr, "%S: LoadLibraryW(\"%S\") was supposed to fail, but instead we got \"%S\"\n", argv[0], argv[i], LibraryPath);
                }

                ::FreeLibrary(hInstance);
                hInstance = NULL;
            }
        }

        i++;
    }

    fSuccess = TRUE;
// Exit:
    return fSuccess;
}

int TestAssemblyName()
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_ASSEMBLYNAME_CONVERSION, 0, NULL, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;

}

int TestPrecomiledManifest(PCWSTR manifestFileName)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_PRECOMPILED_MANIFEST, 0, manifestFileName, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

int TestPCMTime(PCWSTR manifestFilename)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_TIME_PCM, 0, manifestFilename, NULL);

    if (! fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

int TestCreateMultiLevelDirectory(PCWSTR dirs)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_CREAT_MULTILEVEL_DIRECTORY, 0, dirs, NULL);

    if (! fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}


BOOL
TestLeakMemory(
    DWORD dwAmount
    )
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    if (dwAmount < 1) {
        fprintf(stderr, "%s got a bad parameter, %d; rectifying to 1.\n", __FUNCTION__, dwAmount);
        dwAmount = 1;
    }

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_FORCE_LEAK, 0, NULL, (PDWORD)&dwAmount);

    if (! fSuccess)
    {
        fprintf(stderr, "%s failed to leak %d bytes of memory!\n", __FUNCTION__, dwAmount);
        return EXIT_FAILURE;
    }
    else
    {
        fprintf(stdout, "%s leaked %d bytes of memory.\n", __FUNCTION__, dwAmount);
        return EXIT_SUCCESS;
    }
}

BOOL
TestManifestProbing(
    int argc,
    wchar_t **argv,
    int *piNext
    )
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_PROBE_MANIFST, 0, NULL, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;

}

BOOL
TestAssemblyProbing(
    int argc,
    wchar_t **argv,
    int *piNext
    )
{
    PSXS_PROBE_ASSEMBLY_INSTALLATION pfn;
    BOOL fSuccess = FALSE;
    DWORD dwDisposition = 0;

    LoadSxs();
    GetSxsProc(SXS_PROBE_ASSEMBLY_INSTALLATION, &pfn);

    fSuccess = (*pfn)(0, argv[*piNext], &dwDisposition);

    return (fSuccess ? EXIT_SUCCESS : EXIT_FAILURE);
}

int TestCreateProcess2(wchar_t** argv)
{
    STARTUPINFOW StartupInfo = { sizeof(StartupInfo) };
    PROCESS_INFORMATION ProcessInformation = {0};

    *argv += wcsspn(*argv, L" \t\r\n");

    BOOL f = ::CreateProcessW(
        *argv,
        NULL,
        NULL,
        NULL,
        FALSE,
        0,
        NULL,
        NULL,
        &StartupInfo,
        &ProcessInformation);
    printf("CreateProcess(%S) returned %s\n", *argv, f ? "True" : "False");

    return EXIT_SUCCESS;
}


int TestCreateProcess(wchar_t** argv)
{
    CUnicodeStringBuffer CommandLine;
    STARTUPINFOW StartupInfo = { sizeof(StartupInfo) };
    PROCESS_INFORMATION ProcessInformation = {0};

    while (*argv)
    {
        CommandLine.Win32Append(L" ", 1);
        CommandLine.Win32Append(*argv, ::wcslen(*argv));
        argv++;
    }

    CUnicodeStringBufferAccessor MutableCommandLine(&CommandLine);
    BOOL f = ::CreateProcessW(
        NULL,
        MutableCommandLine,
        NULL,
        NULL,
        FALSE,
        0,
        NULL,
        NULL,
        &StartupInfo,
        &ProcessInformation);
    printf("CreateProcess(%S) returned %s\n", static_cast<PCWSTR>(MutableCommandLine), f ? "True" : "False");

    return EXIT_SUCCESS;
}

////////////////////////////////////////////////////////////////////////////
// XMLDOM Helper function:
////////////////////////////////////////////////////////////////////////////
HRESULT XMLDOMHelper_WalkTree(IXMLDOMNode* node, int level)
{
    IXMLDOMNode* pChild, *pNext;
    BSTR nodeName;
    IXMLDOMNamedNodeMap* pattrs;

    node->get_nodeName(&nodeName);
    for (int i = 0; i < level; i++)
        printf(" ");
    printf("%S",nodeName);
    SysFreeString(nodeName);


    if (SUCCEEDED(node->get_attributes(&pattrs)) && pattrs != NULL)
    {
        pattrs->nextNode(&pChild);
        while (pChild)
        {
            BSTR name;
            pChild->get_nodeName(&name);
            printf(" %S='", name);
            ::SysFreeString(name);

            VARIANT value;
            pChild->get_nodeValue(&value);
            if (value.vt == VT_BSTR)
            {
                printf("%S", V_BSTR(&value));
            }
            printf("'");
            VariantClear(&value);
            pChild->Release();
            pattrs->nextNode(&pChild);
        }
        pattrs->Release();
    }
    printf("\n");

    node->get_firstChild(&pChild);
    while (pChild)
    {
        XMLDOMHelper_WalkTree(pChild, level+1);
        pChild->get_nextSibling(&pNext);
        pChild->Release();
        pChild = pNext;
    }

    return S_OK;
}
////////////////////////////////////////////////////////////////////////////
// XMLDOM Helper function:
////////////////////////////////////////////////////////////////////////////
HRESULT XMLDOMHelper_ReportError(IXMLDOMParseError *pXMLError)
{
    long line, linePos;
    LONG errorCode;
    BSTR pBURL = NULL, pBReason = NULL;
    HRESULT hr;

    hr = pXMLError->get_line(&line);
    if (FAILED(hr))
        goto Exit;

    hr = pXMLError->get_linepos(&linePos);
    if (FAILED(hr))
        goto Exit;

    hr = pXMLError->get_errorCode(&errorCode);
    if (FAILED(hr))
        goto Exit;

    hr = pXMLError->get_url(&pBURL);
    if (FAILED(hr))
        goto Exit;

    hr = pXMLError->get_reason(&pBReason);
    if (FAILED(hr))
        goto Exit;


    fprintf(stderr, "%S", pBReason);
    if (line > 0)
    {
        fprintf(stderr, "Error on line %d, position %d in \"%S\".\n",
            line, linePos, pBURL);
    }
    hr= E_FAIL;

Exit:
    SysFreeString(pBURL);
    SysFreeString(pBReason);

    return hr;
}

////////////////////////////////////////////////////////////////////////////
// XMLDOM Helper function: Check load results
////////////////////////////////////////////////////////////////////////////
HRESULT XMLDOMHelper_CheckLoad(IXMLDOMDocument* pDoc)
{
    // And since we don't have the VARIANT_BOOL from load we
    // need to check the parse Error errorCode property to see
    // if everything went ok.
    IXMLDOMParseError  *pXMLError = NULL;
    LONG errorCode;
    HRESULT hr = NOERROR;

    hr = pDoc->get_parseError(&pXMLError);
    if (FAILED(hr))
        goto Exit;

    hr = pXMLError->get_errorCode(&errorCode);
    if (FAILED(hr))
        goto Exit;

    if (errorCode != 0){
        hr = XMLDOMHelper_ReportError(pXMLError);
        goto Exit;
    }

     hr = NOERROR;
Exit:
    if (pXMLError)
        pXMLError->Release();

    return hr;
}
////////////////////////////////////////////////////////////////////////////
// XMLDOM Helper function:
////////////////////////////////////////////////////////////////////////////
HRESULT XMLDOMHelper_LoadDocumentSync(IXMLDOMDocument *pDoc, BSTR pBURL)
{
    IXMLDOMParseError  *pXMLError = NULL;
    VARIANT         vURL;
    VARIANT_BOOL    vb;
    HRESULT         hr;

    hr = pDoc->put_async(VARIANT_FALSE);
    if (FAILED(hr))
        goto Exit;

    // Load xml document from the given URL or file path
    VariantInit(&vURL);
    vURL.vt = VT_BSTR;
    V_BSTR(&vURL) = pBURL;
    hr = pDoc->load(vURL, &vb);
    if (FAILED(hr))
        goto Exit;

    hr = XMLDOMHelper_CheckLoad(pDoc);
    if (FAILED(hr))
        goto Exit;

    hr = NOERROR;

Exit:
    if (pXMLError)
        pXMLError->Release();

    return hr;
}


BOOL TestXMLDOM(PCWSTR pswzXMLFileName)
{
    HRESULT hr = S_OK;
    IXMLDOMDocument *pDoc = NULL;
    IXMLDOMNode* pNode = NULL;
    BSTR pBURL = NULL;

    Ole32.CoInitialize(NULL);

    // Create an empty XML document
    hr = Ole32.CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
                                IID_IXMLDOMDocument, (void**)&pDoc);
    if (FAILED(hr))
        goto Exit;

    pBURL = SysAllocString(pswzXMLFileName);
    hr = XMLDOMHelper_LoadDocumentSync(pDoc, pBURL);
    if (FAILED(hr))
        goto Exit;

    // Now walk the loaded XML document dumping the node names to stdout.
    hr = pDoc->QueryInterface(IID_IXMLDOMNode,(void**)&pNode);
    if (FAILED(hr))
        goto Exit;

    hr = XMLDOMHelper_WalkTree(pNode,0);
    if (FAILED(hr))
        goto Exit;

    hr = NOERROR;

Exit:
    if (pDoc) pDoc->Release();
    SysFreeString(pBURL);
    if (pNode) pNode->Release();

    Ole32.CoUninitialize();

    return hr == 0 ? 0 : 1;

}

BOOL TestFusionArray(PCWSTR dir1, PCWSTR dir2)
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_FUSION_ARRAY, 0, dir1, (PVOID)dir2);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

BOOL TestGeneratePathFromIdentityAttributeString(PCWSTR str)
{
    PSXSPGENERATEMANIFESTPATHONASSEMBLYIDENTITY pfn = NULL;

    BOOL        fSuccess = FALSE;
    WCHAR       folderName[5000];
    SIZE_T      cch = NUMBER_OF(folderName);

    LoadSxs();

    GetSxsProc("SxspGenerateManifestPathOnAssemblyIdentity", &pfn);

    fSuccess = (*pfn)((PWSTR) str, folderName, &cch, NULL);

    if (! fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
    {
        wprintf(L"Folder name returned is %s\n", folderName);
        return EXIT_SUCCESS;
    }

}

BOOL
TestDirectoryChangeWatcher(
    int argc,
    wchar_t **argv,
    int *piNext
    )
{
    SXSP_DEBUG_FUNCTION pfn;
    BOOL fSuccess;

    LoadSxs();
    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);
    fSuccess = (*pfn)(SXS_DEBUG_DIRECTORY_WATCHER, 0, NULL, NULL);
    return (fSuccess ? EXIT_SUCCESS : EXIT_FAILURE);
}


BOOL
TestRefreshAssembly(
    PCWSTR wsAssemblyManifest
    )
{
    BOOL fSuccess = FALSE;
    PSXS_INSTALL_ASSEMBLY_W     sxsInstallAssemblyW = NULL;

    LoadSxs();
    GetSxsProc("SxsInstallAssemblyW", &sxsInstallAssemblyW);

    if (!sxsInstallAssemblyW(
        NULL,
        SXS_INSTALL_ASSEMBLY_FLAG_REPLACE_EXISTING,
        wsAssemblyManifest,
        NULL))
    {
        fwprintf(
            stderr,
            L"Failed reinstalling assembly '%ls', 0x%08X\n",
            wsAssemblyManifest,
            ::GetLastError());
    }
    else
    {
        fSuccess = TRUE;
    }

    return fSuccess;
}

BOOL
TestInstallWithInstallInfo(
    PCWSTR wsAssemblyManifest,
    PCWSTR wsReferenceString
    )
{
#define SXS_TEST

    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    PSXS_INSTALL_W_ROUTINE sxsInstallW = NULL;
    SXS_INSTALLW InstallParameters = {sizeof(InstallParameters)};
    SXS_INSTALL_REFERENCEW InstallReference = {sizeof(InstallReference)};

    LoadSxs();
    GetSxsProc("SxsInstallW",               &sxsInstallW);

    InstallParameters.dwFlags = 
        //SXS_INSTALL_FLAG_MOVE |
        SXS_INSTALL_FLAG_REPLACE_EXISTING |
        SXS_INSTALL_FLAG_CODEBASE_URL_VALID |
        SXS_INSTALL_FLAG_REFERENCE_VALID |
        SXS_INSTALL_FLAG_REFRESH_PROMPT_VALID;
    InstallParameters.lpCodebaseURL = wsAssemblyManifest;
    InstallParameters.lpManifestPath = wsAssemblyManifest;
    InstallParameters.lpReference = &InstallReference;
    InstallParameters.lpRefreshPrompt = L"boop";

    InstallReference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OPAQUESTRING;
    InstallReference.lpIdentifier = wsReferenceString ? wsReferenceString : L"TempRef";

    if (!(*sxsInstallW)(&InstallParameters))
    {
        goto Exit;
    }

    fSuccess = TRUE;
Exit:
    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

BOOL
TestInstallLikeWindowsSetup(
    PCWSTR wsAssemblyManifest,
    PCWSTR wsCodebase
    )
{
    BOOL fSuccess = FALSE;
    FN_TRACE_WIN32(fSuccess);
    PSXS_BEGIN_ASSEMBLY_INSTALL sxsBeginAssemblyInstall = NULL;
    PSXS_INSTALL_W_ROUTINE sxsInstallW = NULL;
    PSXS_END_ASSEMBLY_INSTALL sxsEndAssemblyInstall = NULL;
    SXS_INSTALLW InstallParameters = {sizeof(InstallParameters)};
    SXS_INSTALL_REFERENCEW InstallReference = {sizeof(InstallReference)};

    LoadSxs();
    GetSxsProc("SxsBeginAssemblyInstall",   &sxsBeginAssemblyInstall);
    GetSxsProc("SxsInstallW",               &sxsInstallW);
    GetSxsProc("SxsEndAssemblyInstall",     &sxsEndAssemblyInstall);

    IFW32FALSE_EXIT(
        (*sxsBeginAssemblyInstall)(
            SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NOT_TRANSACTIONAL
            | SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NO_VERIFY
            | SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_REPLACE_EXISTING,
            NULL,
            NULL,
            NULL,
            NULL,
            &InstallParameters.pvInstallCookie));

    InstallReference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL;
    InstallReference.lpNonCanonicalData = L"Foom";
    
    InstallParameters.dwFlags |= SXS_INSTALL_FLAG_CODEBASE_URL_VALID 
                              |  SXS_INSTALL_FLAG_FROM_DIRECTORY
                              |  SXS_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE
                              |  SXS_INSTALL_FLAG_REFRESH_PROMPT_VALID
                              |  SXS_INSTALL_FLAG_INSTALL_COOKIE_VALID
                              |  SXS_INSTALL_FLAG_REFERENCE_VALID
                           ;
    InstallParameters.lpCodebaseURL = wsCodebase;
    InstallParameters.lpRefreshPrompt = L"like..Windows CD..";
    InstallParameters.lpManifestPath = wsAssemblyManifest;
    InstallParameters.lpReference = &InstallReference;

    IFW32FALSE_EXIT((*sxsInstallW)(&InstallParameters));

    IFW32FALSE_EXIT(
        (*sxsEndAssemblyInstall)(
            InstallParameters.pvInstallCookie,
            SXS_END_ASSEMBLY_INSTALL_FLAG_COMMIT,
            NULL));
    InstallParameters.pvInstallCookie = NULL;

    fSuccess = TRUE;
Exit:

    if (InstallParameters.pvInstallCookie != NULL)
    {
        CSxsPreserveLastError ple;
        (void) (*sxsEndAssemblyInstall)(InstallParameters.pvInstallCookie, SXS_END_ASSEMBLY_INSTALL_FLAG_ABORT, NULL);
        ple.Restore();
    }

    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

//#define ASM_CPUID { __asm __emit 0fh __asm __emit 0a2h }
#define ASM_CPUID { __asm cpuid }
#define ASM_RDTSC { __asm rdtsc }

inline VOID GetCpuIdLag(LARGE_INTEGER *ref)
{
#if !defined(_WIN64)
    LARGE_INTEGER temp, temp2;
    _asm
    {
        cpuid
        cpuid
        cpuid
        cpuid
        cpuid
        rdtsc
        mov temp.LowPart, eax
        mov temp.HighPart, edx
        cpuid
        rdtsc
        mov temp2.LowPart, eax
        mov temp2.HighPart, edx
    }

    ref->QuadPart = temp2.QuadPart - temp.QuadPart;
#else
    ref->QuadPart = 0;
#endif
}




BOOL
TestOpeningStuff(PCWSTR wsSourceName, PCWSTR wsType, PCWSTR wsCount)
{
    BOOL fSuccess = FALSE;
    LARGE_INTEGER llStartCount, llEndCount, llCountsPerSec, llTotalSizeSoFar;
    LARGE_INTEGER CpuIdLag;
    BYTE bBuffer[65536];
    SIZE_T cNumTries = _wtol(wsCount);
    double dCountsPerSecond, dSeconds, dCountsPerIteration, dSecondsPerIteration;
    int iFinalIterationsPerSecond;

    GetCpuIdLag(&CpuIdLag);

    llTotalSizeSoFar.QuadPart = 0;

    {
        FUSION_PERF_INFO PerfInfo[2];
        HANDLE hFile;

        for (int i = 0; i < 5000; i++)
        {
            PERFINFOTIME(&PerfInfo[0], hFile = ::CreateFileW(wsSourceName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL));
            PERFINFOTIME(&PerfInfo[1], ::CloseHandle(hFile));
        }

        FusionpReportPerfInfo(
            FUSIONPERF_DUMP_TO_STDOUT |
            FUSIONPERF_DUMP_ALL_STATISTICS |
            FUSIONPERF_DUMP_ALL_SOURCEINFO,
            PerfInfo,
            NUMBER_OF(PerfInfo));
    }



    //
    // Map the DLL as a resource a few thousand times.
    //
    if ((wsType[0] == L'd') || (wsType[0] == L's'))
    {
        FUSION_PERF_INFO PerfInfo[7];

        HMODULE     hDllModule;
        HRSRC       hManifestResource;
        HGLOBAL     hResource;
        PVOID       pvResourceData;
        SIZE_T      cbResourceSize;

        for (SIZE_T i = 0; i < cNumTries; i++)
        {
            PERFINFOTIME(&PerfInfo[0], hDllModule = ::LoadLibraryExW(wsSourceName, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE));
            PERFINFOTIME(&PerfInfo[1], hManifestResource = ::FindResourceW(hDllModule, L"#1", MAKEINTRESOURCEW(RT_MANIFEST)));
            PERFINFOTIME(&PerfInfo[2], hResource = ::LoadResource(hDllModule, hManifestResource));
            PERFINFOTIME(&PerfInfo[3], pvResourceData = ::LockResource(hResource));
            PERFINFOTIME(&PerfInfo[4], cbResourceSize = ::SizeofResource(hDllModule, hManifestResource));

            PERFINFOTIME(&PerfInfo[5],
                { for (SIZE_T i2 = 0; i2 < cbResourceSize; i2++)
                bBuffer[i2] = ((PBYTE)pvResourceData)[i2]; }
                );

            PERFINFOTIME(&PerfInfo[6], FreeLibrary(hDllModule))
        }

        FusionpReportPerfInfo(
            FUSIONPERF_DUMP_TO_STDOUT |
            FUSIONPERF_DUMP_ALL_STATISTICS |
            FUSIONPERF_DUMP_ALL_SOURCEINFO,
            PerfInfo,
            NUMBER_OF(PerfInfo));
    }
    else if (wsType[0] == L'x')
    {
        HANDLE  hFile;
        HANDLE  hFileMapping;
        PVOID   pvFileData;
        SIZE_T  cbFileSize;

        FUSION_PERF_INFO PerfInfo[9];

        for (SIZE_T i = 0; i < cNumTries; i++)
        {
            PERFINFOTIME(&PerfInfo[0], hFile = ::CreateFileW(wsSourceName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL));
            PERFINFOTIME(&PerfInfo[1], cbFileSize = ::GetFileSize(hFile, 0));
            PERFINFOTIME(&PerfInfo[2], hFileMapping = ::CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL));
            PERFINFOTIME(&PerfInfo[3], pvFileData = ::MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0));

            PERFINFOTIME(&PerfInfo[4], { for (SIZE_T i2 = 0; i2 < cbFileSize; i2++)
                PERFINFOTIME(&PerfInfo[8], bBuffer[i2] = ((PBYTE)pvFileData)[i2]); });

            PERFINFOTIME(&PerfInfo[5], ::UnmapViewOfFile(pvFileData));
            PERFINFOTIME(&PerfInfo[6], ::CloseHandle(hFileMapping));
            PERFINFOTIME(&PerfInfo[7], ::CloseHandle(hFile));
        }

        FusionpReportPerfInfo(
            FUSIONPERF_DUMP_TO_STDOUT |
            FUSIONPERF_DUMP_ALL_STATISTICS |
            FUSIONPERF_DUMP_ALL_SOURCEINFO,
            PerfInfo,
            NUMBER_OF(PerfInfo));
    }
    else if (wsType[0] == L'r')
    {
        BYTE bTempBlock[8192];
        ULONG cbReadCount;
        FUSION_PERF_INFO PerfInfo[3];

        for (SIZE_T i = 0; i < cNumTries; i++)
        {
            CResourceStream RStream;

            QueryPerformanceCounter(&llStartCount);

            PERFINFOTIME(&PerfInfo[0], RStream.Initialize(wsSourceName, MAKEINTRESOURCEW(RT_MANIFEST)));
            PERFINFOTIME(&PerfInfo[1], RStream.Read(bTempBlock, sizeof(bTempBlock), &cbReadCount));

            PERFINFOTIME(&PerfInfo[2],
                for (SIZE_T i2 = 0; i2 < cbReadCount; i2++)
                {
                    bBuffer[i2] = bTempBlock[i2];
                }
                );

            wprintf(L"%s", bBuffer);

            QueryPerformanceCounter(&llEndCount);
            llTotalSizeSoFar.QuadPart += llEndCount.QuadPart - llStartCount.QuadPart;
        }

        for (int i = 0; i < NUMBER_OF(PerfInfo); i++)
        {
            FusionpDumpPerfInfo(FUSIONPERF_DUMP_TO_STDOUT, PerfInfo + i);
        }
    }
    else if (wsType[0] == L'f')
    {
        BYTE bTempBlock[8192];
        ULONG cbReadCount;

        for (SIZE_T i = 0; i < cNumTries; i++)
        {
            CFileStream RStream;

            QueryPerformanceCounter(&llStartCount);

            RStream.OpenForRead(
                wsSourceName,
                CImpersonationData(),
                FILE_SHARE_READ,
                OPEN_EXISTING,
                FILE_FLAG_SEQUENTIAL_SCAN);

            RStream.Read(bTempBlock, sizeof(bTempBlock), &cbReadCount);

            for (SIZE_T i2 = 0; i2 < cbReadCount; i2++)
            {
                bBuffer[i2] = bTempBlock[i2];
            }

            QueryPerformanceCounter(&llEndCount);
            llTotalSizeSoFar.QuadPart += llEndCount.QuadPart - llStartCount.QuadPart;
        }
    }

    QueryPerformanceFrequency(&llCountsPerSec);

    dCountsPerIteration = (double)llTotalSizeSoFar.QuadPart / cNumTries;
    dCountsPerSecond = (double)llCountsPerSec.QuadPart;
    dSeconds = (double)llTotalSizeSoFar.QuadPart / dCountsPerSecond;
    dSecondsPerIteration = dCountsPerIteration / dCountsPerSecond;

    iFinalIterationsPerSecond = static_cast<int>(1.0 / dSecondsPerIteration);

    fwprintf(
        stdout,
        L"Completed %d runs: %d attempts per second available\n",
        cNumTries,
        iFinalIterationsPerSecond);

    fSuccess = TRUE;

    return fSuccess;
}


BOOL
TestVerifyFileSignature(PCWSTR wsFilename)
{
    WINTRUST_FILE_INFO  fInfo;
    WINTRUST_DATA       wtData;
    GUID                guidTrustType = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    HRESULT             hResult = E_FAIL;
    PWSTR               pwszMessageText = NULL;

    ZeroMemory(&wtData, sizeof(wtData));
    ZeroMemory(&fInfo, sizeof(fInfo));

    fInfo.cbStruct = sizeof(fInfo);
    fInfo.pcwszFilePath = wsFilename;
    fInfo.hFile = NULL;

    wtData.cbStruct = sizeof(wtData);
    wtData.dwUIChoice = WTD_UI_ALL;
    wtData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
    wtData.dwUnionChoice = WTD_CHOICE_FILE;
    wtData.pFile = &fInfo;

    hResult = WinVerifyTrust(0, &guidTrustType, &wtData);

    if (FAILED(hResult))
    {
        ::FormatMessageW(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            hResult,
            0,
            (PWSTR)&pwszMessageText,
            500,
            NULL);

        fwprintf(stdout, L"Error: %ls (code 0x%08x)\n", pwszMessageText, hResult);
        LocalFree(pwszMessageText);
    }
    else
    {
        fwprintf(stdout, L"File signature(s) are valid.");
    }

    return TRUE;
}

__inline
ULONGLONG
GetCycleCount(void)
{
#if defined(_X86_)
    __asm {
        RDTSC
    }
#else
    return 0;
#endif // defined(_X86_)
}

BOOL TestMessagePerf(int argc, wchar_t **argv, int *piNext)
{
    ATOM a;
    WNDCLASSEXW wc;
    ULONGLONG cc1, cc2, cc3, cc4;
    ULONG i, t;
    HWND hwnd;
    MSG m;
    ULONG mcount;
    HANDLE hActCtx = NULL;
    ULONG_PTR ulCookie;

    wc.cbSize = sizeof(wc);
    wc.style = 0;
    wc.lpfnWndProc = &TestMessagePerfWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = NULL;
    wc.hIcon = NULL;
    wc.hCursor = NULL;
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"SxsMsgPerfTest";
    wc.hIconSm = NULL;

    printf("Beginning message perf test...\n");

    a = ::RegisterClassExW(&wc);
    if (a == NULL)
    {
        printf("RegisterClassExW() failed; ::GetLastError = %d\n", ::GetLastError());
        return FALSE;
    }

    if (argv[*piNext][0] != L'*')
    {
        ACTCTXW ac;

        ac.cbSize = sizeof(ac);
        ac.dwFlags = 0;
        ac.lpSource = argv[*piNext];
        hActCtx = ::CreateActCtxW(&ac);
        if (hActCtx == INVALID_HANDLE_VALUE)
        {
            printf("CreateActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());
            goto ErrorExit;
        }
    }

    if (!ActivateActCtx(hActCtx, &ulCookie))
    {
        printf("ActivateActCtx() failed; ::GetLastError() = %d\n", ::GetLastError());
        goto ErrorExit;
    }

    (*piNext)++;

    hwnd = ::CreateWindowW(
        (LPCWSTR) a,
        L"PerfTestWindow",
        WS_VISIBLE,
        0, // x
        0, // y
        100, // width
        100, // height
        NULL, // hwndParent
        NULL, // hMenu
        NULL, // hInstance
        NULL); // lpParam
    if (hwnd == NULL)
        return FALSE;

    t = _wtoi(argv[*piNext]);
    (*piNext)++;

    mcount = 0;
    cc3 = 0;
    cc4 = 0;

    for (i=0; i<t; i++)
    {
        if (!PostMessageW(hwnd, WM_USER, 0, 0))
        {
            printf("PostMessageW() failed; ::GetLastError() = %d\n", ::GetLastError());
            goto ErrorExit;
        }

        cc1 = GetCycleCount();

        while (::PeekMessage(&m, hwnd, 0, 0, PM_REMOVE))
        {
            mcount++;
            ::TranslateMessage(&m);
            ::DispatchMessage(&m);
        }

        cc2 = GetCycleCount();

        // accumulate the time spend just processing the message...
        cc3 = cc3 + (cc2 - cc1);
    }

    printf("%lu messages in %I64u cycles\n", mcount, cc3);
    printf("   avg cycles/msg: %I64u\n", static_cast<ULONGLONG>((cc3 / static_cast<ULONGLONG>(mcount))));

    return TRUE;

ErrorExit:
    return FALSE;
}

LRESULT CALLBACK TestMessagePerfWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void
TestTrickyMultipleAssemblyCacheItems(PCWSTR pvManifest)
{
    PFNCreateAssemblyCacheItem      pCreateAssemblyCacheItem;
    PFNCreateAssemblyCache          pCreateAssemblyCache;
    CSmartRef<IAssemblyCache>       AssemblyCache;
    CSmartRef<IAssemblyCacheItem>   AssemblyCacheItems[2];
    CStringBuffer                   sbManifestName;
    HRESULT                         hr;

    LoadSxs();
    GetSxsProc("CreateAssemblyCache", &pCreateAssemblyCache);
    GetSxsProc("CreateAssemblyCacheItem", &pCreateAssemblyCacheItem);

    sbManifestName.Win32Assign(pvManifest, wcslen(pvManifest));

    for (int i = 0; i < 2; i++)
    {
        CSmartRef<IStream> pStream;
        CSmartRef<IAssemblyCacheItem> AssemblyItemTemp;
        CStringBuffer TempStreamName;

        hr = pCreateAssemblyCacheItem(
            &AssemblyItemTemp,
            NULL,
            NULL,
            NULL,
            0,
            0);

        //
        // Manifest
        //
        hr = AssemblyItemTemp->CreateStream(0, pvManifest, STREAM_FORMAT_WIN32_MANIFEST, 0, &pStream, NULL);
        TempStreamName.Win32Assign(sbManifestName);
        hr = Helper_WriteStream(&TempStreamName, pStream);
        pStream.Release();

        //
        // Dll
        //
        sbManifestName.Win32ChangePathExtension(L".dll", 4, eAddIfNoExtension);
        hr = AssemblyItemTemp->CreateStream(0, static_cast<PCWSTR>(TempStreamName), STREAM_FORMAT_WIN32_MODULE, 0, &pStream, NULL);
        hr = Helper_WriteStream(&TempStreamName, pStream);
        pStream.Release();

        hr = AssemblyItemTemp->Commit(0, NULL);
        AssemblyCacheItems[i] = AssemblyItemTemp;
    }
    /*
    hr = pCreateAssemblyCache(&AssemblyCache, 0);
    hr = AssemblyCache->MarkAssembliesVisible((IUnknown**)AssemblyCacheItems, 2, 0);
    */
}


void
TestSfcScanKickoff()
{
    SXSP_DEBUG_FUNCTION pfn;
    BOOL fSuccess = FALSE;

    LoadSxs();
    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_SFC_SCANNER, 0, NULL, NULL);
}


void
GenerateStrongNameAndPublicKey(PCWSTR wsCertificate)
{
    BOOL                    bSuccess = FALSE;

    CStringBuffer           sbStrings[3];
    PCCERT_CONTEXT          pContext = NULL;
    SXSP_DEBUG_FUNCTION     pfn;
    HCERTSTORE              hCertStore = NULL;

    hCertStore = CertOpenStore(
        CERT_STORE_PROV_FILENAME,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        NULL,
        CERT_STORE_OPEN_EXISTING_FLAG,
        (void*)wsCertificate);

    LoadSxs();
    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    while (pContext = CertEnumCertificatesInStore(hCertStore, pContext))
    {
        bSuccess = (*pfn)(SXS_DEBUG_GET_STRONGNAME, 0, (PCWSTR)pContext, (PVOID)sbStrings);

        wprintf(L"Signer display name: %ls\n\tPublic Key: %ls\n\tPublic Key Token: %ls\n",
            static_cast<PCWSTR>(sbStrings[0]),
            static_cast<PCWSTR>(sbStrings[1]),
            static_cast<PCWSTR>(sbStrings[2]));

    }

    bSuccess = TRUE;

    if (pContext)     CertFreeCertificateContext(pContext);
    if (hCertStore)   CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
}

BOOL CALLBACK
DumpResourceWorker(
    HMODULE hModule,
    PCWSTR lpszType,
    PWSTR lpszName,
    LONG_PTR lParam
    )
{
    HGLOBAL hGlobal;
    HRSRC hResource;
    PVOID pvData;
    SIZE_T cbResource;

    hResource = ::FindResourceW(hModule, lpszName, lpszType);
    hGlobal = ::LoadResource(hModule, hResource);
    pvData = ::LockResource(hGlobal);

    if (lpszName < (PCWSTR)0xFFFF)
    {
        wprintf(L"----\nResource id: %p\n", lpszName);
    }
    else
    {
        wprintf(L"----\nResource name: %ls\n", lpszName);
    }

    cbResource = ::SizeofResource(hModule, hResource);

    for (SIZE_T i = 0; i < cbResource; i++)
    {
        wprintf(L"%c", ((char*)pvData)[i]);
    }
    wprintf(L"\n");


    return TRUE;
}

BOOL
TestDumpContainedManifests(PCWSTR wsFilename)
{
    HMODULE hThing;

    hThing = ::LoadLibraryExW(wsFilename, NULL, LOAD_LIBRARY_AS_DATAFILE);

    if ((hThing == NULL) || (hThing == INVALID_HANDLE_VALUE))
    {
        wprintf(L"Bad mojo: can't open %ls for enumeration.\n", wsFilename);
        return FALSE;
    }

    if (!::EnumResourceNamesW(hThing, MAKEINTRESOURCEW(RT_MANIFEST), DumpResourceWorker, NULL))
    {
        if (GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)
        {
            wprintf(L"No manifests found in %ls\n", wsFilename);
        }
        else
        {
            wprintf(L"Bad mojo: can't enumerate resources from %ls, error=0x%08x\n",
                wsFilename,
                ::GetLastError());
        }
    }
    ::FreeLibrary(hThing);

    return TRUE;
}

BOOL TestGenerateStringWithIdenticalHash(WCHAR iString[33])
{
#define START_VALUE         1
#define STR_LEN             32
#define HASH_SEED           65599
#define MAGIC_ARRAY         {1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1,-1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1} ;

#define UPPER(ch)     if (ch >=L'a' && ch <= L'z') ch = ch- L'a' + L'A';

    WCHAR oString[STR_LEN + 1];
    DWORD i, nLen, a[STR_LEN];
    ULONG hash1, hash2;
    WCHAR ch;

    a[0] = a[1] = START_VALUE;
    nLen = 2;
    while(nLen < STR_LEN) {
            for(i = nLen; i < nLen*2; i++)
                    a[i] = static_cast<ULONG>(-static_cast<LONG>(a[i-nLen]));
            nLen *= 2;
    }

    oString[32] = iString[32] = L'\0';

    // generate the new string
    for (i = 0; i< 32; i++)
        oString[i] = static_cast<WCHAR>(iString[i] + a[i]);

    // verify that these two string have the same hash
    hash1 = 0 ;
    for (i = 0 ; i<STR_LEN; i++) {
        ch = iString[i];
        UPPER(ch);
        hash1 = hash1*HASH_SEED + ch;
    }

    hash2 = 0 ;
    for (i = 0 ; i<STR_LEN; i++){
        ch = oString[i];
        UPPER(ch);
        hash2 = hash2*HASH_SEED + ch;
    }

    return (hash1 == hash2)? TRUE : FALSE;

#undef START_VALUE
#undef STR_LEN
#undef HASH_SEED
}

BOOL TestAssemblyIdentityHash()
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_ASSEMBLY_IDENTITY_HASH, 0, 0, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

const static WCHAR Beta2Manifest[] =
L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
L"    <assemblyIdentity type=\"win32\" name=\"cards\" version=\"2.0.0.0\" processorArchitecture=\"X86\" language=\"en-us\" />"
L"    <file name=\"cards.dll\"/>"
L"</assembly>;"
;

// check whether the handle counter of csrss has changed dramatically after running this code.....
VOID TestCreateActctxLeakHandles(DWORD dwCallCounter)
{
    int result = -1;
    BOOL fSuccess;    

    for (DWORD i =0 ; i < dwCallCounter ; i ++)
    {
        HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(InheritManifest);
        if (ActivationContextHandle  == INVALID_HANDLE_VALUE) {
            result = i ;
            break;
        }

        ULONG_PTR Cookie;
        ActivateActCtx(ActivationContextHandle, &Cookie);

        HINSTANCE hInstanceKernel32 = ::GetModuleHandleW(L"KERNEL32.DLL");
        PQUERYACTCTXW_FUNC pQueryActCtxW = (PQUERYACTCTXW_FUNC) ::GetProcAddress(hInstanceKernel32, "QueryActCtxW");

        SIZE_T CbWritten;

        if (pQueryActCtxW != NULL)
        {
            for ( ULONG ulInfoClass = ActivationContextBasicInformation; ulInfoClass <= FileInformationInAssemblyOfAssemblyInActivationContext; ulInfoClass ++)
            {
                switch (ulInfoClass) {
                case ActivationContextBasicInformation :
                    break;

                case ActivationContextDetailedInformation :
                    {
                    struct
                    {
                        ACTIVATION_CONTEXT_DETAILED_INFORMATION acdi;
                        WCHAR Buffer[_MAX_PATH * 3]; // manifest, policy and appdir
                    } Data;
                    ULONG index = 0 ; 
                    fSuccess = (*pQueryActCtxW)(0, ActivationContextHandle, &index, ulInfoClass, &Data, sizeof(Data), &CbWritten);
                    fprintf(stderr, "\ncall QueryActCtxW with ActivationContextDetailedInformation\n" );
                    if (fSuccess)
                    {
                        fprintf(stderr, "1st string: %ls\n", Data.acdi.lpAppDirPath);
                        fprintf(stderr, "2nd string: %ls\n", Data.acdi.lpRootManifestPath);
                    }
                    else
                        fprintf(stderr, "QueryActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());

                    }

                    break;

                case AssemblyDetailedInformationInActivationContext: 
                    {
                    struct
                    {
                        ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION acdi;
                        WCHAR Buffer[_MAX_PATH * 4]; 
                    } Data;
                    ULONG AssemblyIndex = 1; 
                    fSuccess = (*pQueryActCtxW)(0, ActivationContextHandle, &AssemblyIndex, ulInfoClass, &Data, sizeof(Data), &CbWritten);

                    fprintf(stderr, "\ncall QueryActCtxW with AssemblyDetailedInformationInActivationContext\n" );
                    if (fSuccess)
                    {
                        fprintf(stderr, "Encoded assembly Identity: %ls\n", Data.acdi.lpAssemblyEncodedAssemblyIdentity);
                        fprintf(stderr, "manifest path: %ls\n", Data.acdi.lpAssemblyManifestPath);
                        fprintf(stderr, "policy path: %ls\n", Data.acdi.lpAssemblyPolicyPath);
                        fprintf(stderr, "Assembly Directory: %ls\n", Data.acdi.lpAssemblyDirectoryName);
                    }
                    else
                        fprintf(stderr, "QueryActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());
                    }
                    break;

                case FileInformationInAssemblyOfAssemblyInActivationContext:
                    {
                    struct
                    {
                        ASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION acdi;
                        WCHAR Buffer[_MAX_PATH * 2]; 
                    } Data;
                    ACTIVATION_CONTEXT_QUERY_INDEX index;
                    index.ulAssemblyIndex = 1; 
                    index.ulFileIndexInAssembly = 0; 

                    fSuccess = (*pQueryActCtxW)(0, ActivationContextHandle, &index, ulInfoClass, &Data, sizeof(Data), &CbWritten);

                    fprintf(stderr, "\ncall QueryActCtxW with FileInformationInAssemblyOfAssemblyInActivationContext\n" );
                    if (fSuccess)
                    {
                        fprintf(stderr, "file name: %ls\n", Data.acdi.lpFileName);
                        fprintf(stderr, "file path: %ls\n", Data.acdi.lpFilePath);
                    }
                    else
                        fprintf(stderr, "QueryActCtxW() failed; ::GetLastError() = %d\n", ::GetLastError());
                    }
                } // end of switch

            }// end of for
        }

        DeactivateActCtx(0, Cookie);
        // CloseHandle(ActivationContextHandle);
    }
    return;
}

BOOL TestSystemDefaultActivationContextGeneration()
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_SYSTEM_DEFAULT_ACTCTX_GENERATION, 0, NULL, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}

CHAR g_AsyncIOBuffer[16384];

struct AsyncIOBlock
{
    OVERLAPPED ol_input;
    OVERLAPPED ol_output;
    HANDLE hInputFile;
    HANDLE hOutputFile;
} g_AsyncIOBlock;

bool g_AsyncIODone = false;
DWORD g_AsyncIOError = ERROR_SUCCESS;

VOID CALLBACK AsyncReadCompleted(
    DWORD dwErrorCode,
    DWORD dwBytesTransferred,
    LPOVERLAPPED ol
    );

VOID CALLBACK AsyncWriteCompleted(
    DWORD dwErrorCode,
    DWORD dwBytesTransferred,
    LPOVERLAPPED ol
    );

VOID CALLBACK AsyncReadCompleted(
    DWORD dwErrorCode,
    DWORD dwBytesTransferred,
    LPOVERLAPPED ol
    )
{
    if (dwErrorCode != ERROR_SUCCESS)
    {
        fprintf(stderr, "Error passed to AsyncReadCompleted(); error code = %ul; bytes transferred = %ul\n", dwErrorCode, dwBytesTransferred);
        g_AsyncIOError = dwErrorCode;
        g_AsyncIODone = true;
    }
    else
    {
        g_AsyncIOBlock.ol_input.Offset += dwBytesTransferred;
        if (!::WriteFileEx(g_AsyncIOBlock.hOutputFile, g_AsyncIOBuffer, dwBytesTransferred, &g_AsyncIOBlock.ol_output, &AsyncWriteCompleted))
        {
            g_AsyncIOError = ::GetLastError();
            fprintf(stderr, "WriteFileEx() failed; ::GetLastError() = %d\n", g_AsyncIOError);
            g_AsyncIODone = true;
        }
    }
}

VOID CALLBACK AsyncWriteCompleted(
    DWORD dwErrorCode,
    DWORD dwBytesTransferred,
    LPOVERLAPPED ol
    )
{
    if (dwErrorCode != ERROR_SUCCESS)
    {
        fprintf(stderr, "Error passed to AsyncWriteCompleted(); error code = %ul; dwBytesTransferred = %ul\n", dwErrorCode, dwBytesTransferred);
        g_AsyncIOError = dwErrorCode;
        g_AsyncIODone = true;
    }
    else
    {
        g_AsyncIOBlock.ol_output.Offset += dwBytesTransferred;
        if (!::ReadFileEx(g_AsyncIOBlock.hInputFile, g_AsyncIOBuffer, sizeof(g_AsyncIOBuffer), &g_AsyncIOBlock.ol_input, &AsyncReadCompleted))
        {
            g_AsyncIOError = ::GetLastError();
            if (g_AsyncIOError != ERROR_HANDLE_EOF)
                fprintf(stderr, "ReadFileEx() failed; ::GetLastError() = %d\n", g_AsyncIOError);
            else
                g_AsyncIOError = ERROR_SUCCESS;
            g_AsyncIODone = true;
        }
    }
}

BOOL
TestAsyncIO(int argc, wchar_t **argv, int *piNext)
{
    HANDLE hFileIn;
    HANDLE hFileOut;
    HANDLE hActCtx = NULL;
    ULONG_PTR cookie = 0;
    PCWSTR pszManifest;
    PCWSTR pszInputFile;
    PCWSTR pszOutputFile;
    ACTCTXW acw;

    pszManifest = argv[(*piNext)++];
    pszInputFile = argv[(*piNext)++];
    pszOutputFile = argv[(*piNext)++];

    if (wcscmp(pszManifest, L"-") != 0)
    {
        acw.cbSize = sizeof(acw);
        acw.dwFlags = 0;
        acw.lpSource = pszManifest;

        hActCtx = ::CreateActCtxW(&acw);
        if (hActCtx == INVALID_HANDLE_VALUE)
        {
            fprintf(stderr, "CreateActCtxW() on %ls failed; ::GetLastError() = %d\n", pszManifest, ::GetLastError());
            return EXIT_FAILURE;
        }
    }

    hFileIn = ::CreateFileW(pszInputFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hFileIn == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "CreateFileW() on %ls failed; ::GetLastError() = %d\n", pszInputFile, ::GetLastError());
        return EXIT_FAILURE;
    }

    hFileOut = ::CreateFileW(pszOutputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
    if (hFileOut == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "CreateFileW() on %ls failed; ::GetLastError() = %d\n", pszOutputFile, ::GetLastError());
        return EXIT_FAILURE;
    }

    ActivateActCtx(hActCtx, &cookie);

    g_AsyncIOBlock.ol_input.Offset = 0;
    g_AsyncIOBlock.ol_input.OffsetHigh = 0;
    g_AsyncIOBlock.ol_output.Offset = 0;
    g_AsyncIOBlock.ol_output.OffsetHigh = 0;
    g_AsyncIOBlock.hInputFile = hFileIn;
    g_AsyncIOBlock.hOutputFile = hFileOut;

    if (!::ReadFileEx(hFileIn, &g_AsyncIOBuffer, sizeof(g_AsyncIOBuffer), &g_AsyncIOBlock.ol_input, &AsyncReadCompleted))
    {
        fprintf(stderr, "First ReadFileEx() failed; ::GetLastError() = %d\n", ::GetLastError());
        return EXIT_FAILURE;
    }

    while (!g_AsyncIODone)
    {
        ::SleepEx(1000, TRUE);
    }

    if (g_AsyncIOError != ERROR_SUCCESS)
    {
        fprintf(stderr, "Async IO test failed; win32 error code = %d\n", g_AsyncIOError);
        return EXIT_FAILURE;
    }

    CloseHandle(hFileIn);
    CloseHandle(hFileOut);

    return EXIT_SUCCESS;
}

static
DWORD
WINAPI
TestThreadInheritLeakThreadProc(
    LPVOID lpParameter
    )
{
    // Don't need to do anything...
    return 1;
}

int
TestThreadInheritLeak()
{
    HANDLE ActivationContextHandle = ::CreateActivationContextFromStringW(InheritManifest);
    DWORD dwThreadId;
    ULONG_PTR ulpCookie;
    HANDLE hThread = NULL;

    ::ActivateActCtx(ActivationContextHandle, &ulpCookie);
    hThread = ::CreateThread(NULL, 0, &TestThreadInheritLeakThreadProc, NULL, 0, &dwThreadId);

    if (hThread == NULL)
    {
        fprintf(stderr, "CreateThread() failed with ::GetLastError = %d\n", ::GetLastError());
        return EXIT_FAILURE;
    }

    WaitForSingleObject(hThread, INFINITE);

    fprintf(stderr, "Created thread %lu\n", dwThreadId);
    ::DeactivateActCtx(0, ulpCookie);
    ::ReleaseActCtx(ActivationContextHandle);

    return EXIT_SUCCESS;
}

BOOL
TestNewCatalogSignerThingy(
    PCWSTR CatalogName
    )
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;

    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_CATALOG_SIGNER_CHECK, 0, CatalogName, NULL);

    return fSuccess;
}

HMODULE MyHandle;
WCHAR MyModuleFullPath[MAX_PATH];
WCHAR MyModuleFullPathWithoutExtension[MAX_PATH];

/*
see \\scratch\scratch\JayK\exedll for a slightly different approach
*/
HANDLE GetExeHandle()
{
    return GetModuleHandle(NULL);
}

extern "C" { extern IMAGE_DOS_HEADER __ImageBase; }

HMODULE GetMyHandle()
{
    //Trace("GetMyHandle():%p\n", &__ImageBase);
    return (HMODULE)&__ImageBase;
}

BOOL AmITheExe()
{
    IamExe = (GetExeHandle() == GetMyHandle());
    IamDll = !IamExe;
    return IamExe;
}

PCWSTR GetMyModuleFullPath()
{
    return
        ((MyModuleFullPath[0]
        || ::GetModuleFileNameW(GetMyHandle(), MyModuleFullPath, RTL_NUMBER_OF(MyModuleFullPath))),
        MyModuleFullPath);
}

PCWSTR GetMyModuleFullPathWithoutExtension()
{
    //
    // not thread safe
    //
    wcscpy(MyModuleFullPathWithoutExtension, GetMyModuleFullPath());
    *wcsrchr(MyModuleFullPathWithoutExtension, '.') = 0;
    return MyModuleFullPathWithoutExtension;
}

void TestExeDll()
{
#if defined(_X86_)
    WCHAR x[MAX_PATH];
    CStringBuffer y;

    Kernel32.GetModuleFileNameW(NULL, x, RTL_NUMBER_OF(x));
    if (!y.Win32Assign(x, StringLength(x)))
        ThrowLastError();

    if (!y.Win32ChangePathExtension(L"dll", 3, eAddIfNoExtension))
        ThrowLastError();

    if (!CopyFileW(x, y, FALSE))
        ThrowLastError();

    SetDllBitInPeImage(y);

    FreeLibrary(LoadLibraryW(y));
#endif
}

void PrintDll()
{
    //Trace("dll %ls\n", GetMyModuleFullPath());
}

void PrintExe()
{
    //Trace("exe %ls\n", GetMyModuleFullPath());
}

extern "C" void __cdecl wmainCRTStartup();
extern "C" BOOL __stdcall _DllMainCRTStartup(HINSTANCE, DWORD, PVOID);

extern "C" BOOL __stdcall DllMain(HINSTANCE DllHandle, DWORD Reason, PVOID SemiReserved)
{
    //Trace("Enter DllMain\n");
    switch (Reason)
    {
    default:
        break;
    case DLL_PROCESS_ATTACH:
        if (
               wcsstr(GetCommandLineW(), L" -tQueryActCtx")
            || wcsstr(GetCommandLineW(), L" -tqueryactctx")
            || wcsstr(GetCommandLineW(), L" /tQueryActCtx")
            || wcsstr(GetCommandLineW(), L" /tqueryactctx")
            )
        {
            TestQueryActCtx();
        }
    }
    //Trace("Exit DllMain\n");
    return TRUE;
}

#if defined(_X86_)

extern "C" void __declspec(naked) Entry()
// This works for .dlls or .exes.
// void ExeMain(void)
// BOOL __stdcall DllMain(HINSTANCE dll, DWORD reason, void* reserved)
{
    static const char* DllReason[] =
    {
        "DllProcessDetach %ls\n",
        "DllProcessAttach %ls\n",
        "DllThreadAttach  %ls\n",
        "DllThreadDetach  %ls\n"
    };
    __asm {
    //int 3
    call AmITheExe
    test eax,eax
    jne  Lexe
//Ldll:
    call GetMyModuleFullPath
    push eax
    mov eax, [esp+12]
    mov eax, DllReason[eax*4]
    push eax
    call Trace
    add esp,8
    jmp _DllMainCRTStartup
Lexe:
    call PrintExe
    jmp  wmainCRTStartup
}
}

#else

extern "C" void Entry()
{
    wmainCRTStartup();
}

#endif


BOOL TestSxsSfcUI()
{
    SXSP_DEBUG_FUNCTION pfn = NULL;
    BOOL fSuccess = FALSE;
    LoadSxs();

    GetSxsProc(SXSP_DEBUG_ORDINAL, &pfn);

    fSuccess = (*pfn)(SXS_DEBUG_SFC_UI_TEST, 0, 0, NULL);

    if (! fSuccess){
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;

    return fSuccess;
}

void TestGetModuleHandleEx()
{
    HMODULE p;
    BOOL  f;
    HMODULE q;
    unsigned u;
#define GetModuleHandleExA pfnGetModuleHandleExA
#define GetModuleHandleExW pfnGetModuleHandleExW
    HMODULE kernel32 = GetModuleHandleW(L"Kernel32");
    PGET_MODULE_HANDLE_EXA GetModuleHandleExA = reinterpret_cast<PGET_MODULE_HANDLE_EXA>(GetProcAddress(kernel32, "GetModuleHandleExA"));
    PGET_MODULE_HANDLE_EXW GetModuleHandleExW = reinterpret_cast<PGET_MODULE_HANDLE_EXW>(GetProcAddress(kernel32, "GetModuleHandleExW"));

    if (GetModuleHandleExA == NULL || GetModuleHandleExW == NULL)
    {
        return;
    }

    union
    {
         CHAR BufferA[MAX_PATH];
        WCHAR BufferW[MAX_PATH];
    };
    BufferA[0] = 0;
    GetWindowsDirectoryA(BufferA, MAX_PATH);
    std::string windowsA = BufferA;
    std::string systemA = windowsA + "System32";

    BufferW[0] = 0;
    GetWindowsDirectoryW(BufferW, MAX_PATH);
    std::wstring windowsW = BufferW;
    std::wstring systemW = windowsW + L"System32";

#define PIN GET_MODULE_HANDLE_EX_FLAG_PIN
#define NOCHANGE GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
#define ADDRESS GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS

#define X(x) SetLastError(NO_ERROR); p = x; printf("%s:%p,%d\n", #x, p, ::GetLastError());
#define Y(x) SetLastError(NO_ERROR); f = x; printf("%s:%s,%p,%d\n", #x, "false\0true"+f*6, p, ::GetLastError());
    printf("first some basic GetModuleHandle testing\n\n");
    X(GetModuleHandleA(NULL));
    X(GetModuleHandleW(NULL));
    X(GetModuleHandleA("ntdll.dll"));
    X(GetModuleHandleW(L"ntdll.dll"));
    X(GetModuleHandleA("ntdll"));
    X(GetModuleHandleW(L"ntdll"));
    X(GetModuleHandleA("c:\\ntdll"));
    X(GetModuleHandleW(L"c:\\ntdll"));
    X(GetModuleHandleA((systemA + "\\ntdll").c_str()));
    X(GetModuleHandleW((systemW + L"\\ntdll").c_str()));
    X(GetModuleHandleA((systemA + "\\ntdll.dll").c_str()));
    X(GetModuleHandleW((systemW + L"\\ntdll.dll").c_str()));
    X(GetModuleHandleA("sxs"));
    X(GetModuleHandleW(L"sxs.dll"));

    printf("\n\nnow show that FreeLibrary \"works\", GetModuleHandle honors it\n\n");
    X(LoadLibraryA("Sxs.dll"));
    X(GetModuleHandleA("sxs"));
    X(GetModuleHandleW(L"sxs.dll"));
    Y(FreeLibrary(p = GetModuleHandleA("Sxs.dll")));
    X(GetModuleHandleA("sxs"));
    X(GetModuleHandleW(L"sxs.dll"));
    X(LoadLibraryW(L"Sxs.dll"));
    Y(GetModuleHandleExA(0, NULL, &p));
    Y(GetModuleHandleExW(0, NULL, &p));

    printf("\n\nsome invalid parameters (%d)\n\n", ERROR_INVALID_PARAMETER);

    Y(GetModuleHandleExA(PIN | NOCHANGE, "sxs", &p));
    Y(GetModuleHandleExW(PIN | NOCHANGE, L"sxs", &p));
    Y(GetModuleHandleExA(PIN | NOCHANGE, "ntdll", &p));
    Y(GetModuleHandleExW(PIN | NOCHANGE, L"ntdll", &p));

    printf("\n\nshow NOCHANGE's equiv to regular\n\n");

    Y(GetModuleHandleExA(NOCHANGE, "sxs", &p));
    Y(GetModuleHandleExW(NOCHANGE, L"sxs", &p));
    Y(FreeLibrary(p = GetModuleHandleA("Sxs.dll")));
    Y(GetModuleHandleExA(NOCHANGE, "sxs", &p));
    Y(GetModuleHandleExW(NOCHANGE, L"sxs", &p));

    printf("\n\nshow PIN works\n\n");

    X(LoadLibraryW(L"Sxs.dll"));
    Y(GetModuleHandleExA(PIN, "sxs", &p));
    Y(FreeLibrary(p = GetModuleHandleA("Sxs.dll")));
    Y(GetModuleHandleExW(0, L"sxs", &(q = p)));
    Y(GetModuleHandleExW(0, L"c:\\sxs", &p));

    printf("\n\nshow the VirtualQuery form\n\n");

    Y(GetModuleHandleExA(ADDRESS, "sxs", &p)); // string, actually in .exe
    Y(GetModuleHandleExW(ADDRESS, L"c:\\sxs", &p));

    Y(GetModuleHandleExA(ADDRESS, reinterpret_cast<LPCSTR>(q), &p));
    Y(GetModuleHandleExW(ADDRESS, reinterpret_cast<LPCWSTR>(q), &p));

    printf("\n\nsome more invalid parameters (%d)\n\n", ERROR_INVALID_PARAMETER);

    Y(GetModuleHandleExA(0, NULL, NULL));
    Y(GetModuleHandleExW(0, NULL, NULL));

    printf("\n\nshow that static loads can't be unloaded\n\n");
    for (u = 0 ; u < 4 ; ++u)
    {
        printf("%#x\n", u);
        Y(FreeLibrary(p = GetModuleHandleA("kernel32")));
        Y(FreeLibrary(p = GetModuleHandleW(L"sxstest.exe")));
        Y(FreeLibrary(p = GetModuleHandleA("msvcrt.dll")));
    }

    printf("\n\ntry all flag combinations, including invalids ones\n\n");
    for (u = 0 ; u <= (PIN | ADDRESS | NOCHANGE) ; ++u)
    {
        printf("%#x\n", u);
        p = NULL;
        Y(  GetModuleHandleExA(u, NULL, NULL)); p = NULL;
        Y(  GetModuleHandleExW(u, NULL, NULL)); p = NULL;
        Y(  GetModuleHandleExA(u, NULL, &p)); p = NULL;
        Y(  GetModuleHandleExW(u, NULL, &p)); p = NULL;
        if (u & ADDRESS)
        {
            Y(  GetModuleHandleExA(u, "", NULL)); p = NULL;
            Y(  GetModuleHandleExW(u, reinterpret_cast<LPCWSTR>(GetModuleHandleExA), NULL)); p = NULL;
            Y(  GetModuleHandleExW(u, reinterpret_cast<LPCWSTR>(atexit), NULL)); p = NULL;

            Y(  GetModuleHandleExA(u, "", &p)); p = NULL;
            Y(  GetModuleHandleExW(u, reinterpret_cast<LPCWSTR>(GetModuleHandleExA), &p)); p = NULL;
            Y(  GetModuleHandleExW(u, reinterpret_cast<LPCWSTR>(atexit), &p)); p = NULL;
        }
        else
        {
            Y( GetModuleHandleExA(u, "foo32", NULL)); p = NULL;
            Y( GetModuleHandleExW(u, L"kernel32", NULL)); p = NULL;

            Y( GetModuleHandleExA(u, "foo32", &p)); p = NULL;
            Y( GetModuleHandleExW(u, L"kernel32", &p)); p = NULL;
        }
    }
    printf("\n\ntry all bits of flags, should be mostly invalid (%d)\n\n", ERROR_INVALID_PARAMETER);
    for (u = 0 ; u < RTL_BITS_OF(u) ; ++u)
    {
        printf("%#x\n", u);
        Y(GetModuleHandleExW(1<<u, L"kernel32", &p));
    }

    printf("\n\nPIN | ADDRESS wasn't covered\n\n", ERROR_INVALID_PARAMETER);

    X(GetModuleHandleW(L"shell32"));
    X(q = LoadLibraryA("shell32"));
    Y(FreeLibrary(GetModuleHandleA("shell32")));
    Y(GetModuleHandleExW(PIN | ADDRESS, reinterpret_cast<LPCWSTR>(q), &p));

    X(q = LoadLibraryW(L"shell32"));
    Y(GetModuleHandleExW(PIN | ADDRESS, reinterpret_cast<LPCWSTR>(q) + 100, &p));
    Y(FreeLibrary(q)); Y(FreeLibrary(q)); Y(FreeLibrary(q));
    X(GetModuleHandleW(L"shell32.dll"));
    Y(GetModuleHandleExW(ADDRESS, reinterpret_cast<LPCWSTR>(q) + 1000, &p));

#undef X
#undef Y
#undef PIN
#undef NOCHANGE
#undef ADDRESS
}

void
TestGetFullPathName(
    PCWSTR s
    )
{
    WCHAR FullPath[MAX_PATH * 2];
    PWSTR FilePart = L"";
    DWORD dw;
    DWORD dwError;

    SetLastError(NO_ERROR);
    dw = GetFullPathNameW(s, RTL_NUMBER_OF(FullPath), FullPath, &FilePart);
    dwError = ::GetLastError();
    printf(
        "GetFullPathNameW(%ls):%lu,lastError=%lu,filePart=%ls\n",
        FullPath,
        dw,
        dwError,
        FilePart
        );
}

void
TestCreateFile(
    PCWSTR s
    )
{
    HANDLE handle;
    DWORD dwError;

    SetLastError(NO_ERROR);
    handle = CreateFileW(
        s,
        0,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );
    dwError = ::GetLastError();
    if (handle != INVALID_HANDLE_VALUE)
        CloseHandle(handle);
    printf(
        "CreateFileW(%ls):%p,lastError=%lu\n",
        s,
        handle,
        dwError
        );
}

/*
from base\ntsetup\dll\sxs.c
*/
VOID
SxspGetPathBaseName(
    LPCWSTR Path,
    LPWSTR  Base
    )
{
    LPCWSTR Dot = wcsrchr(Path, '.');
    LPCWSTR Slash = wcsrchr(Path, '\\');
    //
    // beware \foo.txt\bar
    // beware \bar
    // beware bar
    // beware .bar
    // beware \.bar
    //
    *Base = 0;
    if (Slash == NULL)
        Slash = Path;
    else
        Slash += 1;
    if (Dot == NULL || Dot < Slash)
        Dot = Path + StringLength(Path);
    CopyMemory(Base, Slash, (Dot - Slash) * sizeof(*Base));
    Base[Dot - Slash] = 0;
}

VOID
TestGetPathBaseName(
    LPCWSTR Path
    )
{
    WCHAR Base[MAX_PATH];

    SxspGetPathBaseName(Path, Base);
    printf("\"%ls\" -> \"%ls\"\n", Path, Base);

}

void TestVersion()
{
	printf(
        "Windows NT %lu.%lu.%lu\n",
        FusionpGetWindowsNtMajorVersion(),
        FusionpGetWindowsNtMinorVersion(),
        FusionpGetWindowsNtBuildVersion()
        );
}

void TestGetProcessImageFileName()
{
    UNICODE_STRING s;

    FusionpGetProcessImageFileName(&s);
    printf("%wZ\n", &s);
}

BOOL TestErrorInfra1()  { FN_PROLOG_WIN32; IFW32FALSE_ORIGINATE_AND_EXIT(LoadLibraryA("1"));       FN_EPILOG; }
BOOL TestErrorInfra2()  { FN_PROLOG_WIN32; IFW32NULL_EXIT(LoadLibraryA("  2  "));                  FN_EPILOG; }
BOOL TestErrorInfra3()  { FN_PROLOG_WIN32; IFFAILED_CONVERTHR_HRTOWIN32_EXIT_TRACE(E_FAIL | 3);   FN_EPILOG; }
BOOL TestErrorInfra4()  { FN_PROLOG_WIN32; IFCOMFAILED_EXIT(E_FAIL | 4);                          FN_EPILOG; }
BOOL TestErrorInfra5()  { FN_PROLOG_WIN32; IFCOMFAILED_ORIGINATE_AND_EXIT(E_FAIL | 5);            FN_EPILOG; }
BOOL TestErrorInfra6()  { FN_PROLOG_WIN32; IFREGFAILED_EXIT(6);                                   FN_EPILOG; }
BOOL TestErrorInfra7()  { FN_PROLOG_WIN32; IFREGFAILED_ORIGINATE_AND_EXIT(7);                     FN_EPILOG; }

HRESULT TestErrorInfra8()   { FN_PROLOG_HR; IFW32FALSE_ORIGINATE_AND_EXIT(LoadLibraryA("8"));       FN_EPILOG; }
HRESULT TestErrorInfra9()   { FN_PROLOG_HR; IFW32NULL_EXIT(LoadLibraryA("!@#   9  \\"));            FN_EPILOG; }
HRESULT TestErrorInfra10()  { FN_PROLOG_HR; IFFAILED_CONVERTHR_HRTOWIN32_EXIT_TRACE(E_FAIL | 10);  FN_EPILOG; }
HRESULT TestErrorInfra11()  { FN_PROLOG_HR; IFCOMFAILED_EXIT(E_FAIL | 11);                         FN_EPILOG; }
HRESULT TestErrorInfra12()  { FN_PROLOG_HR; IFCOMFAILED_ORIGINATE_AND_EXIT(E_FAIL | 12);           FN_EPILOG; }
HRESULT TestErrorInfra13()  { FN_PROLOG_HR; IFREGFAILED_EXIT(13);                                  FN_EPILOG; }
HRESULT TestErrorInfra14()  { FN_PROLOG_HR; IFREGFAILED_ORIGINATE_AND_EXIT(14);                    FN_EPILOG; }

void TestErrorInfra()
{
#define X(x) DbgPrint("%s\n", #x); x()
    X(TestErrorInfra1);
    X(TestErrorInfra2);
    X(TestErrorInfra3);
    X(TestErrorInfra4);
    X(TestErrorInfra5);
    X(TestErrorInfra7);
    X(TestErrorInfra8);
    X(TestErrorInfra9);
    X(TestErrorInfra10);
    X(TestErrorInfra11);
    X(TestErrorInfra12);
    X(TestErrorInfra13);
    X(TestErrorInfra14);
#undef X
}

void TestQueryActCtx3(
    ULONG Flags,
    HANDLE ActCtxHandle
    )
{
    SIZE_T                                          BytesWrittenOrRequired = 0;
    BYTE                                            QueryBuffer[4][4096];
    ACTIVATION_CONTEXT_QUERY_INDEX                  QueryIndex = { 0 };
    PACTIVATION_CONTEXT_BASIC_INFORMATION           BasicInfo = reinterpret_cast<PACTIVATION_CONTEXT_BASIC_INFORMATION>(&QueryBuffer[0]);
    PASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION  DllRedir = reinterpret_cast<PASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION>(&QueryBuffer[1]);
    PACTIVATION_CONTEXT_DETAILED_INFORMATION        ContextDetailed = reinterpret_cast<PACTIVATION_CONTEXT_DETAILED_INFORMATION>(&QueryBuffer[2]);
    PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION AssemblyDetailed = reinterpret_cast<PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION>(&QueryBuffer[3]);
    CStringBuffer decimalContext;

    RtlZeroMemory(&QueryBuffer, sizeof(QueryBuffer));

    if (IS_SPECIAL_ACTCTX(ActCtxHandle))
        decimalContext.Win32Format(L" (%Id)", reinterpret_cast<LONG_PTR>(ActCtxHandle));

#define QueryFailed(x,n) Trace("%s:%ld:QueryActCtx(%s) failed %lu\n", __FUNCTION__, ULONG(n), x, GetLastError())
    if (!QueryActCtxW(
        Flags,
        ActCtxHandle,
        NULL,
        ActivationContextBasicInformation,
        &QueryBuffer,
        sizeof(QueryBuffer),
        &BytesWrittenOrRequired
        ))
        QueryFailed("ActivationContextBasicInformation", __LINE__ - 1);
    else
    {
        if (IS_SPECIAL_ACTCTX(BasicInfo->hActCtx))
            decimalContext.Win32Format(L" (%Id)", reinterpret_cast<LONG_PTR>(BasicInfo->hActCtx));
        Trace(
            "BytesWrittenOrRequired   : 0x%lx\n"
            "BasicInfo->hActCtx       : %p%ls\n"
            "BasicInfo->Flags         : 0x%lx\n",
            BytesWrittenOrRequired,
            BasicInfo->hActCtx,
            static_cast<PCWSTR>(decimalContext),
            BasicInfo->Flags
            );
    }
    Flags = (Flags & ~QUERY_ACTCTX_FLAG_NO_ADDREF);
    if (!QueryActCtxW(Flags, ActCtxHandle, NULL, ActivationContextDetailedInformation, ContextDetailed, 4096, &BytesWrittenOrRequired))
        QueryFailed("ActivationContextDetailedInformation", __LINE__ - 1);
    else
    {
        Trace(
            "BytesWrittenOrRequired                        : 0x%lx\n"
            "ContextDetailed->dwFlags                      : 0x%lx\n"
            "ContextDetailed->ulFormatVersion              : 0x%lx\n"
            "ContextDetailed->ulAssemblyCount              : 0x%lx\n"
            "ContextDetailed->ulRootManifestPathType       : 0x%lx\n"
            "ContextDetailed->ulRootManifestPathChars      : 0x%lx\n"
            "ContextDetailed->ulRootConfigurationPathType  : 0x%lx\n"
            "ContextDetailed->ulRootConfigurationPathChars : 0x%lx\n"
            "ContextDetailed->ulAppDirPathType             : 0x%lx\n"
            "ContextDetailed->ulAppDirPathChars            : 0x%lx\n"
            "ContextDetailed->lpRootManifestPath           : %ls\n"
            "ContextDetailed->lpRootConfigurationPath      : %ls\n"
            "ContextDetailed->lpAppDirPath                 : %ls\n"
            ,
            BytesWrittenOrRequired,
            ContextDetailed->dwFlags,
            ContextDetailed->ulFormatVersion,
            ContextDetailed->ulAssemblyCount,
            ContextDetailed->ulRootManifestPathType,
            ContextDetailed->ulRootManifestPathChars,
            ContextDetailed->ulRootConfigurationPathType,
            ContextDetailed->ulRootConfigurationPathChars,
            ContextDetailed->ulAppDirPathType,
            ContextDetailed->ulAppDirPathChars,
            ContextDetailed->lpRootManifestPath,
            ContextDetailed->lpRootConfigurationPath,
            ContextDetailed->lpAppDirPath
            );
    }
    {
        ULONG AssemblyIndex = 0;
        ULONG FileInAssemblyIndex = 0;

        //
        // 0 produces ERROR_INTERNAL_ERROR
        //
        for (AssemblyIndex = 0 ; AssemblyIndex <= ContextDetailed->ulAssemblyCount ; AssemblyIndex += 1)
        {
            if (!QueryActCtxW(Flags, ActCtxHandle, &AssemblyIndex, AssemblyDetailedInformationInActivationContext, AssemblyDetailed, 4096, &BytesWrittenOrRequired))
            {
                Trace(
                    "%s(%lu):QueryActCtx(Flags=0x%lx, ActCtxHandle=%p%ls, AssemblyIndex=0x%lx, AssemblyDetailedInformationInActivationContext) LastError=%lu (%ls)\n",
                    __FUNCTION__,
                    __LINE__ - 1,
                    Flags,
                    ActCtxHandle,
                    static_cast<PCWSTR>(decimalContext),
                    AssemblyIndex,
                    ::FusionpGetLastWin32Error(),
                    ::FusionpThreadUnsafeGetLastWin32ErrorMessageW()
                    );
            }
            else
            {
                Trace(
                    "AssemblyIndex                                       : 0x%lx\n"
                    "BytesWrittenOrRequired                              : 0x%lx\n"
                    "AssemblyDetailed->ulFlags                           : 0x%lx\n"
                    "AssemblyDetailed->ulEncodedAssemblyIdentityLength   : 0x%lx\n"
                    "AssemblyDetailed->ulManifestPathType                : 0x%lx\n"
                    "AssemblyDetailed->ulManifestPathLength              : 0x%lx\n"
                    "AssemblyDetailed->liManifestLastWriteTime           : 0x%I64x\n"
                    "AssemblyDetailed->ulPolicyPathType                  : 0x%lx\n"
                    "AssemblyDetailed->ulPolicyPathLength                : 0x%lx\n"
                    "AssemblyDetailed->liPolicyLastWriteTime             : 0x%I64x\n"
                    "AssemblyDetailed->ulMetadataSatelliteRosterIndex    : 0x%lx\n"
                    "AssemblyDetailed->ulManifestVersionMajor            : 0x%lx\n"
                    "AssemblyDetailed->ulManifestVersionMinor            : 0x%lx\n"
                    "AssemblyDetailed->ulPolicyVersionMajor              : 0x%lx\n"
                    "AssemblyDetailed->ulPolicyVersionMinor              : 0x%lx\n"
                    "AssemblyDetailed->ulAssemblyDirectoryNameLength     : 0x%lx\n"
                    "AssemblyDetailed->lpAssemblyEncodedAssemblyIdentity : %ls\n"
                    "AssemblyDetailed->lpAssemblyManifestPath            : %ls\n"
                    "AssemblyDetailed->lpAssemblyPolicyPath              : %ls\n"
                    "AssemblyDetailed->lpAssemblyDirectoryName           : %ls\n"
                    "AssemblyDetailed->ulFileCount                       : 0x%lx\n"
                    ,
                    AssemblyIndex,
                    BytesWrittenOrRequired,
                    AssemblyDetailed->ulFlags,
                    AssemblyDetailed->ulEncodedAssemblyIdentityLength,
                    AssemblyDetailed->ulManifestPathType,
                    AssemblyDetailed->ulManifestPathLength,
                    AssemblyDetailed->liManifestLastWriteTime.QuadPart,
                    AssemblyDetailed->ulPolicyPathType,
                    AssemblyDetailed->ulPolicyPathLength,
                    AssemblyDetailed->liPolicyLastWriteTime.QuadPart,
                    AssemblyDetailed->ulMetadataSatelliteRosterIndex,
                    AssemblyDetailed->ulManifestVersionMajor,
                    AssemblyDetailed->ulManifestVersionMinor,
                    AssemblyDetailed->ulPolicyVersionMajor,
                    AssemblyDetailed->ulPolicyVersionMinor,
                    AssemblyDetailed->ulAssemblyDirectoryNameLength,
                    AssemblyDetailed->lpAssemblyEncodedAssemblyIdentity,
                    AssemblyDetailed->lpAssemblyManifestPath,
                    AssemblyDetailed->lpAssemblyPolicyPath,
                    AssemblyDetailed->lpAssemblyDirectoryName,
                    AssemblyDetailed->ulFileCount
                    );

                QueryIndex.ulAssemblyIndex = AssemblyIndex;
                SetLastError(NO_ERROR);
                if (AssemblyDetailed->ulFileCount == 0)
                {
                    Trace("AssemblyDetailed->ulFileCount is 0, working around bug, setting it to 4.\n");
                    AssemblyDetailed->ulFileCount = 4; // bug workaround
                }
                for (FileInAssemblyIndex = 0 ; FileInAssemblyIndex != AssemblyDetailed->ulFileCount ; FileInAssemblyIndex += 1)
                {
                    QueryIndex.ulFileIndexInAssembly = FileInAssemblyIndex;
                    if (!QueryActCtxW(Flags, ActCtxHandle, &QueryIndex, FileInformationInAssemblyOfAssemblyInActivationContext, DllRedir, 4096, &BytesWrittenOrRequired))
                    {
                        Trace(
                            "%s(%lu):QueryActCtx(Flags=0x%lx, ActCtxHandle=%p%ls, QueryIndex={ulAssemblyIndex=0x%lx, ulFileIndexInAssembly=0x%lx}, FileInformationInAssemblyOfAssemblyInActivationContext) LastError=%lu (%ls)\n",
                            __FUNCTION__,
                            __LINE__,
                            Flags,
                            ActCtxHandle,
                            static_cast<PCWSTR>(decimalContext),
                            QueryIndex.ulAssemblyIndex,
                            QueryIndex.ulFileIndexInAssembly,
                            ::FusionpGetLastWin32Error(),
                            ::FusionpThreadUnsafeGetLastWin32ErrorMessageW()
                            );
                        //break;
                    }
                    else
                    {
                        Trace(
                            "AssemblyIndex                          : 0x%lx\n"
                            "FileIndex                              : 0x%lx\n"
                            "BytesWrittenOrRequired                 : 0x%lx\n"
                            "DllRedir[0x%lx,0x%lx]->ulFlags         : 0x%lx\n"
                            "DllRedir[0x%lx,0x%lx]->ulFilenameLength : 0x%lx\n"
                            "DllRedir[0x%lx,0x%lx]->ulPathLength    : 0x%lx\n"
                            "DllRedir[0x%lx,0x%lx]->lpFileName      : %ls\n"
                            "DllRedir[0x%lx,0x%lx]->lpFilePath      : %ls\n"
                            ,
                            AssemblyIndex,
                            FileInAssemblyIndex,
                            BytesWrittenOrRequired,
                            AssemblyIndex, FileInAssemblyIndex, DllRedir->ulFlags,
                            AssemblyIndex, FileInAssemblyIndex, DllRedir->ulFilenameLength,
                            AssemblyIndex, FileInAssemblyIndex, DllRedir->ulPathLength,
                            AssemblyIndex, FileInAssemblyIndex, DllRedir->lpFileName,
                            AssemblyIndex, FileInAssemblyIndex, DllRedir->lpFilePath
                            );
                    }
                }
            }
        }
    }
}

void TestQueryActCtx2()
{
    {
        CFusionActCtxHandle LogonuiActCtxHandle;
        {
            WCHAR LogonuiManifest[MAX_PATH * 2];
            ACTCTXW            LogonuiActCtx = {sizeof(LogonuiActCtx)};

            LogonuiManifest[0] = 0;
            GetSystemDirectoryW(LogonuiManifest, MAX_PATH);
            wcscat(LogonuiManifest, L"\\logonui.exe.manifest");
            LogonuiActCtx.lpSource = LogonuiManifest;
            LogonuiActCtxHandle.Win32Create(&LogonuiActCtx);
        }
        {
            CFusionActCtxScope LogonuiActCtxScope;
            LogonuiActCtxScope.Win32Activate(LogonuiActCtxHandle);

            TestQueryActCtx3(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX | QUERY_ACTCTX_FLAG_NO_ADDREF, NULL);
        }
        TestQueryActCtx3(QUERY_ACTCTX_FLAG_NO_ADDREF, LogonuiActCtxHandle);
    }
    TestQueryActCtx3(QUERY_ACTCTX_FLAG_NO_ADDREF, ACTCTX_EMPTY);
    TestQueryActCtx3(QUERY_ACTCTX_FLAG_NO_ADDREF, ACTCTX_SYSTEM_DEFAULT);
}

void TestQueryActCtx()
{
    WCHAR ExePath[MAX_PATH];
    CStringBuffer DllPath;
    HMODULE hmod = NULL;
    WCHAR buffer[200];

    if (IamExe)
    {
        if (!Kernel32.GetModuleFileNameW(NULL, ExePath, RTL_NUMBER_OF(ExePath)))
            ThrowLastError();
        if (!DllPath.Win32Format(L"%ls.dll", ExePath))
            ThrowLastError();
        ::CopyFileW(ExePath, DllPath, FALSE);
    }

    if (IamDll)
    {
        hmod = NULL;
        hmod = (HMODULE)RtlPcToFileHeader(InitCommonControls, (PVOID*)&hmod);
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from dll static dep: %p %ls\n", hmod, buffer);

#if defined(ISOLATION_AWARE_ENABLED)
        hmod = IsolationAwareLoadLibraryW(L"comctl32.dll");
        LastError = ::GetLastError();
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from dll IsolationAwareLoadLibraryW: %p %ls\n", hmod, buffer);
#endif

        hmod = LoadLibraryW(L"comctl32.dll");
        LastError = ::GetLastError();
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from dll LoadLibraryW: %p %ls\n", hmod, buffer);

        return;
    }

    {
        hmod = NULL;
        hmod = (HMODULE)RtlPcToFileHeader(InitCommonControls, (PVOID*)&hmod);
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from exe static dep: %p %ls\n", hmod, buffer);

#if defined(ISOLATION_AWARE_ENABLED)
        hmod = IsolationAwareLoadLibraryW(L"comctl32.dll");
        LastError = ::GetLastError();
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from exe IsolationAwareLoadLibraryW: %p %ls\n", hmod, buffer);
#endif
        hmod = LoadLibraryW(L"comctl32.dll");
        LastError = ::GetLastError();
        wcscpy(buffer, L"FAILED");
        if (hmod != NULL)
            Kernel32.GetModuleFileNameW(hmod, buffer, RTL_NUMBER_OF(buffer));
        Trace("from exe LoadLibraryW: %p %ls\n", hmod, buffer);

    }

    SetDllBitInPeImage(DllPath);
    Trace("Enter LoadLibraryW\n");
    hmod = LoadLibraryW(DllPath);
    LastError = ::GetLastError();
    Trace("Exit LoadLibraryW\n");
    void (*pfn)(void) = reinterpret_cast<void (*)(void)>(GetProcAddress(hmod, "TestQueryActCtx"));
    Trace("Enter non DllMain call to TestQueryActCtx\n");
    pfn();
    Trace("Exit non DllMain call to TestQueryActCtx\n");
    FreeLibrary(hmod);
    DeleteFileW(DllPath);
}

DWORD WINAPI Test64kThreadMain(void* pv)
{
    LONG_PTR i = 0;
    HANDLE h = 0;
    ULONG_PTR cookie = 0;
    LONG_PTR j = reinterpret_cast<LONG_PTR>(pv);

    GetCurrentActCtx(&h);

    ActivateActCtx(h, &cookie);
    printf("%Id ", cookie);
    if (cookie == 0) printf("cookie 0\n");
    ActivateActCtx(h, &cookie);
    printf("%Id ", cookie);
    if (cookie == 0) printf("cookie 0\n");

    __try
    {
        for (i = 0 ; i < j ; ++i)
        {
            if (!ActivateActCtx(h, &cookie))
                printf("activate error %lu\n", ::GetLastError());
            else
            {
                if (cookie == 0) printf("cookie 0\n");
                //printf("%Id ", cookie);
                if (!DeactivateActCtx(0, cookie))
                    printf("deactivate error %lu\n", ::GetLastError());
            }
        }
    }
    __except(printf("exception %lx\n", GetExceptionCode()),EXCEPTION_EXECUTE_HANDLER)
    {
    }
    printf("final cookie value %Id\n", cookie);
    return 0;
}

void Test64k()
{
    HANDLE h = 0;
    ULONG_PTR cookie = 0;
    DWORD threadId = 0;
    LONG_PTR i = 0;
    LONG_PTR j = 0;
    HANDLE thread = 0;

    GetCurrentActCtx(&h);

    ActivateActCtx(h, &cookie);
    if (cookie == 0) printf("cookie 0\n");
    printf("%Id ", cookie);
    ActivateActCtx(h, &cookie);
    if (cookie == 0) printf("cookie 0\n");
    printf("%Id ", cookie);

    for (j = 0 ; j < 0xfff0 ; ++j)
    {
        if (!ActivateActCtx(h, &cookie))
            printf("activate error %lu\n", ::GetLastError());
        else
        {
            if (cookie == 0) printf("cookie 0\n");
            if (!DeactivateActCtx(0, cookie))
                printf("deactivate error %lu\n", ::GetLastError());
        }
    }
    for ( ; j < 0xffff + 0xf ; ++j)
    {
        if (!ActivateActCtx(h, &cookie))
            printf("activate error %lu\n", ::GetLastError());
        printf("%Id ", cookie);
        if (cookie == 0) printf("cookie 0\n");
        thread = CreateThread(NULL, 0, Test64kThreadMain, reinterpret_cast<void*>(j), 0, &threadId);
    }
    WaitForSingleObject(thread, INFINITE);
}

void TestDotLocalSingleInstancing()
{
    FILE* File = 0;
    HMODULE DllHandle = 0;

    {
        WCHAR DotLocal[MAX_PATH];
        if (!Kernel32.GetModuleFileNameW(GetMyHandle(), DotLocal, NUMBER_OF(DotLocal) - sizeof(".local")))
            ThrowLastError();
        wcscat(DotLocal, L".local");
        File = _wfopen(DotLocal, L"w");
        fprintf(File, "\n");
        fclose(File);
    }
    {
        WCHAR System32Mshtml[MAX_PATH];
        WCHAR LocalMshtml[MAX_PATH];
        WCHAR ResultingMshtml[MAX_PATH];

        if (!GetSystemDirectoryW(System32Mshtml, NUMBER_OF(System32Mshtml) - sizeof("\\Mshtml.dll")))
            ThrowLastError();
        wcscat(System32Mshtml, L"\\Mshtml.dll");

        if (!Kernel32.GetModuleFileNameW(GetMyHandle(), LocalMshtml, NUMBER_OF(LocalMshtml) - sizeof("\\Mshtml.dll")))
            ThrowLastError();
        *wcsrchr(LocalMshtml, '\\') = 0;
        wcscat(LocalMshtml, L"\\Mshtml.dll");

        //DllHandle = LoadLibraryW(L"Mshtml.dll");
        //Trace("LoadLibrary(Mshtml): %p\n", DllHandle);

        if (!CopyFileW(System32Mshtml, LocalMshtml, FALSE))
            ThrowLastError();
        Trace("copy %ls -> %ls\n", System32Mshtml, LocalMshtml);

        ULONG i;
        for (i = 0 ; i != 4 ; i += 1)
        {
            DllHandle = LoadLibraryW(System32Mshtml);
            wcscpy(ResultingMshtml, L"FAILED");
            if (DllHandle != NULL)
                Kernel32.GetModuleFileNameW(DllHandle, ResultingMshtml, RTL_NUMBER_OF(ResultingMshtml));
            Trace("LoadLibrary(%ls): %p %ls\n", System32Mshtml, DllHandle, ResultingMshtml);
        }
    }
}

void
TestCreateActCtx(
    int n,
    wchar_t **args
    )
{
    ACTCTXW acw;
    int i;
    WCHAR rgwchSource[MAX_PATH];
    PCWSTR pszResource;
    HANDLE hActCtx;
    DWORD dwLastError;

    for (i=0; i<n; i++)
    {
        PCWSTR arg = args[i];
        PCWSTR semi = wcschr(arg, L';');

        memset(&acw, 0, sizeof(acw));

        acw.cbSize = sizeof(acw);

        if (semi == NULL)
        {
            acw.lpSource = arg;
        }
        else
        {
            int cch = (int) (semi - arg);

            if (cch >= NUMBER_OF(rgwchSource))
                cch = NUMBER_OF(rgwchSource) - 1;

            memcpy(rgwchSource, arg, cch * sizeof(WCHAR));
            rgwchSource[cch] = L'\0';

            if (semi[1] == L'#')
            {
                wchar_t *pszDummy;
                pszResource = MAKEINTRESOURCEW(wcstoul(semi+1, &pszDummy, 10));
            }
            else
            {
                pszResource = semi+1;
            }

            acw.lpSource = rgwchSource;
            acw.lpResourceName = pszResource;
            acw.dwFlags |= ACTCTX_FLAG_RESOURCE_NAME_VALID;
        }

        hActCtx = ::CreateActCtxW(&acw);
        dwLastError = ::GetLastError();
        printf("CreateActCtxW() on \"%ls\" returned %p\n", arg, hActCtx);
        if (hActCtx == INVALID_HANDLE_VALUE)
        {
            printf("   ::GetLastError() = %lu\n", dwLastError);
        }

        ::ReleaseActCtx(hActCtx);
    }
}

const char comctlv6manifest[]=
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
"<assemblyIdentity"
"    name=\"Microsoft.Windows.Shell.notepad\""
"    processorArchitecture=\"x86\""
"    version=\"5.1.0.0\""
"    type=\"win32\"/>"
"<dependency>"
"    <dependentAssembly>"
"        <assemblyIdentity"
"            type=\"win32\""
"            name=\"Microsoft.Windows.Common-Controls\""
"            version=\"6.0.0.0\""
"            processorArchitecture=\"x86\""
"            publicKeyToken=\"6595b64144ccf1df\""
"            language=\"*\""
"        />"
"    </dependentAssembly>"
"</dependency>"
"</assembly>"
;

const char comctlv5manifest[]=
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
"<assemblyIdentity"
"    name=\"Microsoft.Windows.Shell.notepad\""
"    processorArchitecture=\"x86\""
"    version=\"5.1.0.0\""
"    type=\"win32\"/>"
"</assembly>"
;

void TestCreateActctxAdminOverride()
{
    WCHAR exe[MAX_PATH];
    WCHAR dll[MAX_PATH];
    WCHAR comctl[MAX_PATH];
    WCHAR manifest[MAX_PATH];
    ACTCTXW Actctx = {sizeof(Actctx)};
    FILE* File = NULL;
    ULONG_PTR ulCookie = 0;
    HMODULE DllHandle = 0;
    HANDLE ActctxHandle;
    GUID Guid = { 0 };

    wcscpy(exe, GetMyModuleFullPath());
    wcscpy(dll, GetMyModuleFullPath());
    wcscat(dll, L".dll");
    CopyFileW(exe, dll, FALSE);
    SetDllBitInPeImage(dll);

#if 0
    wcscpy(String, GetMyModuleFullPath());
    wcscat(String, L".Manifest");
    DeleteFileW(String);
    Trace("DeleteFile(%ls)\n", String);

    wcscpy(String, GetMyModuleFullPath());
    wcscat(String, L".2.Manifest");
    DeleteFileW(String);
    Trace("DeleteFile(%ls)\n", String);

    wcscpy(String, GetMyModuleFullPath());
    wcscat(String, L".3.Manifest");
    DeleteFileW(String);
    Trace("DeleteFile(%ls)\n", String);
#endif

    Actctx.lpSource = dll;
    /*
    ActctxHandle = CreateActCtxW(&Actctx);
    if (ActctxHandle == INVALID_HANDLE_VALUE)
        return;
    Trace("CreateActCtxW succeeded\n");
    */

    //
    // manfile is number to put in the manifest file name, 0 for none
    // good is what the contents of the file are, 0=>bad, 1=>v5, 2=>v6
    // res is what resource id to ask for
    //
    for (int manfile = 0 ; manfile != 4 ; manfile += 1)
    {
        WCHAR Number[RTL_BITS_OF(ULONG_PTR) + 3];
        for (int good = 0 ; good != 3 ; good += 1)
        {
            for (int res = -1 ; res != 4 ; res += 1)
            {
                Trace("---------------------------------------------------------------\n");
                Trace("resourceid is %d%s\n", res, (res != -1) ? "" : " (flag not set)");
                if (res != -1)
                {
                    Actctx.lpResourceName = MAKEINTRESOURCEW(res);
                    Actctx.dwFlags |= ACTCTX_FLAG_RESOURCE_NAME_VALID;
                    Actctx.lpResourceName = MAKEINTRESOURCEW(res);
                }
                else
                {
                    Actctx.dwFlags &= ~ACTCTX_FLAG_RESOURCE_NAME_VALID;
                }
                for (int delman = 0 ; delman != 4 ; delman += 1)
                {
                    Number[0] = 0;
                    if (delman)
                        swprintf(Number, L".%d", delman);
                    swprintf(manifest, L"%ls%ls%ls%ls", GetMyModuleFullPathWithoutExtension(), L".dll", Number, L".Manifest");
                    /*
                    CoCreateGuid(&Guid);
                    swprintf(String3, L"%ls%I64x%I64x", GetMyModuleFullPath(), *reinterpret_cast<__int64*>(&Guid), *(1+reinterpret_cast<__int64*>(&Guid)));
                    if (!MoveFileW(String, String3) && ::GetLastError() != ERROR_FILE_NOT_FOUND)
                        Trace("MoveFile(%ls -> %ls) FAILED %d\n", String, String3, ::GetLastError());
                    else
                        ;//Trace("MoveFile(%ls -> %ls)\n", String, String3);
                    */
                    if (!DeleteFileW(manifest) && ::GetLastError() != ERROR_FILE_NOT_FOUND)
                        Trace("DeleteFile(%ls) FAILED %d\n", manifest, ::GetLastError());
                    else
                        ;//Trace("DeleteFile(%ls)\n", String3);
                }
                Number[0] = 0;
                if (manfile != 0)
                {
                    swprintf(Number, L".%d", manfile);
                }
                swprintf(manifest, L"%ls%ls%ls%ls", GetMyModuleFullPathWithoutExtension(), L".dll", Number, L".Manifest");
                //Trace("fopen(%ls)\n", String);
                File = _wfopen(manifest, L"w+");
                if (File == NULL)
                {
                    perror("fopen");
                }
                switch (good)
                {
                case 0:
                    fprintf(File, "bad");
                    Trace("%ls is bad\n", manifest);
                    break;
                case 1:
                    fprintf(File, "%s", comctlv5manifest);
                    Trace("%ls is comctlv5manifest\n", manifest);
                    break;
                case 2:
                    fprintf(File, "%s", comctlv6manifest);
                    Trace("%ls is comctlv6manifest\n", manifest);
                    break;
                }
                fclose(File);

                ActctxHandle = CreateActCtxW(&Actctx);
                if (ActctxHandle == INVALID_HANDLE_VALUE)
                {
                    Trace("CreateActCtxW failed %d\n", ::GetLastError());
                    ulCookie = 0;
                }
                else
                {
                    Trace("CreateActCtxW succeeded %p\n", ActctxHandle);
                    ActivateActCtx(ActctxHandle, &ulCookie);
                }
                __try
                {
                    PWSTR filePart;
                    comctl[0] = 0;
                    SearchPathW(NULL, L"comctl32.dll", NULL, RTL_NUMBER_OF(comctl), comctl, &filePart);
                }
                __finally
                {
                    if (ActctxHandle != INVALID_HANDLE_VALUE)
                        DeactivateActCtx(0, ulCookie);
                }
                Trace("SearchPathW(comctl32.dll): %ls\n", comctl);
            }
        }
    }
}

void TestCreateActctxLikeCreateProcess()
{
#if defined(ACTCTX_FLAG_LIKE_CREATEPROCESS)
    WCHAR comctl[MAX_PATH];
    WCHAR manifest[MAX_PATH];
    ACTCTXW Actctx = {sizeof(Actctx)};
    FILE* File = NULL;
    ULONG_PTR ulCookie = 0;
    HMODULE DllHandle = 0;
    HANDLE ActctxHandle;
    PWSTR filePart;

    Actctx.lpSource = GetMyModuleFullPath();
    Actctx.dwFlags = ACTCTX_FLAG_LIKE_CREATEPROCESS;

    wcscpy(manifest, GetMyModuleFullPath());
    wcscat(manifest, L".Manifest");
    DeleteFileW(manifest);
    //Trace("DeleteFile(%ls)\n", manifest);

    ActctxHandle = CreateActCtxW(&Actctx);
    if (ActctxHandle == INVALID_HANDLE_VALUE)
    {
        Trace("CreateActCtxW failed %d\n", ::GetLastError());
        ulCookie = 0;
    }
    else
    {
        Trace("CreateActCtxW succeeded %p\n", ActctxHandle);
        ActivateActCtx(ActctxHandle, &ulCookie);
    }
    __try
    {
        comctl[0] = 0;
        SearchPathW(NULL, L"comctl32.dll", NULL, RTL_NUMBER_OF(comctl), comctl, &filePart);
    }
    __finally
    {
        if (ActctxHandle != INVALID_HANDLE_VALUE)
            DeactivateActCtx(0, ulCookie);
    }
    Trace("SearchPathW(comctl32.dll): %ls\n", comctl);

    File = _wfopen(manifest, L"w");
    fprintf(File, "%s", comctlv5manifest);
    fclose(File);
    Trace("%ls == comctlv5manifest\n", manifest);

    ActctxHandle = CreateActCtxW(&Actctx);
    if (ActctxHandle == INVALID_HANDLE_VALUE)
    {
        Trace("CreateActCtxW failed %d\n", ::GetLastError());
        ulCookie = 0;
    }
    else
    {
        Trace("CreateActCtxW succeeded %p\n", ActctxHandle);
        ActivateActCtx(ActctxHandle, &ulCookie);
    }
    __try
    {
        comctl[0] = 0;
        SearchPathW(NULL, L"comctl32.dll", NULL, RTL_NUMBER_OF(comctl), comctl, &filePart);
    }
    __finally
    {
        if (ActctxHandle != INVALID_HANDLE_VALUE)
            DeactivateActCtx(0, ulCookie);
    }
    Trace("SearchPathW(comctl32.dll): %ls\n", comctl);

    File = _wfopen(manifest, L"w");
    fprintf(File, "%ls", comctlv6manifest);
    fclose(File);
    Trace("%ls == comctlv6manifest\n", manifest);

    ActctxHandle = CreateActCtxW(&Actctx);
    if (ActctxHandle == INVALID_HANDLE_VALUE)
    {
        Trace("CreateActCtxW failed %d\n", ::GetLastError());
        ulCookie = 0;
    }
    else
    {
        Trace("CreateActCtxW succeeded %p\n", ActctxHandle);
        ActivateActCtx(ActctxHandle, &ulCookie);
    }
    __try
    {
        comctl[0] = 0;
        SearchPathW(NULL, L"comctl32.dll", NULL, RTL_NUMBER_OF(comctl), comctl, &filePart);
    }
    __finally
    {
        if (ActctxHandle != INVALID_HANDLE_VALUE)
            DeactivateActCtx(0, ulCookie);
    }
    Trace("SearchPathW(comctl32.dll): %ls\n", comctl);
#endif
}

void
TestQueryManifestInformationBasic(
    PCWSTR pszManifest
    )
{
    PSXS_QUERY_MANIFEST_INFORMATION pfn = NULL;
    LoadSxs();
    struct {
        SXS_MANIFEST_INFORMATION_BASIC mib;
        WCHAR rgwchSpaceForIdentity[1024];
        WCHAR rgwchSpaceForDirName[1024];
    } buff;

    GetSxsProc("SxsQueryManifestInformation", &pfn);

    if (!(*pfn)(0, pszManifest, SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC, 0, sizeof(buff), &buff, NULL)) {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
    }
}

void TestImage()
{
    PIMAGE_RESOURCE_DIRECTORY ImageResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)4;
    PIMAGE_RESOURCE_DIRECTORY_ENTRY ImageResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)4;;

    printf("ImageResourceDirectory %p\n", ImageResourceDirectory);
    printf("ImageResourceDirectory + 1 %p\n", ImageResourceDirectory + 1);

    printf("ImageResourceDirectoryEntry %p\n", ImageResourceDirectoryEntry);
    printf("ImageResourceDirectoryEntry + 1 %p\n", ImageResourceDirectoryEntry + 1);
}

class CSxsTestCleanup : public CCleanupBase
{
public:
    VOID DeleteYourself() { }
    ~CSxsTestCleanup() { }
};

#define private public
#include "sxsprotect.h"
#undef private

void TestInterlockedAlignment()
{
    __declspec(align(16)) SLIST_HEADER SlistHeader;

    RtlInitializeSListHead(&SlistHeader);

    CSxsTestCleanup* pc = new CSxsTestCleanup();
    printf("%p\n", pc);
    printf("%p\n", static_cast<SINGLE_LIST_ENTRY*>(pc));
    SxspAtExit(pc);

    CProtectionRequestRecord* pr = new CProtectionRequestRecord;
    printf("%p\n", pr);
    printf("%p\n", &pr->m_ListHeader);

    CStringListEntry* psle = new CStringListEntry;
    printf("%p\n", psle);
    printf("%p\n", static_cast<SINGLE_LIST_ENTRY*>(psle));

    RtlInterlockedPushEntrySList(&SlistHeader, pc);
    RtlInterlockedPushEntrySList(&SlistHeader, psle);
    RtlQueryDepthSList(&SlistHeader);
    RtlInterlockedPopEntrySList(&SlistHeader);
    RtlInterlockedFlushSList(&SlistHeader);
    // untested: RtlInterlockedPushListSList

    RtlInterlockedPushEntrySList(&pr->m_ListHeader, pc);
    RtlInterlockedPushEntrySList(&pr->m_ListHeader, psle);
    RtlQueryDepthSList(&pr->m_ListHeader);
    RtlInterlockedPopEntrySList(&pr->m_ListHeader);
    RtlInterlockedFlushSList(&pr->m_ListHeader);
    // untested: RtlInterlockedPushListSList

    printf("success\n");
}

void TestCreateActctxWindowsShellManifest()
{
    WCHAR WindowsShellManifestFileName[MAX_PATH];
    ACTCTXW ActCtx = { sizeof(ActCtx) };
    HANDLE ActCtxHandle = 0;
    WindowsShellManifestFileName[0] = 0;

    GetWindowsDirectoryW(WindowsShellManifestFileName, NUMBER_OF(WindowsShellManifestFileName) - 64);
    wcscat(WindowsShellManifestFileName, L"\\WindowsShell.Manifest");
    ActCtx.lpSource = WindowsShellManifestFileName;

    ActCtxHandle = CreateActCtxW(&ActCtx);
    Trace("TestCreateActctxWindowsShellManifest: %p, %lu\n", ActCtxHandle, ::GetLastError());
    ReleaseActCtx(ActCtxHandle);
}

class CObjectTypes
{
protected:
    std::vector<BYTE> m_ByteBuffer;
    PSYSTEM_OBJECTTYPE_INFORMATION m_TypedBuffer;
public:
};

void TestCreateGlobalEvent()
{
	if (!::CreateEventW(NULL, FALSE, FALSE, L"MGRIER"))
		return;
	Sleep(500000);
}

class CObjectSnapshot
{
protected:
    //
    // This interface is not very good, but it's easy..the entries
    // are of variable size...
    //
    std::vector<BYTE> m_ByteBuffer;
    SIZE_T            m_Size;

    //
    // Some operations, like sorting, require us to move all the string data
    // out of the elements. We do not manage this data in a lossless way.
    //
    // Ultimately, you may benefit from copying/transforming the data completely.
    //
    std::vector<BYTE> m_StringData;
public:

    SIZE_T size() const { return m_Size; }

    class iterator;

    class const_iterator
    {
    protected:
        const SYSTEM_OBJECT_INFORMATION* m_p;
    public:
        ~const_iterator() { }

        void operator=(const const_iterator& x) { m_p = x.m_p; }
        const_iterator(const const_iterator& x) : m_p(x.m_p) { }
        const_iterator(const BYTE* p = NULL) : m_p(reinterpret_cast<const SYSTEM_OBJECT_INFORMATION*>(p)) { }

        //void operator=(const iterator& x);
        //const_iterator(const iterator& x);

        bool operator==(const const_iterator& i) const
        {
            return (m_p == i.m_p);
        }

        bool operator!=(const const_iterator& i) const
        {
            return (m_p != i.m_p);
        }

        const SYSTEM_OBJECT_INFORMATION& operator*() const { return *m_p; }

        void operator++()
        {
            if (m_p != NULL)
            {
                if (m_p->NextEntryOffset != 0)
                    m_p = reinterpret_cast<const SYSTEM_OBJECT_INFORMATION*>(reinterpret_cast<const BYTE*>(m_p) + m_p->NextEntryOffset);
                else
                    m_p = NULL; // end
            }
        }

        const_iterator operator++(int)
        {
            const_iterator tmp = *this;
            ++*this;;
            return tmp;
        }
    };

    class iterator : public const_iterator
    {
    private:
        void operator=(const const_iterator&);
    public:
        ~iterator() { }
        iterator(BYTE* p = NULL) : const_iterator(p) { }

        SYSTEM_OBJECT_INFORMATION& operator*() { return const_cast<SYSTEM_OBJECT_INFORMATION&>(*m_p); }
    };

    const_iterator begin() const { return const_iterator(&m_ByteBuffer[0]); }
          iterator begin()       { return iterator(&m_ByteBuffer[0]); }
    const_iterator end() const   { return const_iterator(); }
          iterator end()         { return iterator(); }

    void swap(CObjectSnapshot& x)
    {
        std::swap(m_ByteBuffer, x.m_ByteBuffer);
        std::swap(m_Size, x.m_Size);
    }

    CObjectSnapshot() { }
    ~CObjectSnapshot() { }
};

class CHandleSnapshot
{
protected:
    std::vector<BYTE> m_ByteBuffer;
    PSYSTEM_HANDLE_INFORMATION_EX m_TypedBuffer;
public:

    SIZE_T size() const { return m_TypedBuffer->NumberOfHandles; }

    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* begin() { return &m_TypedBuffer->Handles[0]; }
    const SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* begin() const { return &m_TypedBuffer->Handles[0]; }

    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* end() { return begin() + size(); }
    const SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* end() const { return begin() + size(); }

    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX& operator[](size_t index) { return *(begin() + index); }
    const SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX& operator[](size_t index) const { return *(begin() + index); }

    void reserve(SIZE_T n)
    {
        resize(n); // since there's no constructor..
    }

    void resize(SIZE_T n)
    {
        m_ByteBuffer.resize(sizeof(SYSTEM_HANDLE_INFORMATION_EX) + (n - 1) * sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX));
        Resync();
        m_TypedBuffer->NumberOfHandles = n;
    }

    void swap(CHandleSnapshot& x)
    {
        std::swap(m_ByteBuffer, x.m_ByteBuffer);
        x.Resync();
        Resync();
    }

    CHandleSnapshot() : m_TypedBuffer(NULL) { }
    ~CHandleSnapshot() { }

    void GetHandlesForCurrentProcess()
    {
        GetHandlesForProcess(GetCurrentProcessId());
    }

    void GetHandlesForProcess(ULONG_PTR pid)
    {
        GetHandlesForSystem();
        FilterByProcessId(pid);
    }

    void GetHandlesForSystem()
    {
        //
        // the actual needed size can be very large, over 256k
        //
        ULONG Size = 0;

        m_TypedBuffer = NULL;
        m_ByteBuffer.resize(sizeof(SYSTEM_HANDLE_INFORMATION_EX));
        NTSTATUS Status = NtQuerySystemInformation(SystemExtendedHandleInformation, &m_ByteBuffer[0], static_cast<ULONG>(m_ByteBuffer.size()), &Size);
        while (Status == STATUS_INFO_LENGTH_MISMATCH && Size != 0)
        {
            //
            // since it is transient, let's be safe and double it
            //
            m_ByteBuffer.resize(Size * 2);
            Status = NtQuerySystemInformation(SystemExtendedHandleInformation, &m_ByteBuffer[0], static_cast<ULONG>(m_ByteBuffer.size()), &Size);
        }
        if (!NT_SUCCESS(Status))
        {
            Trace("NtQuerySystemInformation failed 0x%lx\n", Status);
            return;
        }
        m_ByteBuffer.resize(Size);
        m_TypedBuffer = reinterpret_cast<PSYSTEM_HANDLE_INFORMATION_EX>(&m_ByteBuffer[0]);
        Trace("%Id total handles system-wide\n", m_TypedBuffer->NumberOfHandles);
    }

    void FilterByProcessId(ULONG_PTR pid)
    {
        SIZE_T Scan = 0;
        SIZE_T Keep = 0;

        for (Scan = 0 ; Scan != m_TypedBuffer->NumberOfHandles ; Scan += 1)
        {
            if (m_TypedBuffer->Handles[Scan].UniqueProcessId == pid)
            {
                if (Keep != Scan)
                    m_TypedBuffer->Handles[Keep] = m_TypedBuffer->Handles[Scan]; // struct copy
                Keep += 1;
            }
        }
        m_TypedBuffer->NumberOfHandles = Keep;
    }

    void Resync()
    {
        m_TypedBuffer = reinterpret_cast<PSYSTEM_HANDLE_INFORMATION_EX>(&m_ByteBuffer[0]);
    }

    CHandleSnapshot(const CHandleSnapshot& x) : m_TypedBuffer(NULL)
    {
        this->m_ByteBuffer = x.m_ByteBuffer;
        Resync();
    }

    void operator=(const CHandleSnapshot& x)
    {
        this->m_ByteBuffer = x.m_ByteBuffer;
        Resync();
    }

    class CHandleValueOperatorLessThan
    {
    public:
        bool operator()(const SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX& x, const SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX& y)
        {
            return (x.HandleValue < y.HandleValue);
        }
    };

    void SortByHandleValue()
    {
        std::sort(begin(), end(), CHandleValueOperatorLessThan());
    }

    void operator-=(/*const*/CHandleSnapshot& x)
    {
        SortByHandleValue();
        x.SortByHandleValue();
        CHandleSnapshot temp(*this);
        resize(
            std::set_difference(temp.begin(), temp.end(), x.begin(), x.end(), begin(), CHandleValueOperatorLessThan())
            - begin());
    }

    void Dump()
    {
    }
};

class CHandleSnapshots
{
public:
    void Begin() { m_Begin.GetHandlesForCurrentProcess(); }
    void End() { m_End.GetHandlesForCurrentProcess(); m_Diff = m_Begin; m_Diff -= m_End; }

    CHandleSnapshot m_Begin;
    CHandleSnapshot m_End;
    CHandleSnapshot m_Diff;
};

void Pause()
{
    Trace("Press a key to continue\n");
    getchar();
}

void TestHandleLeaks()
{
    WCHAR WindowsDirectory[MAX_PATH];
    ULONG i = 0;
    CFusionFile DevNull;
    //SECURITY_ATTRIBUTES SecurityAttributes = { sizeof(SecurityAttributes), NULL, TRUE};

    WindowsDirectory[0] = 0;

    DevNull = CreateFileW(L"nul:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL/*&SecurityAttributes*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (DevNull == INVALID_HANDLE_VALUE)
        Trace("Open(nul:) failed %ld\n", ::GetLastError());

    GetWindowsDirectoryW(WindowsDirectory, NUMBER_OF(WindowsDirectory) - 64);

    {
        const WCHAR SubFunction[] = L"CreateActCtx";

        CHandleSnapshots handleSnapshots;
        handleSnapshots.Begin();
        Trace("%s Begin %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Begin.size());
        {
            WCHAR WindowsShellManifestFileName[MAX_PATH];
            ACTCTXW ActCtx = { sizeof(ActCtx) };
            HANDLE ActCtxHandle = 0;

            WindowsShellManifestFileName[0] = 0;
            wcscpy(WindowsShellManifestFileName, WindowsDirectory);
            wcscat(WindowsShellManifestFileName, L"\\WindowsShell.Manifest");
            ActCtx.lpSource = WindowsShellManifestFileName;

            for (i = 0 ; i != 100 ; ++i)
            {
                HANDLE ActCtxHandle = CreateActCtxW(&ActCtx);
                if (ActCtxHandle == INVALID_HANDLE_VALUE)
                    Trace("TestCreateActctxWindowsShellManifest: %p, %lu\n", ActCtxHandle, ::GetLastError());
                else
                    ReleaseActCtx(ActCtxHandle);
            }
        }
        handleSnapshots.End();
        Trace("%s End %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_End.size());

        if (handleSnapshots.m_Diff.size() != 0)
        {
            Trace("%s Diff %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Diff.size());
        }
    }

    Pause();
    {
        const WCHAR SubFunction[] = L"CreateActCtx + LoadLibrary(comctl32)";

        CHandleSnapshots handleSnapshots;
        handleSnapshots.Begin();
        Trace("%s Begin %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Begin.size());
        {
            WCHAR WindowsShellManifestFileName[MAX_PATH];
            ACTCTXW ActCtx = { sizeof(ActCtx) };
            HANDLE ActCtxHandle = 0;

            WindowsShellManifestFileName[0] = 0;
            wcscpy(WindowsShellManifestFileName, WindowsDirectory);
            wcscat(WindowsShellManifestFileName, L"\\WindowsShell.Manifest");
            ActCtx.lpSource = WindowsShellManifestFileName;

            for (i = 0 ; i != 100 ; ++i)
            {
                ULONG_PTR ulCookie = 0;

                HANDLE ActCtxHandle = CreateActCtxW(&ActCtx);
                if (ActCtxHandle == INVALID_HANDLE_VALUE)
                    Trace("TestCreateActctxWindowsShellManifest: %p, %lu\n", ActCtxHandle, ::GetLastError());
                else
                {
                    ActivateActCtx(ActCtxHandle, &ulCookie);
                    HMODULE Comctl = LoadLibraryW(L"comctl32.dll");
                    if (i == 1)
                    {
                        CHandleSnapshot handleSnapshot;
                        handleSnapshot.GetHandlesForCurrentProcess();
                        Trace("Comctl32.dll loaded first time %Id\n", handleSnapshot.size());
                        Pause();
                    }
                    FreeLibrary(Comctl);
                    if (i == 1)
                    {
                        CHandleSnapshot handleSnapshot;
                        handleSnapshot.GetHandlesForCurrentProcess();
                        Trace("Comctl32.dll unloaded first time %Id\n", handleSnapshot.size());
                        Pause();
                    }
                    if (ulCookie != 0)
                        DeactivateActCtx(0, ulCookie);
                    ReleaseActCtx(ActCtxHandle);
                    if (i == 1)
                    {
                        CHandleSnapshot handleSnapshot;
                        handleSnapshot.GetHandlesForCurrentProcess();
                        Trace("Comctl32.dll unloaded + ReleaseActCtxfirst time %Id\n", handleSnapshot.size());
                        Pause();
                    }
                }
            }
        }
        handleSnapshots.End();
        Trace("%s End %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_End.size());

        if (handleSnapshots.m_Diff.size() != 0)
        {
            Trace("%s Diff %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Diff.size());
        }
    }

    Pause();
    {
        WCHAR Me[MAX_PATH];
        STARTUPINFOW StartupInfo = {sizeof(StartupInfo)};
        PROCESS_INFORMATION ProcessInfo = {0};
        static const WCHAR SubFunction[] = L"CreateProcess";

        Kernel32.GetModuleFileNameW(NULL, Me, NUMBER_OF(Me));

        CHandleSnapshots handleSnapshots;
        handleSnapshots.Begin();
        Trace("%s Begin %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Begin.size());

        for (i = 0 ; i != 100 ; ++i)
        {
            StartupInfo.hStdOutput = DevNull;
            StartupInfo.hStdError = DevNull;
            StartupInfo.dwFlags = STARTF_USESTDHANDLES;

            if (!CreateProcessW(Me, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo))
            {
                Trace("CreateProcess failed %ld\n", ::GetLastError());
            }
            else
            {
                WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
                WaitForSingleObject(ProcessInfo.hThread, INFINITE);
                if (!CloseHandle(ProcessInfo.hProcess))
                    Trace("CloseHandle(Process %p) failed %ld\n", ProcessInfo.hProcess, ::GetLastError());
                if (!CloseHandle(ProcessInfo.hThread))
                    Trace("CloseHandle(Thread %p) failed %ld\n", ProcessInfo.hThread, ::GetLastError());
            }
        }
        handleSnapshots.End();
        Trace("%s End %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_End.size());

        if (handleSnapshots.m_Diff.size() != 0)
        {
            Trace("%s Diff %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Diff.size());
        }
    }
    Pause();
    {
        WCHAR SubFunction[sizeof("LoadLibrary xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")];
        WCHAR DllPath[MAX_PATH];
        ULONG j = 0;

        const static PCWSTR Leaves[] = {
            L"mshtml.dll",
            L"wintrust.dll",
            L"shell32.dll",
            L"crypt32.dll",
            L"msxml.dll",
            L"shdocvw.dll",
            L"msxml2.dll",
            L"msxml3.dll"
            };

        for (j = 0 ; j != NUMBER_OF(Leaves) ; ++j)
        {
            SubFunction[0] = 0;
            wcscat(SubFunction, L"LoadLibrary ");
            wcscat(SubFunction, Leaves[j]);

            DllPath[0] = 0;
            wcscat(DllPath, WindowsDirectory);
            wcscat(DllPath, L"\\system32\\");
            wcscat(DllPath, Leaves[j]);

            CHandleSnapshots handleSnapshots;
            handleSnapshots.Begin();
            Trace("%s Begin %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Begin.size());
            for (i = 0 ; i != 20 ; ++i)
            {
                HMODULE DllHandle;

                if ((DllHandle = LoadLibraryW(DllPath)) != NULL)
                    FreeLibrary(DllHandle);
                else
                    Trace("LoadLibraryW(%ls) failed %ld\n", DllPath, ::GetLastError());
            }
            handleSnapshots.End();
            Trace("%s End %ls : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_End.size());
            if (handleSnapshots.m_Diff.size() != 0)
            {
                Trace("%s Diff %s : %Id handles\n", __FUNCTION__, SubFunction, handleSnapshots.m_Diff.size());
            }
        }
    }
    Pause();
}

#define YET_ANOTHER_PASTE(x,y) x##y
#define YET_YET_ANOTHER_PASTE(x,y) YET_ANOTHER_PASTE(x,y)
#define LSXS_PROCESSOR_ARCHITECTURE YET_YET_ANOTHER_PASTE(L, SXS_PROCESSOR_ARCHITECTURE)

const WCHAR ToolsCrtManifest[]=
L"<?xml version=\"1.0\" standalone=\"yes\"?>"
L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
L"<assemblyIdentity"
L"    name=\"Microsoft.Windows.SxsTest.ToolsCrtClient\""
L"    processorArchitecture=\"" LSXS_PROCESSOR_ARCHITECTURE L"\"" /* Note that this only actually exists on x86 */
L"    version=\"5.1.0.0\""
L"    type=\"win32\"/>"
L"<dependency>"
L"    <dependentAssembly>"
L"        <assemblyIdentity"
L"            type=\"win32\""
L"            name=\"Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries\""
L"            version=\"6.0.0.0\""
L"            processorArchitecture=\"" LSXS_PROCESSOR_ARCHITECTURE L"\""
L"            publicKeyToken=\"6595b64144ccf1df\""
L"            language=\"*\""
L"        />"
L"    </dependentAssembly>"
L"</dependency>"
L"</assembly>"
;

const WCHAR WindowsCrtManifest[]=
L"<?xml version=\"1.0\" standalone=\"yes\"?>"
L"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">"
L"<assemblyIdentity"
L"    name=\"Microsoft.Windows.SxsTest.WindowsCrtClient\""
L"    processorArchitecture=\"" LSXS_PROCESSOR_ARCHITECTURE L"\""
L"    version=\"5.1.0.0\""
L"    type=\"win32\"/>"
L"<dependency>"
L"    <dependentAssembly>"
L"        <assemblyIdentity"
L"            type=\"win32\""
L"            name=\"Microsoft.Windows.CPlusPlusRuntime\""
L"            version=\"7.0.0.0\""
L"            processorArchitecture=\"" LSXS_PROCESSOR_ARCHITECTURE L"\""
L"            publicKeyToken=\"6595b64144ccf1df\""
L"            language=\"*\""
L"        />"
L"    </dependentAssembly>"
L"</dependency>"
L"</assembly>"
;

void TestCRuntimeAsms()
{
    CFusionActCtxHandle WindowsCrtActCtxHandle;
    CFusionActCtxHandle ToolsCrtActCtxHandle;

    WindowsCrtActCtxHandle = ::CreateActivationContextFromStringW(WindowsCrtManifest);
    if (WindowsCrtActCtxHandle == INVALID_HANDLE_VALUE)
        ::Trace("CreateActCtx(WindowsCrtManifest %p) failed %ld\n", WindowsCrtManifest, ::GetLastError());
    ToolsCrtActCtxHandle = ::CreateActivationContextFromStringW(ToolsCrtManifest);
    if (ToolsCrtActCtxHandle == INVALID_HANDLE_VALUE)
        ::Trace("CreateActCtx(WindowsCrtManifest %p) failed %ld\n", WindowsCrtManifest, ::GetLastError());

    CFusionActCtxScope ToolsCrtActCtxScope;
    CFusionActCtxScope WindowsCrtActCtxScope;

    if (!WindowsCrtActCtxScope.Win32Activate(WindowsCrtActCtxHandle))
        ::Trace("Activate(WindowsCrtActCtxHandle %p) failed %ld\n", WindowsCrtActCtxHandle, ::GetLastError());

    if (!ToolsCrtActCtxScope.Win32Activate(ToolsCrtActCtxHandle))
        ::Trace("Activate(ToolsCrtActCtxHandle %p) failed %ld\n", ToolsCrtActCtxHandle, ::GetLastError());

    CStringBuffer MsvcrtBuffer;
    CStringBuffer AtlBuffer;

    //::SearchPathW();
}

/*
    <comInterfaceExternalProxyStub
        name="IPropertyPage"
        iid="{B196B28D-BAB4-101A-B69C-00AA00341D07}"
        proxyStubClsid32="{B196B286-BAB4-101A-B69C-00AA00341D07}"
        numMethods="14"
        baseInterface="{00000000-0000-0000-C000-000000000046}"
    >

    <comInterfaceExternalProxyStub
        name="IPropertyPage2"
        iid="{01E44665-24AC-101B-84ED-08002B2EC713}"
        proxyStubClsid32="{B196B286-BAB4-101A-B69C-00AA00341D07}"
        numMethods="15"
        baseInterface="{B196B28D-BAB4-101A-B69C-00AA00341D07}"
    >

    <comInterfaceExternalProxyStub
        name="IPropertyNotifySink"
        iid="{9BFBBC02-EFF1-101A-84ED-00AA00341D07}"
        proxyStubClsid32="{B196B286-BAB4-101A-B69C-00AA00341D07}"
        baseInterface="{00000000-0000-0000-C000-00 00 00 00 00 46}"
        numMethods="5"
    >
*/

#if 0
BOOL Win32FormatGuid(
    CBaseStringBuffer& buff
    REFGUID            guid
    )
{
    FN_PROLOG_WIN32
    IFW32FALSE_EXIT(this->Win32ResizeBuffer(64));
    IFW32FALSE_EXIT(buff.Win32Format(L"{%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x}",
        guid.Data1,
        guid.Data2,
        guid.Data3,
        guid.Data4[0], guid.Data4[1],
        guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7],
        ));
    FN_EPILOG
}
#endif

BOOL Win32Append(
    CBaseStringBuffer& s,
    PCWSTR             t
    )
{
    FN_PROLOG_WIN32
    IFW32FALSE_EXIT(s.Win32Append(t, wcslen(t)));
    FN_EPILOG
}

typedef struct _FUSIONTESTP_REG_DATA
{
#define FUSIONTESTP_REG_TYPE_INTERFACE (1)
#define FUSIONTESTP_REG_TYPE_CLASS     (2)
    ULONG  Type;
    PCWSTR Name; // for debugging/tracing purposes (should coincide with InterfaceName)
    PCWSTR Guid;
    union
    {
        struct
        {
            WCHAR  InprocServerFilePath[MAX_PATH];
            WCHAR  ThreadingModel[64];
        };
        struct
        {
            WCHAR  InterfaceName[MAX_PATH];
            WCHAR  NumMethods[64];
            WCHAR  ProxyStubClsid[64];
            //
            // These usually aren't provided.
            //
            // WCHAR BaseInterface[64];
            // WCHAR OLEViewerIViewerCLSID[64];
            //
        };
    };
#define FUSIONTESTP_REG_ROOT_CURRENT_USER  (1)
#define FUSIONTESTP_REG_ROOT_LOCAL_MACHINE (2)
#define FUSIONTESTP_REG_ROOT_CLASSES_ROOT  (3)
    ULONG  Root;

//
// It is perhaps a bit inelegant to put this data here, perhaps not..
// We are deliberately a bit sloppy on the refcounting of these right now.
//
//#define FUSIONTESTP_PLAIN_COM_POINTER(t) CSmartRef<t>
#define FUSIONTESTP_PLAIN_COM_POINTER(t) t*
//#define FUSIONTESTP_PLAIN_COM_POINTER(t) void*
    FUSIONTESTP_PLAIN_COM_POINTER(IUnknown)   CoCreatedObject;
    //FUSIONTESTP_PLAIN_COM_POINTER(IUnknown)   InterfaceIntoObjectInCreatingThread;
    //FUSIONTESTP_PLAIN_COM_POINTER(IUnknown)   InterfaceIntoObjectInAnotherThread;
    //WCHAR                               ModulePathInOtherThread[MAX_PATH]; // expected to be oleaut32.dll, but possibly already unloaded
    //IID                                 InterfaceIdOfObject;
    DWORD                               GlobalInterfaceTableCookie;
} FUSIONTESTP_REG_DATA, *PFUSIONTESTP_REG_DATA;
typedef const FUSIONTESTP_REG_DATA* PCFUSIONTESTP_REG_DATA;

#if 0
//
// In reality, all three MFC classes implement all three interfaces, but let's broaden
// our minds a bit, ok?
//
BOOL
FusionTestpDynamicallyFindAnyInterfaceIntoObjectExceptIUnknown(
    IUnknown*               Object,
    PCFUSIONTESTP_REG_DATA  RegData, // too broad of a type really, but ok
    SIZE_T                  Count,
    FUSIONTESTP_PLAIN_COM_POINTER(IUnknown)& InterfaceIntoObject,
    SIZE_T&                 InterfaceId
    )
{
    BOOL Success = FALSE;
    FN_PROLOG_WIN32(Success);
    IID LocalInterfaceId = { 0 };
    FUSIONTESTP_PLAIN_COM_POINTER(IUnknown) LocalInterfaceIntoObject = NULL;
    SIZE_T InterfaceIndex = 0;

    InterfaceIntoObject = LocalInterfaceIntoObject;
    InterfaceId = LocalInterfaceId;

    for ( LocalInterfaceIndex = 0; LocalInterfaceIndex != Count ; LocalInterfaceIndex += 1)
    {
        FUSIONTESTP_REG_DATA* const p = &RegData[LocalInterfaceIndex];

        IFCOMFAILED_EXIT(hr = Ole32.IIDFromString(const_cast<PWSTR>(p->Guid), &InterfaceId));

        if (InterfaceId == IID_IUnknown) // shouldn't happen, but easy enough to handle
            continue;

        hr = Object->QueryInterface(InterfaceId, reinterpret_cast<void**>(&LocalInterfaceIntoObject));
        if (SUCCEEDED(hr))
        {
            ::Trace("%s found interface %ls\n", __FUNCTION__, p->Name);
            InterfaceIntoObject = LocalInterfaceIntoObject;
            InterfaceId = LocalInterfaceId;
            Success = TRUE;
            goto Exit;
        }
    }
    FN_EPILOG
}
#endif

#define OLEAUT_MARSHALER_CLSID_STRING L"{B196B286-BAB4-101A-B69C-00AA00341D07}"

FUSIONTESTP_REG_DATA FusionTestpMfcRegData[] =
{
    { FUSIONTESTP_REG_TYPE_CLASS, L"Font Property Page", L"{0BE35200-8F91-11CE-9DE3-00AA004BB851}" },
    { FUSIONTESTP_REG_TYPE_CLASS, L"Color Property Page", L"{0BE35201-8F91-11CE-9DE3-00AA004BB851}" },
    { FUSIONTESTP_REG_TYPE_CLASS, L"Picture Property Page", L"{0BE35202-8F91-11CE-9DE3-00AA004BB851}" },
    { FUSIONTESTP_REG_TYPE_INTERFACE, L"IPropertyPage",  L"{B196B28D-BAB4-101A-B69C-00AA00341D07}" },
    { FUSIONTESTP_REG_TYPE_INTERFACE, L"IPropertyPage2", L"{01E44665-24AC-101B-84ED-08002B2EC713}" },
    { FUSIONTESTP_REG_TYPE_INTERFACE, L"IPropertyNotifySink", L"{9BFBBC02-EFF1-101A-84ED-00AA00341D07}" },
    // Leave this registered, since the manifest does not specify a file.
    //{ FUSIONTESTP_REG_TYPE_CLASS,     L"oleaut32 marshaller (PSFactoryBuffer)", OLEAUT_MARSHALER_CLSID_STRING }
};

FUSIONTESTP_REG_DATA FusionTestpAtlRegData[1];

const HKEY FusionTestpHkeyRoots[] = { NULL, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT };
const PCWSTR FusionTestpClassStringRoots[] = { NULL, L"Software\\Classes\\CLSID\\", L"Software\\Classes\\CLSID\\", L"CLSID\\" };
const PCWSTR FusionTestpInterfaceStringRoots[] = { NULL, L"Software\\Classes\\Interface\\", L"Software\\Classes\\Interface\\", L"Interface\\" };
const PCWSTR* FusionTestpStringRoots[] = { NULL, FusionTestpInterfaceStringRoots, FusionTestpClassStringRoots};

#define FUSIONTESTP_REG_DELETE    (1)
#define FUSIONTESTP_REG_RESTORE   (2)
#define FUSIONTESTP_REG_BACKUP    (3)

BOOL FusionTestpEnumerateRegistryData(FUSIONTESTP_REG_DATA* RegData, ULONG Count, ULONG Mode)
{
    BOOL Success = FALSE;
    FN_PROLOG_WIN32(Success);

    for (ULONG i = 0 ; i != Count ; i += 1)
    {
        FUSIONTESTP_REG_DATA* const p = &RegData[i];
        ULONG MinRoot = 0;
        ULONG MaxRoot = 0;
        switch (Mode)
        {
        case FUSIONTESTP_REG_RESTORE:
        case FUSIONTESTP_REG_DELETE:
            MinRoot = p->Root;
            if (MinRoot == 0)
                continue;
            MaxRoot = MinRoot;
            break;
        case FUSIONTESTP_REG_BACKUP:
            MinRoot = 1;
            MaxRoot = 3;
            break;
        }
        //
        // It'd be nice if you could embed the if within a switch..
        //
        for (ULONG root = MinRoot ; root <= MaxRoot ; root += 1)
        {
            CFusionRegKey regKey;
            CFusionRegKey inprocServerKey;
            CStringBuffer stringBuffer;
            CFusionRegKey numMethodsKey;
            CFusionRegKey proxyStubClsidKey;
            DWORD dwSize = 0;
            DWORD dwType = 0;

            CFusionRegKey rootKey(FusionTestpHkeyRoots[root]);

            IFW32FALSE_EXIT(Win32Append(stringBuffer, FusionTestpStringRoots[p->Type][root]));
            IFW32FALSE_EXIT(Win32Append(stringBuffer, p->Guid));
            switch (Mode)
            {
            case FUSIONTESTP_REG_DELETE:
            case FUSIONTESTP_REG_BACKUP:
                rootKey.OpenSubKey(regKey, stringBuffer);
                break;
            case FUSIONTESTP_REG_RESTORE:
                IFW32FALSE_EXIT(rootKey.OpenOrCreateSubKey(regKey, stringBuffer));
                break;
            }
            if (regKey != regKey.GetInvalidValue())
            {
                switch (Mode)
                {
                case FUSIONTESTP_REG_BACKUP:
                    p->Root = root;
                    break;
                case FUSIONTESTP_REG_DELETE:
                case FUSIONTESTP_REG_RESTORE:
                    break;
                }
                switch (p->Type)
                {
                case FUSIONTESTP_REG_TYPE_CLASS:
                    switch (Mode)
                    {
                    case FUSIONTESTP_REG_BACKUP:
#define FusionTestpQueryRegString(hkey, name, value) \
    do { dwSize = sizeof(value); \
         RegQueryValueExW(hkey, name, NULL, &dwType, reinterpret_cast<BYTE*>(value), &dwSize); \
    } while(false)
                        if (regKey.OpenSubKey(inprocServerKey, L"InprocServer32"))
                        {
                            FusionTestpQueryRegString(inprocServerKey, NULL, p->InprocServerFilePath);
                            FusionTestpQueryRegString(inprocServerKey, L"ThreadingModel", p->ThreadingModel);
                        }
                        break;
                    case FUSIONTESTP_REG_RESTORE:
                        if (regKey.OpenOrCreateSubKey(inprocServerKey, L"InprocServer32"))
                        {
#define FusionTestpRegStringSize(x) static_cast<ULONG>(((wcslen(x) + 1)*sizeof((x)[0])))
#define FusionTestpSetRegString(hkey, name, value) \
    do { if (value[0] != 0) \
            RegSetValueExW(hkey, name, NULL, REG_SZ, reinterpret_cast<const BYTE*>(value), FusionTestpRegStringSize(value)); \
    } while(false)
                            FusionTestpSetRegString(inprocServerKey, NULL, p->InprocServerFilePath);
                            FusionTestpSetRegString(inprocServerKey, L"ThreadingModel", p->ThreadingModel);
                        }
                        break;
                    case FUSIONTESTP_REG_DELETE:
                        break;
                    }
                    break;
                case FUSIONTESTP_REG_TYPE_INTERFACE:
                    switch (Mode)
                    {
                    case FUSIONTESTP_REG_BACKUP:
                        FusionTestpQueryRegString(regKey, NULL, p->InterfaceName);
                        if (regKey.OpenSubKey(numMethodsKey, L"NumMethods"))
                            FusionTestpQueryRegString(numMethodsKey, NULL, p->NumMethods);
                        if (regKey.OpenSubKey(proxyStubClsidKey, L"ProxyStubClsid32"))
                            FusionTestpQueryRegString(proxyStubClsidKey, NULL, p->ProxyStubClsid);
                        break;
                    case FUSIONTESTP_REG_RESTORE:
                        FusionTestpSetRegString(regKey, NULL, p->InterfaceName);
                        if (regKey.OpenOrCreateSubKey(numMethodsKey, L"NumMethods"))
                            FusionTestpSetRegString(numMethodsKey, NULL, p->NumMethods);
                        if (regKey.OpenOrCreateSubKey(proxyStubClsidKey, L"ProxyStubClsid32"))
                            FusionTestpSetRegString(proxyStubClsidKey, NULL, p->ProxyStubClsid);
                    case FUSIONTESTP_REG_DELETE:
                        break;
                    }
                    break;
                }
                switch (Mode)
                {
                case FUSIONTESTP_REG_DELETE:
                    regKey.DestroyKeyTree();
                    break;
                case FUSIONTESTP_REG_BACKUP:
                case FUSIONTESTP_REG_RESTORE:
                    break;
                }
                break;
            }
        }
    }
    FN_EPILOG
}

#if 0 // Use CoWaitForMultipleHandles instead, it is much safer because it only
      // pumps particular messages.
void FusionTestpPumpMessages()
{
    MSG  msg;
    BOOL bRet;
    while ((bRet = ::GetMessageW(&msg, NULL, 0, 0)) != 0)
    { 
        if (bRet == -1)
            return;
        else
        {
            ::TranslateMessage(&msg); 
            if (::IsWindowUnicode(msg.hwnd))
                ::DispatchMessageW(&msg); 
            else
                ::DispatchMessage(&msg); 
        }
    }
}
#endif

HMODULE FusionTestpHmoduleFromComObject(IUnknown* unk)
{
    void** ppv = reinterpret_cast<void**>(unk);
    void* pv = *ppv;
    MEMORY_BASIC_INFORMATION MemBasicInfo = { 0 };
    SIZE_T dw = 0;

    if ((dw = Kernel32.VirtualQuery(pv, &MemBasicInfo, sizeof(MemBasicInfo))) == 0
        || (dw < RTL_SIZEOF_THROUGH_FIELD(MEMORY_BASIC_INFORMATION, BaseAddress)))
    {
        ::Trace("VirtualQuery(%p) failed %lu\n", pv, ::GetLastError());
        return NULL;
    }
    return reinterpret_cast<HMODULE>(MemBasicInfo.AllocationBase);
}


DWORD WINAPI FusionTestpMfcCreateAndMarshalThreadMain(LPVOID pvShouldBeAbleToMarshal)
{
    BOOL Success = FALSE;
    FN_PROLOG_WIN32(Success);
    HRESULT hr = 0;
    const bool ShouldBeAbleToMarshal = (pvShouldBeAbleToMarshal != NULL ? true : false);
    Ole32.CoInitialize(NULL);

    //
    // For each interface, make sure we can unmarshal at least one object.
    //
    for (ULONG InterfaceIndex = 0 ; InterfaceIndex != NUMBER_OF(::FusionTestpMfcRegData) ; InterfaceIndex += 1)
    {
        FUSIONTESTP_REG_DATA* const pi = &::FusionTestpMfcRegData[InterfaceIndex];

        switch (pi->Type)
        {
        case FUSIONTESTP_REG_TYPE_CLASS:
            continue;
        case FUSIONTESTP_REG_TYPE_INTERFACE:
            IID InterfaceId = { 0 };
            FUSIONTESTP_PLAIN_COM_POINTER(IUnknown) InterfaceIntoObjectInAnotherThread = NULL;

            IFCOMFAILED_EXIT(hr = Ole32.IIDFromString(const_cast<PWSTR>(pi->Guid), &InterfaceId));

            // nested loop..
            for (ULONG ClassIndex = 0 ;
                ClassIndex != NUMBER_OF(::FusionTestpMfcRegData) ;
                ClassIndex += 1)
            {
                CLSID ClassId = { 0 };
                FUSIONTESTP_REG_DATA* const pc = &::FusionTestpMfcRegData[ClassIndex];

                switch (pc->Type)
                {
                case FUSIONTESTP_REG_TYPE_INTERFACE:
                    continue;
                case FUSIONTESTP_REG_TYPE_CLASS:

                    WCHAR ModulePathInOtherThread[MAX_PATH];
                    ModulePathInOtherThread[0] = 0;

                    ASSERT(pc->GlobalInterfaceTableCookie != 0);
                    IFCOMFAILED_EXIT(hr = Ole32.CLSIDFromString(const_cast<PWSTR>(pc->Guid), &ClassId));

                    hr = g.GlobalInterfaceTable->GetInterfaceFromGlobal(
                        pc->GlobalInterfaceTableCookie, InterfaceId,
                        reinterpret_cast<void**>(&InterfaceIntoObjectInAnotherThread));

                    if (SUCCEEDED(hr))
                    {
                        IFW32FALSE_EXIT(Kernel32.GetModuleFileNameW(
                            ::FusionTestpHmoduleFromComObject(InterfaceIntoObjectInAnotherThread),
                            ModulePathInOtherThread, NUMBER_OF(ModulePathInOtherThread)));
                    }
                    if (SUCCEEDED(hr) && ShouldBeAbleToMarshal)
                    {
                        Trace("%s SUCCESSfully marshaled interface %ls on class %ls using proxy/stub in %ls\n",
                            __FUNCTION__, pi->Name, pc->Name, ModulePathInOtherThread);
                        g.Successes += 1;
                    }
                    else if (SUCCEEDED(hr) && !ShouldBeAbleToMarshal)
                    {
                        // unexpected success -> ERROR

                        Trace("%s FAILED to fail to marshal interface %ls on class %ls (using proxy/stub in %ls)\n",
                            __FUNCTION__, pi->Name, pc->Name, ModulePathInOtherThread);
                        g.Failures += 1;
                    }
                    else if (FAILED(hr) && ShouldBeAbleToMarshal)
                    {
                        // keep looping, try other objects
                    }
                    else if (FAILED(hr) && !ShouldBeAbleToMarshal)
                    {
                        // keep looping, make sure none succeed
                        //::Trace("%s OK Unable to marshal interface %ls (%ls) 0x%lx (fac 0x%lx code 0x%lx)\n", __FUNCTION__, pi->Name, pi->Guid, hr, HRESULT_FACILITY(hr), HRESULT_CODE(hr));
                    }
                    break;
                }
                if (InterfaceIntoObjectInAnotherThread != NULL && ShouldBeAbleToMarshal)
                {
                    // one successful unmarshal is enough
                    break;
                }
            }
            // use the nullness of InterfaceIntoObjectInAnotherThread as a summary of the loop
            if (InterfaceIntoObjectInAnotherThread == NULL && ShouldBeAbleToMarshal)
            {
                ::Trace("%s FAILURE Unable to marshal interface %ls (%ls)\n", __FUNCTION__, pi->Name, pi->Guid);
                g.Failures += 1;
            }
            else if (InterfaceIntoObjectInAnotherThread == NULL && !ShouldBeAbleToMarshal)
            {
                ::Trace("%s GOOD Unable to marshal interface %ls without actctx as expected\n", __FUNCTION__, pi->Name);
                g.Successes += 1;
            }
            break;
        }
    }
    Ole32.CoUninitialize();
    FN_EPILOG
}

BOOL TestMfcCreateAndMarshal()
{
    BOOL Success = FALSE;
    FN_PROLOG_WIN32(Success);

    ULONG i = 0;
    HRESULT hr = 0;
    HANDLE ThreadHandle = 0;
    DWORD Ignored = 0;

    CFusionActCtxHandle ToolsCrtActCtxHandle;

    ::FusionTestpEnumerateRegistryData(::FusionTestpMfcRegData, NUMBER_OF(::FusionTestpMfcRegData), FUSIONTESTP_REG_BACKUP);
    ::FusionTestpEnumerateRegistryData(::FusionTestpMfcRegData, NUMBER_OF(::FusionTestpMfcRegData), FUSIONTESTP_REG_DELETE);

    Ole32.CoInitialize(NULL);

    //
    // Verify that we cannot create any of the classes.
    //
    for (i = 0 ; i != NUMBER_OF(::FusionTestpMfcRegData) ; i += 1)
    {
        CSmartRef<IUnknown> unk;
        CLSID ClassId = { 0 };

        FUSIONTESTP_REG_DATA* const p = &::FusionTestpMfcRegData[i];
        switch (p->Type)
        {
        case FUSIONTESTP_REG_TYPE_INTERFACE:
            break;
        case FUSIONTESTP_REG_TYPE_CLASS:
            IFCOMFAILED_EXIT(hr = Ole32.CLSIDFromString(const_cast<PWSTR>(p->Guid), &ClassId));
            hr = Ole32.CoCreateInstance(ClassId, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, reinterpret_cast<void**>(&unk));
            if (SUCCEEDED(hr))
            {
                ::Trace("%s BAD, no registry, no actctx CoCreate(%ls) SUCCEEDED, not expected\n", __FUNCTION__, p->Name);
                g.Failures += 1;
            }
            else
            {
                ::Trace("%s GOOD, no registry, no actctx CoCreate(%ls) FAILed 0x%lx, as expected\n", __FUNCTION__, p->Name, hr);
                g.Successes += 1;
            }
            break;
        }
    }

    //
    // Create and activate the context.
    //

    ToolsCrtActCtxHandle = ::CreateActivationContextFromStringW(ToolsCrtManifest);
    if (ToolsCrtActCtxHandle == INVALID_HANDLE_VALUE)
        ::Trace("CreateActCtx(WindowsCrtManifest %p) failed %ld\n", WindowsCrtManifest, ::GetLastError());

    {
        CFusionActCtxScope ToolsCrtActCtxScope;
        if (!ToolsCrtActCtxScope.Win32Activate(ToolsCrtActCtxHandle))
            ::Trace("Activate(ToolsCrtActCtxHandle %p) failed %ld\n", ToolsCrtActCtxHandle, ::GetLastError());

        //
        // Now create each class and print the .dll it came from.
        // And put it in the global interface table for later unmarshalling.
        //

        IFCOMFAILED_EXIT(hr = Ole32.CoCreateInstance(CLSID_StdGlobalInterfaceTable,NULL, CLSCTX_INPROC_SERVER,
            IID_IGlobalInterfaceTable, reinterpret_cast<void**>(&g.GlobalInterfaceTable)));

        for (i = 0 ; i != NUMBER_OF(::FusionTestpMfcRegData)  ; i += 1)
        {
            CLSID ClassId = { 0 };

            FUSIONTESTP_REG_DATA* const p = &::FusionTestpMfcRegData[i];

            //
            // We are not supposed to be able to cocreate this here.
            //
            if (FusionpStrCmpI(p->Guid, OLEAUT_MARSHALER_CLSID_STRING) == 0)
                continue;

            switch (p->Type)
            {
            case FUSIONTESTP_REG_TYPE_INTERFACE:
                break;
            case FUSIONTESTP_REG_TYPE_CLASS:
                IFCOMFAILED_EXIT(hr = Ole32.CLSIDFromString(const_cast<PWSTR>(p->Guid), &ClassId));
                hr = Ole32.CoCreateInstance(ClassId, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,
                    reinterpret_cast<void**>(&p->CoCreatedObject));
                if (FAILED(hr))
                {
                    Trace("%s Failure: CoCreate(%ls) FAILED\n", __FUNCTION__, p->Name);
                    g.Failures += 1;
                }
                else
                {
                    WCHAR ComObjectModule[MAX_PATH];
                    ComObjectModule[0] = 0;
                
                    IFW32FALSE_EXIT(Kernel32.GetModuleFileNameW(
                        ::FusionTestpHmoduleFromComObject(p->CoCreatedObject), ComObjectModule, NUMBER_OF(ComObjectModule)));
                    Trace("%s SUCCESSfully cocreated %p of type %ls from %ls with actctx influence\n",
                        __FUNCTION__, p->CoCreatedObject, p->Name, ComObjectModule);

                    g.Successes += 1;

#if 0
                    //
                    // Put it in the global interface table for later marshalling.
                    // It does not make sense to use IUnknown here, because ole32 can always
                    // marshal IUnknown itself without any proxy/stubs found via the
                    // registry or fusion (see also my special message pumper in vsee that
                    // takes advantage of this feature...)
                    //
                    IFW32FALSE_EXIT(
                        FusionTestpDynamicallyFindAnyInterfaceIntoObjectExceptIUnknown(
                            p->CoCreatedObject,
                            ::FusionTestpMfcRegData,
                            RTL_NUMBER_OF(::FusionTestpMfcRegData),
                            p->InterfaceIntoObjectInCreatingThread,
                            p->InterfaceIdOfObject
                            ));

                    IFCOMFAILED_EXIT(hr = g.GlobalInterfaceTable->RegisterInterfaceInGlobal(
                        p->InterfaceIntoObjectInCreatingThread,
                        p->InterfaceIdOfObject,
                        &p->GlobalInterfaceTableCookie
                        ));
#else
                    //
                    // It'll still have to look for the proxy/stub at unmarshal time. This is fine.
                    //
                    IFCOMFAILED_EXIT(hr = g.GlobalInterfaceTable->RegisterInterfaceInGlobal(
                        p->CoCreatedObject,
                        IID_IUnknown,
                        &p->GlobalInterfaceTableCookie
                        ));
#endif
                }
                break;
            }
        }
    }

    {
        CFusionActCtxScope ToolsCrtActCtxScope;
        if (!ToolsCrtActCtxScope.Win32Activate(ToolsCrtActCtxHandle))
            ::Trace("Activate(ToolsCrtActCtxHandle %p) failed %ld\n", ToolsCrtActCtxHandle, ::GetLastError());
        //
        // try marshalling with the actctx activated, it should work (not NULL => expected success==TRUE)
        //
        ThreadHandle = CreateThread(NULL, 0, FusionTestpMfcCreateAndMarshalThreadMain, &Ignored, 0, &Ignored);
        CoWaitForMultipleHandles(0, INFINITE, 1, &ThreadHandle, &Ignored);
        CloseHandle(ThreadHandle);
    }

#if 0
    //
    // If interfaces are _really_never_ changed, (but doesn't Fusion let you violate that?),
    // this test case isn't valid. COM does caching and prefills caches when you may not expect
    // (like when you put stuff in the GlobalInterfaceTable), and it does not invalidate its caches
    // because of actctx pushes and pops.
    //
    {
        //
        // try marshalling without the actctx activated, it should NOT work (NULL => expected success==FALSE)
        //
        ThreadHandle = CreateThread(NULL, 0, FusionTestpMfcCreateAndMarshalThreadMain, NULL, 0, &Ignored);
        CoWaitForMultipleHandles(0, INFINITE, 1, &ThreadHandle, &Ignored);
        CloseHandle(ThreadHandle);
    }
#endif


    Ole32.CoUninitialize();

    //::FusionTestpEnumerateRegistryData(::FusionTestpMfcRegData, NUMBER_OF(::FusionTestpMfcRegData), FUSIONTESTP_REG_RESTORE);

    FN_EPILOG
}


void TestAtlCreate()
{
    ::FusionTestpEnumerateRegistryData(FusionTestpAtlRegData, NUMBER_OF(FusionTestpAtlRegData), FUSIONTESTP_REG_BACKUP);
    ::FusionTestpEnumerateRegistryData(FusionTestpAtlRegData, NUMBER_OF(FusionTestpAtlRegData), FUSIONTESTP_REG_DELETE);

    ::FusionTestpEnumerateRegistryData(FusionTestpAtlRegData, NUMBER_OF(FusionTestpAtlRegData), FUSIONTESTP_REG_RESTORE);
}

BOOL TestPrivateSha1Impl(
    PCWSTR pcwszDirName
    )
{
    FN_PROLOG_WIN32
    CFusionArray<BYTE> rgbShaState;
    CFusionFile ffInputFile;
    BYTE bChunkletBuffer[4096];
    CSmallStringBuffer sbHashedString;
    DWORD dwReadData;
    HCRYPTPROV hProvider;
    HCRYPTHASH hHash;
    CFusionHash fHashObject;

    IFW32FALSE_EXIT(fHashObject.Win32Initialize(CALG_SHA1));
    IFW32FALSE_EXIT(ffInputFile.Win32CreateFile(
        pcwszDirName,
        GENERIC_READ,
        FILE_SHARE_READ,
        OPEN_EXISTING));

    IFW32FALSE_EXIT(CryptAcquireContextW(
        &hProvider, 
        NULL, 
        NULL, 
        PROV_RSA_FULL, 
        CRYPT_SILENT | CRYPT_VERIFYCONTEXT ));

    IFW32FALSE_EXIT(CryptCreateHash(
        hProvider, 
        CALG_SHA1, 
        0, 
        0, 
        &hHash ));

    do
    {
        IFW32FALSE_EXIT(::ReadFile(
            ffInputFile, 
            bChunkletBuffer, 
            sizeof(bChunkletBuffer), 
            &dwReadData, 
            NULL));
        IFW32FALSE_EXIT(fHashObject.Win32HashData(bChunkletBuffer, dwReadData));
        IFW32FALSE_EXIT(CryptHashData(hHash, bChunkletBuffer, dwReadData, 0));
    }
    while ( dwReadData != 0 );

    IFW32FALSE_EXIT(fHashObject.Win32GetValue(rgbShaState));

    wprintf(L"%ls (private) hashed to ", pcwszDirName);
    for ( SIZE_T n = 0; n < rgbShaState.GetSize(); n++ )
    {
        wprintf(L"%02x", rgbShaState[n]);
    }


    IFW32FALSE_EXIT(CryptGetHashParam(
        hHash, 
        HP_HASHVAL, 
        bChunkletBuffer, 
        &(dwReadData = sizeof(bChunkletBuffer)),
        0));

    wprintf(L"\n%ls (regular) hashed to ", pcwszDirName);
    for ( SIZE_T n = 0; n < dwReadData; n++ )
    {
        wprintf(L"%02x", bChunkletBuffer[n]);
    }
    
    IFW32FALSE_EXIT(CryptReleaseContext(hProvider, 0));

    FN_EPILOG
}

void TestAlignment()
{
    CCleanupBase* p = reinterpret_cast<CCleanupBase*>(ULONG_PTR(0xffff0000));
    SINGLE_LIST_ENTRY* q = p;

    printf("%p %Ix\n", q, ULONG_PTR(q) % 16);
}

void TestCreateActCtx_PE_flags0()
{
    WCHAR SyssetupDll[MAX_PATH * 2];
    ACTCTXW ActCtx = {sizeof(ActCtx)};
    CFusionActCtxHandle ActCtxHandle;

    GetSystemDirectoryW(SyssetupDll, MAX_PATH);
    wcscat(SyssetupDll, L"\\syssetup.dll");
    ActCtx.lpSource = SyssetupDll;

    printf("%s\n", ActCtxHandle.Win32Create(&ActCtx) ? "true" : "false");
}

void
TestUninstall(
    PCWSTR ManifestPath,
    PCWSTR ReferenceString
    )
{
    SXS_UNINSTALLW UninstallParameters = {sizeof(UninstallParameters)};
    SXS_INSTALL_REFERENCEW Reference = {sizeof(Reference)};
    PSXS_UNINSTALL_W_ROUTINE Uninstall = NULL;
    PSXS_QUERY_MANIFEST_INFORMATION QueryManifestInformation;
    BYTE ManifestInformationBuffer[1UL << 16];
    PSXS_MANIFEST_INFORMATION_BASIC ManifestBasicInfo = reinterpret_cast<PSXS_MANIFEST_INFORMATION_BASIC>(&ManifestInformationBuffer);
    DWORD Disposition = 0;
    BOOL  Success = FALSE;

    LoadSxs();
    GetSxsProc("SxsUninstallW", &Uninstall);
    GetSxsProc("SxsQueryManifestInformation", &QueryManifestInformation);

    Success = QueryManifestInformation(0, ManifestPath,
                SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC, 0, sizeof(ManifestInformationBuffer),
                ManifestBasicInfo, NULL);

    printf("QueryManifestInformation(%ls)->(%ls, %ls)\n", ManifestPath, ManifestBasicInfo->lpIdentity, ManifestBasicInfo->lpShortName);

    UninstallParameters.dwFlags |= SXS_UNINSTALL_FLAG_REFERENCE_VALID;
    UninstallParameters.lpInstallReference = &Reference;
    UninstallParameters.lpAssemblyIdentity = ManifestBasicInfo->lpIdentity;

    Reference.lpIdentifier = ReferenceString;
    Reference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OPAQUESTRING;

    Success = Uninstall(&UninstallParameters, &Disposition);

    printf("TestUninstall(%ls, %ls) : %s, 0x%lx\n", ManifestPath, ReferenceString, Success ? "true" : "false", Disposition);
}

BOOL 
TestNewSxsInstallAPI(
    PCWSTR pcwszManifest
)
{
    BOOL fSuccess = FALSE;
    PSXS_INSTALL_W sxsInstallW = NULL;
    PSXS_UNINSTALL_ASSEMBLYW sxsUninstallW = NULL;
    SXS_INSTALLW Info = {sizeof(Info)};
    SXS_INSTALL_REFERENCEW Reference = {sizeof(Reference)};
    SXS_UNINSTALLW Uninstall = {sizeof(Uninstall)};
    DWORD dwDisposition;

    Info.dwFlags = SXS_INSTALL_FLAG_REPLACE_EXISTING |        
        SXS_INSTALL_FLAG_REFERENCE_VALID |
        SXS_INSTALL_FLAG_CODEBASE_URL_VALID |
        SXS_INSTALL_FLAG_LOG_FILE_NAME_VALID;
    Info.lpManifestPath = pcwszManifest;
    Info.lpCodebaseURL = Info.lpManifestPath;
    Info.lpReference = &Reference;
    Info.lpLogFileName = L"c:\\thelogfile";

    DWORD dwAttribute = GetFileAttributesW(pcwszManifest);
    if ( dwAttribute == 0xffffffff)  // non-exist
        goto Exit;
    if ( dwAttribute & FILE_ATTRIBUTE_DIRECTORY) // install from a directory recursively
    {
        Info.dwFlags |= SXS_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE;
    }

    Reference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OPAQUESTRING;
    Reference.lpIdentifier = L"Sxs installation";

    LoadSxs();
    GetSxsProc(SXS_INSTALL_W, &sxsInstallW);
    GetSxsProc(SXS_UNINSTALL_ASSEMBLYW, &sxsUninstallW);

    if (!(*sxsInstallW)(&Info))
    {
        goto Exit;
    }

    Uninstall.dwFlags = SXS_UNINSTALL_FLAG_USE_INSTALL_LOG;
    Uninstall.lpInstallLogFile = L"c:\\thelogfile";
    
    if (!(*sxsUninstallW)(&Uninstall, &dwDisposition))
    {
        goto Exit;
    }

    fSuccess = TRUE;
Exit:
    if (!fSuccess)
    {
        fprintf(stderr, "%s failed!\n", __FUNCTION__);
        return EXIT_FAILURE;
    }
    else
        return EXIT_SUCCESS;
}


VOID
NTAPI
SimpleContextNotification(
    IN ULONG NotificationType,
    IN PACTIVATION_CONTEXT ActivationContext,
    IN const VOID *ActivationContextData,
    IN PVOID NotificationContext,
    IN PVOID NotificationData,
    IN OUT PBOOLEAN DisableNotification
    )
{
    switch (NotificationType)
    {
    case ACTIVATION_CONTEXT_NOTIFICATION_DESTROY:
        RTL_SOFT_VERIFY(NT_SUCCESS(NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) ActivationContextData)));
        break;

    default:
        // Otherwise, we don't need to see this notification ever again.
        *DisableNotification = TRUE;
        break;
    }
}




BOOL
MakeActCtxFromCurrentSxsDll(
    PCWSTR pcwszFileName,
    HANDLE *phActCtx
    )
{
    FN_PROLOG_WIN32;

    SXS_GENERATE_ACTIVATION_CONTEXT_PARAMETERS Parameters = {0};
    PACTIVATION_CONTEXT pContextCreated = NULL;
    CStringBuffer   AssemblyDirectory;
    CFileStream     SourceManifestStream;
    PVOID           pvMappedSection = NULL;
    NTSTATUS status;
    BOOL (WINAPI *pfnSxsGenerateActivationContext)(PSXS_GENERATE_ACTIVATION_CONTEXT_PARAMETERS Parameters);

    *phActCtx = INVALID_HANDLE_VALUE;

    LoadSxs();
    GetSxsProc("SxsGenerateActivationContext", &pfnSxsGenerateActivationContext);

    IFW32FALSE_EXIT(SourceManifestStream.OpenForRead(pcwszFileName, CImpersonationData(), FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN));
    Parameters.Manifest.Path = pcwszFileName;
    Parameters.Manifest.PathType = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE;
    Parameters.Manifest.Stream = &SourceManifestStream;

    IFW32FALSE_EXIT(AssemblyDirectory.Win32Assign(pcwszFileName, ::wcslen(pcwszFileName)));
    AssemblyDirectory.RemoveLastPathElement();
    Parameters.AssemblyDirectory = AssemblyDirectory;
    Parameters.ProcessorArchitecture = 0;
    Parameters.LangId = GetUserDefaultUILanguage();

    IFW32FALSE_EXIT(pfnSxsGenerateActivationContext(&Parameters));
    IFW32NULL_EXIT(pvMappedSection = MapViewOfFile(Parameters.SectionObjectHandle, FILE_MAP_READ, 0, 0, 0));

    status = RtlCreateActivationContext(
        0,
        pvMappedSection,
        0,
        SimpleContextNotification,
        NULL,
        &pContextCreated);

    if (!NT_SUCCESS(status)) {
        ORIGINATE_WIN32_FAILURE_AND_EXIT(RtlCreateActivationContext, RtlNtStatusToDosError(status));
    }

    *phActCtx = pContextCreated;

    FN_EPILOG;
}
    




BOOL SimpleTestFindAndUseSurrogateInformation(PCWSTR filename, PCWSTR GuidToDisplay)
{
    FN_PROLOG_WIN32;
    SXS_GENERATE_ACTIVATION_CONTEXT_PARAMETERS Parameters = {0};
    BOOL (WINAPI *pfnSxsGenerateActivationContext)(PSXS_GENERATE_ACTIVATION_CONTEXT_PARAMETERS Parameters);
    CFileStream         SourceManifestStream;
    CStringBuffer       AssemblyDirectory;
    BOOL fCreatedOk = false;
    GUID GuidToFind;
    
    LoadSxs();
    GetSxsProc("SxsGenerateActivationContext", &pfnSxsGenerateActivationContext);

    IFW32FALSE_EXIT(SxspParseGUID(GuidToDisplay, ::wcslen(GuidToDisplay), GuidToFind));

    IFW32FALSE_EXIT(SourceManifestStream.OpenForRead(filename, CImpersonationData(), FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN));
    Parameters.Manifest.Path = filename;
    Parameters.Manifest.PathType = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE;
    Parameters.Manifest.Stream = &SourceManifestStream;

    IFW32FALSE_EXIT(AssemblyDirectory.Win32Assign(filename, ::wcslen(filename)));
    AssemblyDirectory.RemoveLastPathElement();
    Parameters.AssemblyDirectory = AssemblyDirectory;
    Parameters.ProcessorArchitecture = 0;
    Parameters.LangId = GetUserDefaultUILanguage();

    fCreatedOk = pfnSxsGenerateActivationContext(&Parameters);

    //
    // Spiffy, we've got the thing.
    //
    if (fCreatedOk && (Parameters.SectionObjectHandle != INVALID_HANDLE_VALUE) && (Parameters.SectionObjectHandle != NULL))
    {
        PACTIVATION_CONTEXT pActivationContext = NULL;
        NTSTATUS status;
        ULONG_PTR ulCookie;
        ACTCTX_SECTION_KEYED_DATA Data = { sizeof(Data) };
        // PCACTIVATION_CONTEXT_DATA_CLR_SURROGATE pFoundData;
        /*
        status = RtlCreateActivationContext(
            0,
            Parameters.SectionObjectHandle,
            0,
            SimpleContextNotification,
            NULL,
            &pActivationContext);
        */
        if (ActivateActCtx(static_cast<HANDLE>(pActivationContext), &ulCookie))
        {
            status = FindActCtxSectionGuid(
                0,
                NULL,
                ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES,
                &GuidToFind,
                &Data);
            DeactivateActCtx(0, ulCookie);
            ReleaseActCtx(static_cast<HANDLE>(pActivationContext));
        }
    }

    FN_EPILOG
}

void TestFindAndUseSurrogateInformation(PCWSTR pcwsz, PCWSTR GuidToDisplay)
{
    ACTCTXW actctx = { sizeof(actctx) };
    HANDLE hActCtx = INVALID_HANDLE_VALUE;
    GUID GuidToFind;
    ACTCTX_SECTION_KEYED_DATA Data = { sizeof(Data) };
    PCACTIVATION_CONTEXT_DATA_CLR_SURROGATE pFoundData;
    BOOL status;
    ULONG_PTR ulCookie;

    SxspParseGUID(GuidToDisplay, wcslen(GuidToDisplay), GuidToFind);

    actctx.lpSource = pcwsz;
    hActCtx = CreateActCtxW(&actctx);

    if ((hActCtx == INVALID_HANDLE_VALUE) || (hActCtx == NULL))
    {
        printf("Sorry, can't find file %ls, error %d", pcwsz, ::GetLastError());
        return;
    }

    ActivateActCtx(hActCtx, &ulCookie);
    __try {
        status = FindActCtxSectionGuid(
            0, 
            NULL, 
            ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES,
            &GuidToFind, 
            &Data);

        pFoundData = (PCACTIVATION_CONTEXT_DATA_CLR_SURROGATE)Data.lpData;

        wprintf(L"The guid %ls maps to assembly '%.*ls', runtime version '%.*ls', module '%.*ls'\n",
            GuidToDisplay,
            pFoundData->TypeNameLength / 2,
            (PCWSTR)(((PBYTE)pFoundData) + pFoundData->TypeNameOffset),
            pFoundData->VersionLength / 2,
            (PCWSTR)(((PBYTE)pFoundData) + pFoundData->VersionOffset));

    }
    __finally
    {
        if (ulCookie != 0)
            DeactivateActCtx(0, ulCookie);
    }

    ReleaseActCtx(hActCtx);

}



BOOL
TestSxsExportedSurrogateStuff(
    PCWSTR pcwszManifest,
    PCWSTR pcwszWhat,
    PCWSTR pcwszData
    )
{
    FN_PROLOG_WIN32;
    
    PFN_SXS_FIND_CLR_CLASS_INFO pfnClrClass = NULL;
    PFN_SXS_FIND_CLR_SURROGATE_INFO pfnClrSurrogate = NULL;
    PFN_SXS_LOOKUP_CLR_GUID pfnClrLookup = NULL;
    SIZE_T cbRequired = 0;
    BOOL f = false;
    PVOID pvTargetBuffer = NULL;
    PCSXS_CLR_SURROGATE_INFORMATION pSurrogateInfo = NULL;
    PCSXS_CLR_CLASS_INFORMATION pClassInfo = NULL;
    CFusionActCtxHandle hActCtxCreated;
    CFusionActCtxScope ActivationScope;
    ACTCTXW ActCtxStruct = {sizeof(ActCtxStruct)};

    LoadSxs();
    GetSxsProc(SXS_FIND_CLR_SURROGATE_INFO, &pfnClrSurrogate);
    GetSxsProc(SXS_FIND_CLR_CLASS_INFO, &pfnClrClass);
    GetSxsProc(SXS_LOOKUP_CLR_GUID, &pfnClrLookup);

    IFW32FALSE_EXIT(MakeActCtxFromCurrentSxsDll(pcwszManifest, &hActCtxCreated));
    IFW32FALSE_EXIT(ActivationScope.Win32Activate(hActCtxCreated));

    if (lstrcmpiW(pcwszWhat, L"clrprogid") == 0)
    {
        f = pfnClrClass(
            SXS_FIND_CLR_CLASS_SEARCH_PROGID | SXS_FIND_CLR_CLASS_GET_ALL,
            (PVOID)pcwszData,
            hActCtxCreated,
            NULL, 0, &cbRequired);

        if (!f) 
        {
            SIZE_T cbWritten = 0;
            pvTargetBuffer = HeapAlloc(GetProcessHeap(), 0, cbRequired);
            pfnClrClass(
                SXS_FIND_CLR_CLASS_SEARCH_PROGID | SXS_FIND_CLR_CLASS_GET_ALL,
                (PVOID)pcwszData,
                hActCtxCreated,
                pvTargetBuffer,
                cbRequired,
                &cbWritten);

            pClassInfo = (PCSXS_CLR_CLASS_INFORMATION)pvTargetBuffer;
        }
    }
    else if (lstrcmpiW(pcwszWhat, L"clrguid") == 0)
    {
        GUID ParsedGuid;
        IFW32FALSE_EXIT(SxspParseGUID(pcwszData, wcslen(pcwszData), ParsedGuid));

        f = pfnClrClass(
            SXS_FIND_CLR_CLASS_SEARCH_GUID | SXS_FIND_CLR_CLASS_GET_ALL,
            (PVOID)&ParsedGuid,
            hActCtxCreated,
            NULL, 0, &cbRequired);

        if (!f)
        {
            SIZE_T cbWritten = 0;
            pvTargetBuffer = HeapAlloc(GetProcessHeap(), 0, cbRequired);
            pfnClrClass(
                SXS_FIND_CLR_CLASS_SEARCH_GUID | SXS_FIND_CLR_CLASS_GET_ALL,
                (PVOID)&ParsedGuid,
                hActCtxCreated,
                pvTargetBuffer, cbRequired, &cbWritten);

            pClassInfo = (PCSXS_CLR_CLASS_INFORMATION)pvTargetBuffer;
        }
        
    }
    else if (lstrcmpiW(pcwszWhat, L"lookup") == 0)
    {
        GUID ParsedGuid;
        PCSXS_GUID_INFORMATION_CLR pGuidInfo = NULL;

        IFW32FALSE_EXIT(SxspParseGUID(pcwszData, wcslen(pcwszData), ParsedGuid));

        f = pfnClrLookup(
            SXS_LOOKUP_CLR_GUID_FIND_ANY, 
            &ParsedGuid,
            hActCtxCreated, 
            NULL, 
            0, 
            &cbRequired);

        if (!f)
        {
            SIZE_T cbWritten = 0;
            pvTargetBuffer = HeapAlloc(GetProcessHeap(), 0, cbRequired);
            
            f = pfnClrLookup(
                SXS_LOOKUP_CLR_GUID_FIND_ANY,
                &ParsedGuid,
                hActCtxCreated, 
                pvTargetBuffer, 
                cbRequired, 
                &cbWritten);

            pGuidInfo = (PCSXS_GUID_INFORMATION_CLR)pvTargetBuffer;
        }
    }
    else if (lstrcmpiW(pcwszWhat, L"surrogate") == 0)
    {
        GUID ParsedGuid;

        IFW32FALSE_EXIT(SxspParseGUID(pcwszData, wcslen(pcwszData), ParsedGuid));

        f = pfnClrSurrogate(
            SXS_FIND_CLR_SURROGATE_USE_ACTCTX | SXS_FIND_CLR_SURROGATE_GET_ALL,
            &ParsedGuid,
            hActCtxCreated,
            NULL, 0, &cbRequired);

        if (!f)
        {
            SIZE_T cbWritten = 0;
            pvTargetBuffer = HeapAlloc(GetProcessHeap(), 0, cbRequired);
            pfnClrSurrogate(
                SXS_FIND_CLR_SURROGATE_USE_ACTCTX | SXS_FIND_CLR_SURROGATE_GET_ALL,
                &ParsedGuid,
                hActCtxCreated,
                pvTargetBuffer, cbRequired, &cbWritten);

            pSurrogateInfo = (PCSXS_CLR_SURROGATE_INFORMATION)pvTargetBuffer;
        }        
    }

    FN_EPILOG;    
}