/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    comclass.cpp

Abstract:

    Activation context section contributor for COM servers.

Author:

    Michael J. Grier (MGrier) 23-Feb-2000

Revision History:

--*/

#include "stdinc.h"
#include <windows.h>
#include "sxsp.h"

DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(clsid);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(iid);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(name);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(progid);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(proxyStubClsid32);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(runtimeVersion);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(threadingModel);
DECLARE_STD_ATTRIBUTE_NAME_DESCRIPTOR(tlbid);

#define ALLOCATE_BUFFER_SPACE(_bytesNeeded, _bufferCursor, _bytesLeft, _bytesWritten, _typeName, _ptr) \
do { \
    if ((_bytesLeft) < (_bytesNeeded)) \
        ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER); \
    (_bytesLeft) -= (_bytesNeeded); \
    (_bytesWritten) += (_bytesNeeded); \
    (_ptr) = (_typeName)(_bufferCursor); \
    (_bufferCursor) = (PVOID) (((ULONG_PTR) (_bufferCursor)) + (_bytesNeeded)); \
} while (0)

#define ALLOCATE_BUFFER_SPACE_TYPE(_typeName, _bufferCursor, _bytesLeft, _bytesWritten, _ptr) \
    ALLOCATE_BUFFER_SPACE(sizeof(_typeName), _bufferCursor, _bytesLeft, _bytesWritten, _typeName *, _ptr)


typedef struct _COM_GLOBAL_CONTEXT *PCOM_GLOBAL_CONTEXT;
typedef struct _COM_FILE_CONTEXT *PCOM_FILE_CONTEXT;
typedef struct _COM_SERVER_ENTRY *PCOM_SERVER_ENTRY;

typedef struct _COM_GLOBAL_CONTEXT
{
    _COM_GLOBAL_CONTEXT() { }

    // Temporary holding buffer for the filename until the first COM server entry is
    // found, at which time a COM_FILE_CONTEXT is allocated and the filename moved to it.
    CSmallStringBuffer m_FileNameBuffer;
    PCOM_FILE_CONTEXT m_FileContextListHead;
    ULONG m_FileContextListCount;
    CTinyStringBuffer m_FirstShimNameBuffer;
    ULONG m_FirstShimNameOffset;
    ULONG m_FirstShimNameLength;

    // When the first clrClass entry is created, its file context is written here for
    // easy access in the future.  It will exist in the normal list of files as well,
    // however, and will get cleaned up when the file list goes away.
    PCOM_FILE_CONTEXT m_MscoreeFileContext;

private:
    _COM_GLOBAL_CONTEXT(const _COM_GLOBAL_CONTEXT &);
    void operator =(const _COM_GLOBAL_CONTEXT &);
} COM_GLOBAL_CONTEXT;

typedef struct _COM_FILE_CONTEXT
{
public:
    _COM_FILE_CONTEXT() { }

    PCOM_FILE_CONTEXT m_Next;
    CSmallStringBuffer m_FileNameBuffer;
    PCOM_SERVER_ENTRY m_ServerListHead;
    ULONG m_ServerListCount;
    ULONG m_Offset; // populated during section generation
    PCOM_GLOBAL_CONTEXT m_GlobalContext;

private:
    _COM_FILE_CONTEXT(const _COM_FILE_CONTEXT &);
    void operator =(const _COM_FILE_CONTEXT &);
} COM_FILE_CONTEXT;

typedef struct _COM_SERVER_ENTRY
{
public:
    _COM_SERVER_ENTRY() { }

    PCOM_SERVER_ENTRY m_Next;
    PCOM_FILE_CONTEXT m_FileContext;
    GUID m_ReferenceClsid;
    GUID m_ConfiguredClsid;
    GUID m_ImplementedClsid;
    GUID m_TypeLibraryId;
    ULONG m_ThreadingModel;
    CSmallStringBuffer m_ProgIdBuffer;
    CTinyStringBuffer m_TypeNameBuffer;
    CTinyStringBuffer m_ShimNameBuffer;
    CTinyStringBuffer m_RuntimeVersionBuffer;
    ULONG m_ShimType;
    bool m_IsFirstShim;
private:
    _COM_SERVER_ENTRY(const _COM_SERVER_ENTRY &);
    void operator =(const _COM_SERVER_ENTRY &);
} COM_SERVER_ENTRY;

VOID
WINAPI
SxspComClassRedirectionContributorCallback(
    PACTCTXCTB_CALLBACK_DATA Data
    )
{
    FN_TRACE();

    PGUID_SECTION_GENERATION_CONTEXT GSGenContext = (PGUID_SECTION_GENERATION_CONTEXT) Data->Header.ActCtxGenContext;
    PCOM_GLOBAL_CONTEXT ComGlobalContext = NULL;

    if (GSGenContext != NULL)
        ComGlobalContext = (PCOM_GLOBAL_CONTEXT) ::SxsGetGuidSectionGenerationContextCallbackContext(GSGenContext);

    switch (Data->Header.Reason)
    {
    case ACTCTXCTB_CBREASON_ACTCTXGENBEGINNING:
        Data->GenBeginning.Success = FALSE;

        INTERNAL_ERROR_CHECK(ComGlobalContext == NULL);
        INTERNAL_ERROR_CHECK(GSGenContext == NULL);

        IFALLOCFAILED_EXIT(ComGlobalContext = new COM_GLOBAL_CONTEXT);

        ComGlobalContext->m_FileContextListHead = NULL;
        ComGlobalContext->m_FileContextListCount = 0;
        ComGlobalContext->m_FirstShimNameOffset = 0;
        ComGlobalContext->m_FirstShimNameLength = 0;
        ComGlobalContext->m_MscoreeFileContext = NULL;

        if (!::SxsInitGuidSectionGenerationContext(
                &GSGenContext,
                ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_FORMAT_WHISTLER,
                &::SxspComClassRedirectionGuidSectionGenerationCallback,
                ComGlobalContext))
        {
            FUSION_DELETE_SINGLETON(ComGlobalContext);
            goto Exit;
        }

        Data->Header.ActCtxGenContext = GSGenContext;
        Data->GenBeginning.Success = TRUE;

        break;

    case ACTCTXCTB_CBREASON_ACTCTXGENENDED:
        if (GSGenContext != NULL)
            ::SxsDestroyGuidSectionGenerationContext(GSGenContext);

        FUSION_DELETE_SINGLETON(ComGlobalContext);
        break;

    case ACTCTXCTB_CBREASON_ALLPARSINGDONE:
        Data->AllParsingDone.Success = FALSE;

#if 0 // guid section optimiziation not yet implemented
        if ((GSGenContext != NULL) && !::SxsDoneModifyingGuidSectionGenerationContext(GSGenContext))
            goto Exit;
#endif

        Data->AllParsingDone.Success = TRUE;
        break;

    case ACTCTXCTB_CBREASON_GETSECTIONSIZE:
        Data->GetSectionSize.Success = FALSE;
        // Someone shouldn't be asking for the section size if this is a parse-only
        // run.  These two asserts should be equivalent...
        INTERNAL_ERROR_CHECK(Data->Header.ManifestOperation == MANIFEST_OPERATION_GENERATE_ACTIVATION_CONTEXT);
        INTERNAL_ERROR_CHECK(GSGenContext != NULL);
        IFW32FALSE_EXIT(::SxsGetGuidSectionGenerationContextSectionSize(GSGenContext, &Data->GetSectionSize.SectionSize));
        Data->GetSectionSize.Success = TRUE;
        break;

    case ACTCTXCTB_CBREASON_ELEMENTPARSED:
        Data->ElementParsed.Success = FALSE;

        if ((Data->ElementParsed.ParseContext->XMLElementDepth == 2) &&
            (::FusionpCompareStrings(
                    Data->ElementParsed.ParseContext->ElementPath,
                    Data->ElementParsed.ParseContext->ElementPathCch,
                    L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file",
                    NUMBER_OF(L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file") - 1,
                    false) == 0))
        {
            CStringBuffer FileNameBuffer;
            bool fFound = false;
            SIZE_T cbBytesWritten = 0;

            // capture the name of the file
            IFW32FALSE_EXIT(::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_name,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(FileNameBuffer),
                    &FileNameBuffer,
                    cbBytesWritten,
                    NULL,
                    NULL));

            // If there's no NAME attribute, someone else will puke; we'll handle it
            // gracefully.
            if (fFound || (FileNameBuffer.Cch() == 0))
            {
                INTERNAL_ERROR_CHECK2(
                    ComGlobalContext != NULL,
                    "Window class context NULL while processing file element's name attribute.");

                IFW32FALSE_EXIT(ComGlobalContext->m_FileNameBuffer.Win32Assign(FileNameBuffer));
            }
        }
        else if (
            (Data->ElementParsed.ParseContext->XMLElementDepth == 3) &&
            (::FusionpCompareStrings(
                    Data->ElementParsed.ParseContext->ElementPath,
                    Data->ElementParsed.ParseContext->ElementPathCch,
                    L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file!urn:schemas-microsoft-com:asm.v1^comClass",
                    NUMBER_OF(L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file!urn:schemas-microsoft-com:asm.v1^comClass") - 1,
                    false) == 0))
        {
            bool fFound = false;
            SIZE_T cb;
            CSmallStringBuffer VersionIndependentComClassIdBuffer;
            PCOM_SERVER_ENTRY Entry = NULL;
            PCOM_FILE_CONTEXT FileContext = NULL;
            CStringBuffer TempBuffer;
            CSmallStringBuffer ProgIdBuffer;
            ULONG ThreadingModel;
            GUID ReferenceClsid, ConfiguredClsid, ImplementedClsid;
            GUID TypeLibraryId;

            TypeLibraryId = GUID_NULL;

            INTERNAL_ERROR_CHECK2(
                ComGlobalContext != NULL,
                "COM global context NULL while processing comClass tag");

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    SXSP_GET_ATTRIBUTE_VALUE_FLAG_REQUIRED_ATTRIBUTE,
                    &s_AttributeName_clsid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(VersionIndependentComClassIdBuffer),
                    &VersionIndependentComClassIdBuffer,
                    cb,
                    NULL,
                    0));

            INTERNAL_ERROR_CHECK(fFound);

            IFW32FALSE_EXIT(::SxspParseGUID(VersionIndependentComClassIdBuffer,
                                            VersionIndependentComClassIdBuffer.Cch(),
                                            ReferenceClsid));

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_threadingModel,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(TempBuffer),
                    &TempBuffer,
                    cb,
                    NULL,
                    0));

            if (fFound)
                IFW32FALSE_EXIT(::SxspParseThreadingModel(TempBuffer, TempBuffer.Cch(), &ThreadingModel));
            else
                ThreadingModel = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_THREADING_MODEL_SINGLE;

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_progid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(ProgIdBuffer),
                    &ProgIdBuffer,
                    cb,
                    NULL,
                    0));

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_tlbid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(TempBuffer),
                    &TempBuffer,
                    cb,
                    NULL,
                    0));

            if (fFound)
                IFW32FALSE_EXIT(::SxspParseGUID(TempBuffer, TempBuffer.Cch(), TypeLibraryId));
            else
                TypeLibraryId = GUID_NULL;

            // That was sufficient if we are generating a context.
            if (Data->Header.ManifestOperation == MANIFEST_OPERATION_GENERATE_ACTIVATION_CONTEXT)
            {
                BOOL fNewAllocate = FALSE;
                IFW32FALSE_EXIT(Data->Header.ClsidMappingContext->Map->MapReferenceClsidToConfiguredClsid(
                            &ReferenceClsid,
                            Data->ElementParsed.AssemblyContext,
                            &ConfiguredClsid,
                            &ImplementedClsid));

                // See if we already have a file context; if we do not, allocate one.
                if (ComGlobalContext->m_FileNameBuffer.Cch() != 0)
                {
                    IFALLOCFAILED_EXIT(FileContext = new COM_FILE_CONTEXT);
                    fNewAllocate = TRUE;

                    IFW32FALSE_EXIT(FileContext->m_FileNameBuffer.Win32Assign(ComGlobalContext->m_FileNameBuffer));

                    FileContext->m_Next = ComGlobalContext->m_FileContextListHead;
                    ComGlobalContext->m_FileContextListHead = FileContext;
                    ComGlobalContext->m_FileContextListCount++;
                    FileContext->m_ServerListHead = NULL;
                    FileContext->m_ServerListCount = 0;
                    FileContext->m_GlobalContext = ComGlobalContext;
                }
                else
                    FileContext = ComGlobalContext->m_FileContextListHead;

                ASSERT(FileContext != NULL);

                IFALLOCFAILED_EXIT(Entry = new COM_SERVER_ENTRY);

                Entry->m_ShimType = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM_TYPE_OTHER;
                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = TypeLibraryId;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                IFW32FALSE_EXIT(Entry->m_ProgIdBuffer.Win32Assign(ProgIdBuffer));

                if (!::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ReferenceClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID))
                {
                    if (fNewAllocate)
                        FUSION_DELETE_SINGLETON(FileContext);
                    FUSION_DELETE_SINGLETON(Entry);
                    goto Exit;
                }

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;

                // And we add another, indexed by the configured clsid
                IFALLOCFAILED_EXIT(Entry = new COM_SERVER_ENTRY);

                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = TypeLibraryId;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                IFW32FALSE_EXIT(Entry->m_ProgIdBuffer.Win32Assign(ProgIdBuffer));

                if (!::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ConfiguredClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID))
                {
                    //if (fNewAllocate)  // should not deleted here.
                    //    FUSION_DELETE_SINGLETON(FileContext);

                    FUSION_DELETE_SINGLETON(Entry);
                    goto Exit;
                }

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;
            }
        }
        else if (
            (Data->ElementParsed.ParseContext->XMLElementDepth == 3) &&
            (::FusionpCompareStrings(
                    Data->ElementParsed.ParseContext->ElementPath,
                    Data->ElementParsed.ParseContext->ElementPathCch,
                    L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file!urn:schemas-microsoft-com:asm.v1^comInterfaceProxyStub",
                    NUMBER_OF(L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^file!urn:schemas-microsoft-com:asm.v1^comInterfaceProxyStub") - 1,
                    false) == 0))
        {
            bool fFound = false;
            SIZE_T cb;
            PCOM_SERVER_ENTRY Entry = NULL;
            PCOM_FILE_CONTEXT FileContext = NULL;
            CStringBuffer TempBuffer;
            ULONG ThreadingModel;
            GUID ReferenceClsid, ConfiguredClsid, ImplementedClsid;
            GUID TypeLibraryId, iid;

            TypeLibraryId = GUID_NULL;

            INTERNAL_ERROR_CHECK2(
                ComGlobalContext != NULL,
                "COM global context NULL while processing comInterfaceProxyStub tag");

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    SXSP_GET_ATTRIBUTE_VALUE_FLAG_REQUIRED_ATTRIBUTE,
                    &s_AttributeName_iid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(iid),
                    &iid,
                    cb,
                    &::SxspValidateGuidAttribute,
                    0));

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_proxyStubClsid32,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(ReferenceClsid),
                    &ReferenceClsid,
                    cb,
                    &::SxspValidateGuidAttribute,
                    0));

            if (!fFound)
                ReferenceClsid = iid;

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_threadingModel,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(TempBuffer),
                    &TempBuffer,
                    cb,
                    NULL,
                    0));

            if (fFound)
                IFW32FALSE_EXIT(::SxspParseThreadingModel(TempBuffer, TempBuffer.Cch(), &ThreadingModel));
            else
                ThreadingModel = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_THREADING_MODEL_SINGLE;


            // That was sufficient if we are generating a context.
            if (Data->Header.ManifestOperation == MANIFEST_OPERATION_GENERATE_ACTIVATION_CONTEXT)
            {
                BOOL fNewAllocate = FALSE;
                IFW32FALSE_EXIT(Data->Header.ClsidMappingContext->Map->MapReferenceClsidToConfiguredClsid(
                            &ReferenceClsid,
                            Data->ElementParsed.AssemblyContext,
                            &ConfiguredClsid,
                            &ImplementedClsid));

                // See if we already have a file context; if we do not, allocate one.
                if (ComGlobalContext->m_FileNameBuffer.Cch() != 0)
                {
                    IFALLOCFAILED_EXIT(FileContext = new COM_FILE_CONTEXT);
                    fNewAllocate = TRUE;

                    IFW32FALSE_EXIT(FileContext->m_FileNameBuffer.Win32Assign(ComGlobalContext->m_FileNameBuffer));
                    FileContext->m_Next = ComGlobalContext->m_FileContextListHead;
                    ComGlobalContext->m_FileContextListHead = FileContext;
                    ComGlobalContext->m_FileContextListCount++;
                    FileContext->m_ServerListHead = NULL;
                    FileContext->m_ServerListCount = 0;
                    FileContext->m_GlobalContext = ComGlobalContext;
                }
                else
                    FileContext = ComGlobalContext->m_FileContextListHead;

                ASSERT(FileContext != NULL);

                IFALLOCFAILED_EXIT(Entry = new COM_SERVER_ENTRY);


                INTERNAL_ERROR_CHECK(FileContext != NULL);

                Entry->m_ShimType = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM_TYPE_OTHER;
                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = GUID_NULL;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                Entry->m_ProgIdBuffer.Clear();

                if (!::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ReferenceClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID))
                {
                    if (fNewAllocate)
                        FUSION_DELETE_SINGLETON(FileContext);
                    FUSION_DELETE_SINGLETON(Entry);
                    goto Exit;
                }

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;

                // And we add another, indexed by the configured clsid
                IFALLOCFAILED_EXIT(Entry = new COM_SERVER_ENTRY);

                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = GUID_NULL;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                Entry->m_ProgIdBuffer.Clear();

                if (!::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ConfiguredClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID))
                {
                    //if (fNewAllocate)  // should not deleted here.
                    //    FUSION_DELETE_SINGLETON(FileContext);

                    FUSION_DELETE_SINGLETON(Entry);
                    goto Exit;
                }

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;
            }
        }
        else if (
            (Data->ElementParsed.ParseContext->XMLElementDepth == 2) &&
            (::FusionpCompareStrings(
                    Data->ElementParsed.ParseContext->ElementPath,
                    Data->ElementParsed.ParseContext->ElementPathCch,
                    L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^clrClass",
                    NUMBER_OF(L"urn:schemas-microsoft-com:asm.v1^assembly!urn:schemas-microsoft-com:asm.v1^clrClass") - 1,
                    false) == 0))
        {
            bool fFound = false;
            SIZE_T cb;
            CSmallStringBuffer VersionIndependentComClassIdBuffer;
            PCOM_SERVER_ENTRY  Entry;
            PCOM_FILE_CONTEXT  FileContext;
            CSmallStringBuffer TempBuffer;
            CSmallStringBuffer ProgIdBuffer;
            CSmallStringBuffer RuntimeVersionBuffer;
            CSmallStringBuffer NameBuffer;
            ULONG ThreadingModel;
            GUID ReferenceClsid, ConfiguredClsid, ImplementedClsid;
            GUID TypeLibraryId;
            bool fIsFirstShim = false;

            TypeLibraryId = GUID_NULL;

            INTERNAL_ERROR_CHECK2(
                ComGlobalContext != NULL,
                "COM global context NULL while processing ndpClass tag");

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    SXSP_GET_ATTRIBUTE_VALUE_FLAG_REQUIRED_ATTRIBUTE,
                    &s_AttributeName_name,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(NameBuffer),
                    &NameBuffer,
                    cb,
                    NULL,
                    0));

            INTERNAL_ERROR_CHECK(fFound);

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    SXSP_GET_ATTRIBUTE_VALUE_FLAG_REQUIRED_ATTRIBUTE,
                    &s_AttributeName_clsid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(VersionIndependentComClassIdBuffer),
                    &VersionIndependentComClassIdBuffer,
                    cb,
                    NULL,
                    0));

            INTERNAL_ERROR_CHECK(fFound);

            IFW32FALSE_EXIT(
                ::SxspParseGUID(
                    VersionIndependentComClassIdBuffer,
                    VersionIndependentComClassIdBuffer.Cch(),
                    ReferenceClsid));

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_threadingModel,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(TempBuffer),
                    &TempBuffer,
                    cb,
                    NULL,
                    0));

            if (fFound)
                IFW32FALSE_EXIT(::SxspParseThreadingModel(TempBuffer, TempBuffer.Cch(), &ThreadingModel));
            else
                ThreadingModel = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_THREADING_MODEL_BOTH;

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_progid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(ProgIdBuffer),
                    &ProgIdBuffer,
                    cb,
                    NULL,
                    0));

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_tlbid,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(TempBuffer),
                    &TempBuffer,
                    cb,
                    NULL,
                    0));

            if (fFound)
                IFW32FALSE_EXIT(::SxspParseGUID(TempBuffer, TempBuffer.Cch(), TypeLibraryId));
            else
                TypeLibraryId = GUID_NULL;

            IFW32FALSE_EXIT(
                ::SxspGetAttributeValue(
                    0,
                    &s_AttributeName_runtimeVersion,
                    &Data->ElementParsed,
                    fFound,
                    sizeof(RuntimeVersionBuffer),
                    &RuntimeVersionBuffer,
                    cb,
                    NULL,
                    0));

            // That was sufficient if we are generating a context.
            if (Data->Header.ManifestOperation == MANIFEST_OPERATION_GENERATE_ACTIVATION_CONTEXT)
            {
                if (ComGlobalContext->m_FirstShimNameBuffer.Cch() == 0)
                {
                    fIsFirstShim = true;
                    IFW32FALSE_EXIT(ComGlobalContext->m_FirstShimNameBuffer.Win32Assign(L"MSCOREE.DLL", 11));
                }
                else
                {
                    IFW32FALSE_EXIT(
                        ComGlobalContext->m_FirstShimNameBuffer.Win32Equals(
                            L"MSCOREE.DLL", 11,
                            fIsFirstShim,
                            true));
                }

                IFW32FALSE_EXIT(
                    Data->Header.ClsidMappingContext->Map->MapReferenceClsidToConfiguredClsid(
                        &ReferenceClsid,
                        Data->ElementParsed.AssemblyContext,
                        &ConfiguredClsid,
                        &ImplementedClsid));



                // If we don't already have a file context for mscoree, then we have to create a new one.
                if (ComGlobalContext->m_MscoreeFileContext == NULL)
                {
                    IFALLOCFAILED_EXIT(FileContext = FUSION_NEW_SINGLETON(COM_FILE_CONTEXT));
                    IFW32FALSE_EXIT(FileContext->m_FileNameBuffer.Win32Assign("mscoree.dll", 11));

                    FileContext->m_Next = ComGlobalContext->m_FileContextListHead;
                    ComGlobalContext->m_FileContextListHead = FileContext;
                    ComGlobalContext->m_FileContextListCount++;
                    ComGlobalContext->m_MscoreeFileContext = FileContext;
                    FileContext->m_ServerListHead = NULL;
                    FileContext->m_ServerListCount = 0;
                    FileContext->m_GlobalContext = ComGlobalContext;
                }
                else
                    FileContext = ComGlobalContext->m_MscoreeFileContext;


#if 0
                // See if we already have a file context; if we do not, allocate one.
                if (ComGlobalContext->m_FileNameBuffer.Cch() != 0)
                {
                    IFALLOCFAILED_EXIT(FileContext = FUSION_NEW_SINGLETON(COM_FILE_CONTEXT));
                    IFW32FALSE_EXIT(FileContext->m_FileNameBuffer.Win32Assign(ComGlobalContext->m_FileNameBuffer));

                    FileContext->m_Next = ComGlobalContext->m_FileContextListHead;
                    ComGlobalContext->m_FileContextListHead = FileContext;
                    ComGlobalContext->m_FileContextListCount++;
                    FileContext->m_ServerListHead = NULL;
                    FileContext->m_ServerListCount = 0;
                    FileContext->m_GlobalContext = ComGlobalContext;
                }
                else
                    FileContext = ComGlobalContext->m_FileContextListHead;
#endif
                INTERNAL_ERROR_CHECK(FileContext != NULL);

                IFALLOCFAILED_EXIT(Entry = FUSION_NEW_SINGLETON(COM_SERVER_ENTRY));

                Entry->m_ShimType = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM_TYPE_CLR_CLASS;
                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = TypeLibraryId;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                IFW32FALSE_EXIT(Entry->m_ProgIdBuffer.Win32Assign(ProgIdBuffer));
                IFW32FALSE_EXIT(Entry->m_ShimNameBuffer.Win32Assign(L"MSCOREE.DLL", 11));
                IFW32FALSE_EXIT(Entry->m_RuntimeVersionBuffer.Win32Assign(RuntimeVersionBuffer));
                IFW32FALSE_EXIT(Entry->m_TypeNameBuffer.Win32Assign(NameBuffer));
                Entry->m_IsFirstShim = fIsFirstShim;

                IFW32FALSE_EXIT(
                    ::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ReferenceClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID));

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;

                // And we add another, indexed by the configured clsid
                IFALLOCFAILED_EXIT(Entry = FUSION_NEW_SINGLETON(COM_SERVER_ENTRY));

                Entry->m_ShimType = ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM_TYPE_CLR_CLASS;
                Entry->m_Next = FileContext->m_ServerListHead;
                Entry->m_ReferenceClsid = ReferenceClsid;
                Entry->m_ConfiguredClsid = ConfiguredClsid;
                Entry->m_ImplementedClsid = ImplementedClsid;
                Entry->m_TypeLibraryId = TypeLibraryId;
                Entry->m_ThreadingModel = ThreadingModel;
                Entry->m_FileContext = FileContext;
                IFW32FALSE_EXIT(Entry->m_ProgIdBuffer.Win32Assign(ProgIdBuffer));
                IFW32FALSE_EXIT(Entry->m_ShimNameBuffer.Win32Assign(L"MSCOREE.DLL", 11));
                IFW32FALSE_EXIT(Entry->m_TypeNameBuffer.Win32Assign(NameBuffer));
                IFW32FALSE_EXIT(Entry->m_RuntimeVersionBuffer.Win32Assign(RuntimeVersionBuffer));
                Entry->m_IsFirstShim = fIsFirstShim;

                IFW32FALSE_EXIT(
                    ::SxsAddGuidToGuidSectionGenerationContext(
                        (PGUID_SECTION_GENERATION_CONTEXT) Data->ElementParsed.Header.ActCtxGenContext,
                        &ConfiguredClsid,
                        Entry,
                        Data->ElementParsed.AssemblyContext->AssemblyRosterIndex,
                        ERROR_SXS_DUPLICATE_CLSID));

                FileContext->m_ServerListHead = Entry;
                FileContext->m_ServerListCount++;
            }
        }

        // Everything's groovy!
        Data->ElementParsed.Success = TRUE;
        break;

    case ACTCTXCTB_CBREASON_GETSECTIONDATA:
        Data->GetSectionData.Success = FALSE;
        INTERNAL_ERROR_CHECK(GSGenContext != NULL);
        INTERNAL_ERROR_CHECK(Data->Header.ManifestOperation == MANIFEST_OPERATION_GENERATE_ACTIVATION_CONTEXT);
        IFW32FALSE_EXIT(::SxsGetGuidSectionGenerationContextSectionData(GSGenContext, Data->GetSectionData.SectionSize, Data->GetSectionData.SectionDataStart, NULL));
        Data->GetSectionData.Success = TRUE;
        break;
    }
Exit:
    ;
}

BOOL
SxspComClassRedirectionGuidSectionGenerationCallback(
    PVOID Context,
    ULONG Reason,
    PVOID CallbackData
    )
{
    BOOL fSuccess = FALSE;

    FN_TRACE_WIN32(fSuccess);

    PCOM_GLOBAL_CONTEXT ComGlobalContext = (PCOM_GLOBAL_CONTEXT) Context;

    INTERNAL_ERROR_CHECK(CallbackData != NULL);

    switch (Reason)
    {
    case GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATASIZE:
        {
            INTERNAL_ERROR_CHECK(ComGlobalContext != NULL);

            PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATASIZE CBData =
                (PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATASIZE) CallbackData;
            PCOM_FILE_CONTEXT FileContext = ComGlobalContext->m_FileContextListHead;

            CBData->DataSize = 0;

            // If we have a mscoree shim, add its size to the user data buffer area.
            if (ComGlobalContext->m_FirstShimNameBuffer.Cch() != 0)
                CBData->DataSize += ((ComGlobalContext->m_FirstShimNameBuffer.Cch() + 1) * sizeof(WCHAR));

            while (FileContext != NULL)
            {
                CBData->DataSize += ((FileContext->m_FileNameBuffer.Cch() + 1) * sizeof(WCHAR));
                FileContext = FileContext->m_Next;
            }

            break;
        }

    case GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATA:
        {
            INTERNAL_ERROR_CHECK( ComGlobalContext != NULL );

            PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATA CBData =
                (PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATA) CallbackData;
            SIZE_T BytesWritten = 0;
            SIZE_T BytesLeft = CBData->BufferSize;
            PWSTR Cursor = (PWSTR) CBData->Buffer;
            PCOM_FILE_CONTEXT FileContext = ComGlobalContext->m_FileContextListHead;

            if (ComGlobalContext->m_FirstShimNameBuffer.Cch() != 0)
            {
                IFW32FALSE_EXIT(
                    ComGlobalContext->m_FirstShimNameBuffer.Win32CopyIntoBuffer(
                        &Cursor,
                        &BytesLeft,
                        &BytesWritten,
                        CBData->SectionHeader,
                        &ComGlobalContext->m_FirstShimNameOffset,
                        &ComGlobalContext->m_FirstShimNameLength));
            }

            while (FileContext != NULL)
            {
                IFW32FALSE_EXIT(
                    FileContext->m_FileNameBuffer.Win32CopyIntoBuffer(
                        &Cursor,
                        &BytesLeft,
                        &BytesWritten,
                        CBData->SectionHeader,
                        &FileContext->m_Offset,
                        NULL));                     // the length is tracked elsewhere

                FileContext = FileContext->m_Next;
            }

            CBData->BytesWritten = BytesWritten;

            break;
        }

    case GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_ENTRYDELETED:
        {
            INTERNAL_ERROR_CHECK( ComGlobalContext != NULL );

            PGUID_SECTION_GENERATION_CONTEXT_CBDATA_ENTRYDELETED CBData =
                (PGUID_SECTION_GENERATION_CONTEXT_CBDATA_ENTRYDELETED) CallbackData;
            PCOM_SERVER_ENTRY Entry = (PCOM_SERVER_ENTRY) CBData->DataContext;

            if (Entry != NULL)
            {
                if (Entry->m_FileContext != NULL)
                {
                    Entry->m_FileContext->m_ServerListCount--;

                    if (Entry->m_FileContext->m_ServerListCount == 0)
                        FUSION_DELETE_SINGLETON(Entry->m_FileContext);
                }

                FUSION_DELETE_SINGLETON(Entry);
            }

            break;
        }

    case GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATASIZE:
        {
            PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATASIZE CBData =
                (PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATASIZE) CallbackData;
            PCOM_SERVER_ENTRY Entry = (PCOM_SERVER_ENTRY) CBData->DataContext;

            CBData->DataSize = sizeof(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION);

            if (Entry->m_ProgIdBuffer.Cch() != 0)
                CBData->DataSize += ((Entry->m_ProgIdBuffer.Cch() + 1) * sizeof(WCHAR));

            if (Entry->m_ShimNameBuffer.Cch() != 0)
            {
                CBData->DataSize += sizeof(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM);

                if (Entry->m_RuntimeVersionBuffer.Cch() != 0)
                    CBData->DataSize += ((Entry->m_RuntimeVersionBuffer.Cch() + 1) * sizeof(WCHAR));

                if (!Entry->m_IsFirstShim)
                    CBData->DataSize += ((Entry->m_ShimNameBuffer.Cch() + 1) * sizeof(WCHAR));

                if (Entry->m_TypeNameBuffer.Cch() != 0)
                    CBData->DataSize += ((Entry->m_TypeNameBuffer.Cch() + 1) * sizeof(WCHAR));
            }

            break;
        }

    case GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATA:
        {
            PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATA CBData =
                (PGUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATA) CallbackData;
            PCOM_SERVER_ENTRY Entry = (PCOM_SERVER_ENTRY) CBData->DataContext;
            PACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION Info;
            PACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM ShimInfo = NULL;
            PVOID Cursor = CBData->Buffer;

            SIZE_T BytesLeft = CBData->BufferSize;
            SIZE_T BytesWritten = 0;

            ALLOCATE_BUFFER_SPACE_TYPE(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION, Cursor, BytesLeft, BytesWritten, Info);

            Info->Size = sizeof(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION);
            Info->Flags = 0;
            Info->ThreadingModel = Entry->m_ThreadingModel;
            Info->ReferenceClsid = Entry->m_ReferenceClsid;
            Info->ConfiguredClsid = Entry->m_ConfiguredClsid;
            Info->ImplementedClsid = Entry->m_ImplementedClsid;
            Info->TypeLibraryId = Entry->m_TypeLibraryId;

            if (Entry->m_ShimNameBuffer.Cch() != 0)
            {
                PWSTR ShimName = NULL;
                SIZE_T OldBytesWritten = BytesWritten;
                SIZE_T ShimDataSize = 0;

                ALLOCATE_BUFFER_SPACE_TYPE(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM, Cursor, BytesLeft, BytesWritten, ShimInfo);

                IFW32FALSE_EXIT(
                    Entry->m_TypeNameBuffer.Win32CopyIntoBuffer(
                        (PWSTR *) &Cursor,
                        &BytesLeft,
                        &BytesWritten,
                        ShimInfo,
                        &ShimInfo->TypeOffset,
                        &ShimInfo->TypeLength));

                ShimInfo->ModuleLength = static_cast<ULONG>(Entry->m_FileContext->m_FileNameBuffer.Cch() * sizeof(WCHAR));
                ShimInfo->ModuleOffset = Entry->m_FileContext->m_Offset;

                IFW32FALSE_EXIT(
                    Entry->m_RuntimeVersionBuffer.Win32CopyIntoBuffer(
                        (PWSTR *) &Cursor,
                        &BytesLeft,
                        &BytesWritten,
                        ShimInfo,
                        &ShimInfo->ShimVersionOffset,
                        &ShimInfo->ShimVersionLength));

                ShimDataSize = BytesWritten - OldBytesWritten;

                if (Entry->m_IsFirstShim)
                {
                    Info->ModuleOffset = Entry->m_FileContext->m_GlobalContext->m_FirstShimNameOffset;
                    Info->ModuleLength = Entry->m_FileContext->m_GlobalContext->m_FirstShimNameLength;
                }
                else
                {
                    IFW32FALSE_EXIT(
                        Entry->m_ShimNameBuffer.Win32CopyIntoBuffer(
                            (PWSTR *) &Cursor,
                            &BytesLeft,
                            &BytesWritten,
                            CBData->SectionHeader,
                            &Info->ModuleOffset,
                            &Info->ModuleLength));
                }

                ShimInfo->Size = sizeof(ACTIVATION_CONTEXT_DATA_COM_SERVER_REDIRECTION_SHIM);
                ShimInfo->Flags = 0;
                ShimInfo->Type = Entry->m_ShimType;

                ShimInfo->DataLength = 0;
                ShimInfo->DataOffset = 0;

                Info->ShimDataLength = static_cast<ULONG>(ShimDataSize);
                Info->ShimDataOffset = static_cast<ULONG>(((ULONG_PTR) ShimInfo) - ((ULONG_PTR) Info));
            }
            else
            {
                Info->ModuleLength = static_cast<ULONG>(Entry->m_FileContext->m_FileNameBuffer.Cch() * sizeof(WCHAR));
                Info->ModuleOffset = Entry->m_FileContext->m_Offset;
                Info->ShimDataLength = 0;
                Info->ShimDataOffset = 0;
            }

            IFW32FALSE_EXIT(Entry->m_ProgIdBuffer.Win32CopyIntoBuffer(
                (PWSTR *) &Cursor,
                &BytesLeft,
                &BytesWritten,
                Info,
                &Info->ProgIdOffset,
                &Info->ProgIdLength));

            CBData->BytesWritten = BytesWritten;
        }
    }

    fSuccess = TRUE;
Exit:
    return fSuccess;
}