/*++

Copyright (C) Microsoft Corporation, 2000

Module Name:

    EEInfo.cxx

Abstract:

    Extended Error Info public & private functions

Author:

    Kamen Moutafov    [KamenM]


Revision History:

    KamenM      Mar 2000    Initial version
    KamenM      Oct 2000    Add caching of EEInfo blocks to 
                            solve Exchange perf problems

--*/
#include <precomp.hxx>
#include <EEInfo.h>
#include <EEInfo.hxx>

const int MaxBinaryBlobSize = 4096; // 4K limit

ExtendedErrorInfo *
AllocateExtendedErrorInfoRecord (
    IN int NumberOfParams
    )
/*++

Routine Description:

    Allocates a memory block large enough to hold an
    extended error record with the specified number of
    parameters. It is allocated with MIDL_user_allocate.

Arguments:
    NumberOfParams - number of paramaters to provide space
        for

Return Value:

    The address of the block or NULL if out of memory

--*/
{
    ExtendedErrorInfo *EEInfo;
    THREAD *ThisThread;

    ThisThread = ThreadSelf();
    if (ThisThread)
        {
        EEInfo = ThisThread->GetCachedEEInfoBlock(NumberOfParams);
        }
    else
        EEInfo = NULL;

    if (EEInfo == NULL)
        {
        EEInfo = (ExtendedErrorInfo *) MIDL_user_allocate(sizeof(ExtendedErrorInfo) + 
            (NumberOfParams - 1) * sizeof(ExtendedErrorParam));
        }

    if (EEInfo)
        EEInfo->nLen = (short)NumberOfParams;
    return EEInfo;
}

inline void
FreeEEInfoRecordShallow (
    IN ExtendedErrorInfo *InfoToFree
    )
/*++

Routine Description:

    Frees only the eeinfo record - not any of
    the pointers contained in it.

Arguments:
    InfoToFree - the eeinfo record

Return Value:

    void

--*/
{
    MIDL_user_free(InfoToFree);
}

RPC_STATUS
DuplicatePrivateString (
    IN EEUString *SourceString,
    OUT EEUString *DestString
    )
/*++

Routine Description:

    Takes a EEUString structure and makes a copy
    of it.

Arguments:
    SourceString - the string to copy
    DestString - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    DestString->nLength = SourceString->nLength;
    DestString->pString = (LPWSTR)MIDL_user_allocate(DestString->nLength * sizeof(unsigned short));
    if (DestString->pString != NULL)
        {
        RpcpMemoryCopy(DestString->pString, SourceString->pString, DestString->nLength * sizeof(unsigned short));
        return RPC_S_OK;
        }
    else
        {
        return RPC_S_OUT_OF_MEMORY;
        }
}

RPC_STATUS
DuplicatePrivateString (
    IN EEAString *SourceString,
    OUT EEAString *DestString
    )
/*++

Routine Description:

    Takes a EEAString structure and makes a copy
    of it.

Arguments:
    SourceString - the string to copy
    DestString - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    DestString->nLength = SourceString->nLength;
    DestString->pString = (unsigned char *)MIDL_user_allocate(DestString->nLength);
    if (DestString->pString != NULL)
        {
        RpcpMemoryCopy(DestString->pString, SourceString->pString, DestString->nLength);
        return RPC_S_OK;
        }
    else
        {
        return RPC_S_OUT_OF_MEMORY;
        }
}

RPC_STATUS
DuplicateBlob (
    IN void *SourceBlob,
    IN short Size,
    OUT void **DestBlob)
/*++

Routine Description:

    Takes a blob and makes a copy of it.

Arguments:
    SourceBlob - the blob to copy
    Size - the size of the blob
    DestBlob - a placeholder where a pointer to the copied
        buffer will be put

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    void *CopiedBlob;

    CopiedBlob = MIDL_user_allocate(Size);
    if (CopiedBlob)
        {
        RpcpMemoryCopy(CopiedBlob, SourceBlob, Size);
        *DestBlob = CopiedBlob;
        return RPC_S_OK;
        }
    else
        {
        return RPC_S_OUT_OF_MEMORY;
        }
}

RPC_STATUS 
DuplicatePrivateBlob (
    IN BinaryEEInfo *SourceBlob,
    OUT BinaryEEInfo *DestBlob)
/*++

Routine Description:

    Takes a binary param and makes a copy of it.

Arguments:
    SourceBlob - the blob to copy
    DestBlob - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    RPC_STATUS Status;

    Status = DuplicateBlob(SourceBlob->pBlob, SourceBlob->nSize, (PVOID *)&DestBlob->pBlob);
    if (Status == RPC_S_OK)
        {
        DestBlob->nSize = SourceBlob->nSize;
        }

    return Status;
}

RPC_STATUS 
ConvertPublicStringToPrivateString (
    IN LPWSTR PublicString,
    OUT EEUString *PrivateString)
/*++

Routine Description:

    Takes a LPWSTR string and makes a copy
    of it into a EEUString structure

Arguments:
    PublicString - the string to copy - cannot be NULL
    PrivateString - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    // the StringLength is in bytes
    int StringLength = (wcslen(PublicString) + 1) * 2;
    LPWSTR CopiedString;
    CopiedString = (LPWSTR)MIDL_user_allocate(StringLength);
    if (CopiedString)
        {
        RpcpMemoryCopy(CopiedString, PublicString, StringLength);
        PrivateString->pString = CopiedString;
        PrivateString->nLength = (short)StringLength / 2;
        return RPC_S_OK;
        }
    else
        {
        return RPC_S_OUT_OF_MEMORY;
        }
}

RPC_STATUS 
ConvertPublicStringToPrivateString (
    IN LPSTR PublicString,
    OUT EEAString *PrivateString)
/*++

Routine Description:

    Takes a LPSTR string and makes a copy
    of it into a EEAString structure

Arguments:
    PublicString - the string to copy - cannot be NULL
    PrivateString - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    int StringLength = strlen(PublicString) + 1;
    LPSTR CopiedString;
    CopiedString = (LPSTR)MIDL_user_allocate(StringLength);
    if (CopiedString)
        {
        RpcpMemoryCopy(CopiedString, PublicString, StringLength);
        PrivateString->pString = (unsigned char *)CopiedString;
        PrivateString->nLength = (short)StringLength;
        return RPC_S_OK;
        }
    else
        {
        return RPC_S_OUT_OF_MEMORY;
        }
}

RPC_STATUS 
ConvertPublicBlobToPrivateBlob (
    IN BinaryParam *PublicBlob,
    OUT BinaryEEInfo *PrivateBlob)
/*++

Routine Description:

    Takes a binary param and converts it to private format.

Arguments:
    PublicBlob - the blob to copy - cannot be NULL
    PrivateBlob - a placeholder allocated by caller to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    unsigned char *CopiedBlob;

    if (PublicBlob->Size > MaxBinaryBlobSize)
        {
        return ERROR_INVALID_PARAMETER;
        }

    RPC_STATUS Status;

    Status = DuplicateBlob(PublicBlob->Buffer, PublicBlob->Size, (PVOID *)&PrivateBlob->pBlob);
    if (Status == RPC_S_OK)
        {
        PrivateBlob->nSize = PublicBlob->Size;
        }

    return Status;
}

RPC_STATUS
ConvertPrivateStringToPublicString (
    IN EEUString *PrivateString,
    IN BOOL CopyStrings,
    OUT LPWSTR *PublicString
    )
/*++

Routine Description:

    Takes a EEUString and makes a copy
    of it into a LPWSTR

Arguments:
    PrivateString - the string to copy
    CopyStrings - if non-zero this routine will allocate
        space on the process heap and will copy the string.
        If zero, it will alias the PublicString to the
        pString member of PrivateString
    PublicString - the string to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    LPWSTR ReturnString;
    int StringLength;   // in bytes

    if (CopyStrings)
        {
        StringLength = PrivateString->nLength * 2;
        ReturnString = (LPWSTR)RtlAllocateHeap(RtlProcessHeap(), 0, StringLength);
        if (ReturnString == NULL)
            return RPC_S_OUT_OF_MEMORY;
        RpcpMemoryCopy(ReturnString, PrivateString->pString, StringLength);
        }
    else
        {
        ReturnString = PrivateString->pString;
        }

    *PublicString = ReturnString;
    return RPC_S_OK;
}

RPC_STATUS
ConvertPrivateStringToPublicString (
    IN EEAString *PrivateString,
    IN BOOL CopyStrings,
    OUT LPSTR *PublicString
    )
/*++

Routine Description:

    Takes a EEAString and makes a copy
    of it into a LPSTR

Arguments:
    PrivateString - the string to copy
    CopyStrings - if non-zero this routine will allocate
        space on the process heap and will copy the string.
        If zero, it will alias the PublicString to the
        pString member of PrivateString
    PublicString - the string to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    LPSTR ReturnString;
    if (CopyStrings)
        {
        ReturnString = (LPSTR)RtlAllocateHeap(RtlProcessHeap(), 0, PrivateString->nLength);
        if (ReturnString == NULL)
            return RPC_S_OUT_OF_MEMORY;
        RpcpMemoryCopy(ReturnString, PrivateString->pString, PrivateString->nLength);
        }
    else
        {
        ReturnString = (char *)PrivateString->pString;
        }

    *PublicString = ReturnString;
    return RPC_S_OK;
}

RPC_STATUS
ConvertPrivateBlobToPublicBlob (
    IN BinaryEEInfo *PrivateBlob,
    IN BOOL CopyStrings,
    OUT BinaryParam *PublicBlob
    )
/*++

Routine Description:

    Takes a private blob and makes a copy
    of it into a public blob format

Arguments:
    PrivateBlob - the blob to copy
    CopyStrings - if non-zero this routine will allocate
        space on the process heap and will copy the blob.
        If zero, it will alias the PublicBlob to the
        Blob.pBlob member of PrivateBlob
    PublicBlob - the blob to copy to

Return Value:

    RPC_S_OK or RPC_S_OUT_OF_MEMORY

--*/
{
    void *ReturnBuffer;
    if (CopyStrings)
        {
        ReturnBuffer = RtlAllocateHeap(RtlProcessHeap(), 0, PrivateBlob->nSize);
        if (ReturnBuffer == NULL)
            return RPC_S_OUT_OF_MEMORY;
        RpcpMemoryCopy(ReturnBuffer, PrivateBlob->pBlob, PrivateBlob->nSize);
        }
    else
        {
        ReturnBuffer = PrivateBlob->pBlob;
        }

    PublicBlob->Buffer = ReturnBuffer;
    PublicBlob->Size = PrivateBlob->nSize;
    return RPC_S_OK;
}

inline void
FreePublicStringIfNecessary (
    OUT LPWSTR PublicString,
    IN BOOL CopyStrings
    )
/*++

Routine Description:

    Deletes the string if necessary

Arguments:
    PublicString - the string to delete. Must be on the
        process heap
    CopyStrings - the value of the CopyStrings parameter
        when RpcErrorGetNextRecord was called. If non-zero
        the string will be freed. Otherwise, the function
        is a no-op

Return Value:

    void

--*/
{
    if (CopyStrings)
        {
        if (PublicString)
            {
            RtlFreeHeap(RtlProcessHeap(), 0, PublicString);
            }
        }
}

void
FreeEEInfoPrivateParam (
    IN ExtendedErrorParam *Param
    )
{
    if ((Param->Type == eeptiAnsiString)
        || (Param->Type == eeptiUnicodeString))
        {
        // AnsiString & UnicodeString occupy the same
        // memory location - ok to free any of them
        MIDL_user_free(Param->AnsiString.pString);
        }
    else if (Param->Type == eeptiBinary)
        {
        MIDL_user_free(Param->Blob.pBlob);
        }
}

void
FreeEEInfoPublicParam (
    IN OUT RPC_EE_INFO_PARAM *Param,
    IN BOOL CopyStrings
    )
/*++

Routine Description:

    If the type of parameter is string (ansi or unicode)
        and CopyStrings, free the string

Arguments:
    Param - the parameter to free
    CopyStrings - the value of the CopyStrings parameter
        when RpcErrorGetNextRecord was called. If non-zero
        the string will be freed. Otherwise, the function
        is a no-op

Return Value:

    void

--*/
{
    if ((Param->ParameterType == eeptAnsiString)
        || (Param->ParameterType == eeptUnicodeString))
        {
        if (CopyStrings)
            {
            RtlFreeHeap(RtlProcessHeap(), 0, Param->u.AnsiString);
            }
        }
    else if (Param->ParameterType == eeptBinary)
        {
        if (CopyStrings)
            {
            RtlFreeHeap(RtlProcessHeap(), 0, Param->u.BVal.Buffer);
            }
        }
}

RPC_STATUS 
ConvertPublicParamToPrivateParam (
    IN RPC_EE_INFO_PARAM *PublicParam,
    OUT ExtendedErrorParam *PrivateParam
    )
/*++

Routine Description:

    Takes a parameter in format RPC_EE_INFO_PARAM and
    converts it to ExtendedErrorParam.

Arguments:
    PublicParam - the parameter to copy.
    PrivateParam - the parameter to copy to

Return Value:

    RPC_S_OK, RPC_S_INTERNAL_ERROR, RPC_S_OUT_OF_MEMORY

--*/
{
    RPC_STATUS RpcStatus = RPC_S_OK;

    PrivateParam->Type = (ExtendedErrorParamTypesInternal)PublicParam->ParameterType;
    switch (PrivateParam->Type)
        {
        case eeptiAnsiString:
            RpcStatus = ConvertPublicStringToPrivateString(PublicParam->u.AnsiString,
                 &PrivateParam->AnsiString);
            break;

        case eeptiUnicodeString:
            RpcStatus = ConvertPublicStringToPrivateString(PublicParam->u.UnicodeString,
                &PrivateParam->UnicodeString);
            break;

        case eeptiLongVal:
            PrivateParam->LVal = PublicParam->u.LVal;
            break;

        case eeptiShortVal:
            PrivateParam->IVal = PublicParam->u.SVal;
            break;

        case eeptiPointerVal:
            PrivateParam->PVal = (ULONGLONG)PublicParam->u.PVal;
            break;

        case eeptiNone:
            break;

        case eeptiBinary:
            RpcStatus = ConvertPublicBlobToPrivateBlob(&PublicParam->u.BVal,
                &PrivateParam->Blob);
            break;

        default:
            ASSERT(FALSE);
            RpcStatus = ERROR_INVALID_PARAMETER;
        }
    return RpcStatus;
}

RPC_STATUS 
ConvertPrivateParamToPublicParam (
    IN ExtendedErrorParam *PrivateParam,
    IN BOOL CopyStrings,
    OUT RPC_EE_INFO_PARAM *PublicParam
    )
/*++

Routine Description:

    Takes a parameter in format ExtendedErrorParam and
    converts it to RPC_EE_INFO_PARAM.

Arguments:
    PrivateParam - the parameter to copy
    CopyStrings - if non-zero, this function will allocate
        space for any strings to be copied on the process
        heap and will copy the strings. If FALSE, it
        will alias the output pointers to RPC internal
        data structures
    PublicParam - the parameter to copy to

Return Value:

    RPC_S_OK, RPC_S_INTERNAL_ERROR, RPC_S_OUT_OF_MEMORY

--*/
{
    RPC_STATUS RpcStatus = RPC_S_OK;

    PublicParam->ParameterType = (ExtendedErrorParamTypes)PrivateParam->Type;
    switch (PublicParam->ParameterType)
        {
        case eeptAnsiString:
            RpcStatus = ConvertPrivateStringToPublicString(&PrivateParam->AnsiString,
                CopyStrings,
                &PublicParam->u.AnsiString);
            break;

        case eeptUnicodeString:
            RpcStatus = ConvertPrivateStringToPublicString(&PrivateParam->UnicodeString,
                CopyStrings,
                &PublicParam->u.UnicodeString);
            break;

        case eeptLongVal:
            PublicParam->u.LVal = PrivateParam->LVal;
            break;

        case eeptShortVal:
            PublicParam->u.SVal = PrivateParam->IVal;
            break;

        case eeptPointerVal:
            PublicParam->u.PVal = PrivateParam->PVal;
            break;

        case eeptNone:
            break;

        case eeptBinary:
            RpcStatus = ConvertPrivateBlobToPublicBlob(&PrivateParam->Blob,
                CopyStrings,
                &PublicParam->u.BVal);
            break;

        default:
            ASSERT(FALSE);
            RpcStatus = RPC_S_INTERNAL_ERROR;
        }
    return RpcStatus;    
}

void
InitializePrivateEEInfo (
    IN ExtendedErrorInfo *ErrorInfo
    )
/*++

Routine Description:

    Initializes the common data members of the ExtendedErrorInfo
    structure.

Arguments:
    ErrorInfo - the structure to initialize

Return Value:

    void

--*/
{
    ErrorInfo->Next = NULL;
    ErrorInfo->ComputerName.Type = eecnpNotPresent;
    ErrorInfo->ProcessID = GetCurrentProcessId();
    GetSystemTimeAsFileTime((FILETIME *)&ErrorInfo->TimeStamp);
    ErrorInfo->Flags = 0;
}

RPC_STATUS 
ConvertPublicEEInfoToPrivateEEInfo (
    IN RPC_EXTENDED_ERROR_INFO *PublicEEInfo,
    IN short DetectionLocation,
    OUT ExtendedErrorInfo *PrivateEEInfo
    )
/*++

Routine Description:

    Takes a RPC_EXTENDED_ERROR_INFO record and converts
    it to an ExtendedErrorInfo record.

Arguments:
    PublicEEInfo - the public record to convert
    DetectionLocation - the detection location to use in the
        private record.
    PrivateEEInfo - the private record to copy to

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    int NumberOfParams;
    int i;
    RPC_STATUS RpcStatus = RPC_S_OK;

    ASSERT(PrivateEEInfo != NULL);
    ASSERT(PrivateEEInfo->nLen == PublicEEInfo->NumberOfParameters);

    if (PublicEEInfo->Version != RPC_EEINFO_VERSION)
        return RPC_S_INVALID_LEVEL;

    InitializePrivateEEInfo(PrivateEEInfo);
    // EEInfoGCCOM can come externally. If it's not, set it to
    // EEInfoGCApplication
    if (PublicEEInfo->GeneratingComponent != EEInfoGCCOM)
        {
        PrivateEEInfo->GeneratingComponent = EEInfoGCApplication;
        }
    else
        {
        PrivateEEInfo->GeneratingComponent = EEInfoGCCOM;
        }
    PrivateEEInfo->Status = PublicEEInfo->Status;
    PrivateEEInfo->DetectionLocation = DetectionLocation;
    // the next line should have been executed by the allocating code
    //PrivateEEInfo->nLen = PublicEEInfo->NumberOfParameters;
    NumberOfParams = PrivateEEInfo->nLen;

    for (i = 0; i < NumberOfParams; i ++)
        {
        if ((PublicEEInfo->Parameters[i].ParameterType < eeptAnsiString)
            || (PublicEEInfo->Parameters[i].ParameterType > eeptBinary))
            RpcStatus = ERROR_INVALID_PARAMETER;

        if (RpcStatus == RPC_S_OK)
            {
            RpcStatus = ConvertPublicParamToPrivateParam(&PublicEEInfo->Parameters[i],
                &PrivateEEInfo->Params[i]);
            }

        if (RpcStatus != RPC_S_OK)
            {
            // go backward and free all memory
            i --;
            for (; i >= 0; i --)
                {
                FreeEEInfoPrivateParam(&PrivateEEInfo->Params[i]);
                }
            break;
            }
        }

    return RpcStatus;
}

RPC_STATUS 
ConvertPrivateEEInfoToPublicEEInfo (
    IN ExtendedErrorInfo *PrivateEEInfo,
    IN BOOL CopyStrings,
    OUT RPC_EXTENDED_ERROR_INFO *PublicEEInfo
    )
/*++

Routine Description:

    Takes an ExtendedErrorInfo record and converts
    it to a RPC_EXTENDED_ERROR_INFO  record.

Arguments:
    PrivateEEInfo - the private record to convert
    CopyStrings - If non-zero, all strings will be allocated
        space on the process heap and will be copied. Otherwise
        they will be aliased to the privated data structure 
        strings
    PublicEEInfo - the public record to copy to

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    BOOL Result;
    int NumberOfParams;
    int i;
    RPC_STATUS RpcStatus;

    ASSERT (PublicEEInfo != NULL);

    if (PublicEEInfo->Version != RPC_EEINFO_VERSION)
        return RPC_S_INVALID_LEVEL;

    if (PublicEEInfo->NumberOfParameters < PrivateEEInfo->nLen)
        return RPC_S_BUFFER_TOO_SMALL;

    if (PublicEEInfo->Flags & (~EEInfoValidInputFlags))
        return RPC_S_INVALID_LEVEL;

    if (PrivateEEInfo->ComputerName.Type == eecnpNotPresent)
        {
        PublicEEInfo->ComputerName = NULL;
        }
    else
        {
        RpcStatus = ConvertPrivateStringToPublicString(&PrivateEEInfo->ComputerName.Name,
            CopyStrings, &PublicEEInfo->ComputerName);
        if (RpcStatus != RPC_S_OK)
            return RpcStatus;
        }
    PublicEEInfo->ProcessID = PrivateEEInfo->ProcessID;
    if (PublicEEInfo->Flags & EEInfoUseFileTime)
        {
        RpcpMemoryCopy(&PublicEEInfo->u.FileTime, &PrivateEEInfo->TimeStamp, sizeof(FILETIME));
        }
    else
        {
        Result = FileTimeToSystemTime((FILETIME *)&PrivateEEInfo->TimeStamp,
            &PublicEEInfo->u.SystemTime);
        if (Result == 0)
            return GetLastError();
        }
    PublicEEInfo->GeneratingComponent = PrivateEEInfo->GeneratingComponent;
    PublicEEInfo->Status = PrivateEEInfo->Status;
    PublicEEInfo->DetectionLocation = PrivateEEInfo->DetectionLocation;
    PublicEEInfo->Flags = PrivateEEInfo->Flags;
    // restore the consistency of the flags, if necessary
    if (PrivateEEInfo->Next)
        {
        // if there is next record, and its flags indicate that
        // a previous record is missing
        if (PrivateEEInfo->Next->Flags & EEInfoPreviousRecordsMissing)
            PublicEEInfo->Flags |= EEInfoNextRecordsMissing;
        }
    NumberOfParams = PrivateEEInfo->nLen;
    PublicEEInfo->NumberOfParameters = NumberOfParams;

    for (i = 0; i < NumberOfParams; i ++)
        {
        // convert the params
        RpcStatus = ConvertPrivateParamToPublicParam(&PrivateEEInfo->Params[i],
            CopyStrings, &PublicEEInfo->Parameters[i]);
        if (RpcStatus != RPC_S_OK)
            {
            // go back, and free eveyrthing
            FreePublicStringIfNecessary(PublicEEInfo->ComputerName, CopyStrings);
            i --;
            for ( ; i >= 0; i --)
                {
                FreeEEInfoPublicParam(&PublicEEInfo->Parameters[i], CopyStrings);
                }
            return RpcStatus;
            }
        }

    return RPC_S_OK;
}

void
FreeEEInfoRecord (
    IN ExtendedErrorInfo *EEInfo
    )
/*++

Routine Description:

    Frees a single ExtendedErrorInfo record and
    all strings within it.

Arguments:
    EEInfo - record to free

Return Value:

    void

--*/
{
    int i;
    THREAD *Thread;

    for (i = 0; i < EEInfo->nLen; i ++)
        {
        FreeEEInfoPrivateParam(&EEInfo->Params[i]);
        }

    if (EEInfo->ComputerName.Type == eecnpPresent)
        {
        MIDL_user_free(EEInfo->ComputerName.Name.pString);
        }

    Thread = RpcpGetThreadPointer();

    if (Thread)
        {
        Thread->SetCachedEEInfoBlock(EEInfo, EEInfo->nLen);
        }
    else
        {
        FreeEEInfoRecordShallow(EEInfo);
        }
}

void
FreeEEInfoChain (
    IN ExtendedErrorInfo *EEInfo
    )
/*++

Routine Description:

    Frees a chain of ExtendedErrorInfo records and
        all strings within them.

Arguments:
    EEInfo - head of list to free

Return Value:

    void

--*/
{
    ExtendedErrorInfo *CurrentInfo, *NextInfo;

    CurrentInfo = EEInfo;
    while (CurrentInfo != NULL)
        {
        // get the next link while we can
        NextInfo = CurrentInfo->Next;
        FreeEEInfoRecord(CurrentInfo);
        CurrentInfo = NextInfo;
        }
}

RPC_STATUS
CloneEEInfoParam (
    IN ExtendedErrorParam *SourceParam,
    OUT ExtendedErrorParam *DestParam
    )
/*++

Routine Description:

    Makes an exact deep copy of an ExtendedErrorParam structure

Arguments:
    SourceParam - the parameter to copy from
    DestParam - the parameter to copy to

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    RPC_STATUS RpcStatus = RPC_S_OK;

    ASSERT (DestParam != NULL);

    switch (SourceParam->Type)
        {
        case eeptiAnsiString:
            RpcStatus = DuplicatePrivateString(&SourceParam->AnsiString,
                &DestParam->AnsiString);
            break;

        case eeptiUnicodeString:
            RpcStatus = DuplicatePrivateString(&SourceParam->UnicodeString,
                &DestParam->UnicodeString);
            break;

        case eeptiLongVal:
            DestParam->LVal = SourceParam->LVal;
            break;

        case eeptiShortVal:
            DestParam->IVal = SourceParam->IVal;
            break;

        case eeptiPointerVal:
            DestParam->PVal = SourceParam->PVal;
            break;

        case eeptiNone:
            break;

        case eeptiBinary:
            RpcStatus = DuplicatePrivateBlob(&SourceParam->Blob,
                &DestParam->Blob);
            break;

        default:
            ASSERT(0);
            RpcStatus = RPC_S_INTERNAL_ERROR;
        }

    DestParam->Type = SourceParam->Type;

    return RpcStatus;
}

RPC_STATUS
CloneEEInfoRecord (
    IN ExtendedErrorInfo *SourceInfo,
    OUT ExtendedErrorInfo **DestInfo
    )
/*++

Routine Description:

    Makes an exact deep copy of a single ExtendedErrorInfo record

Arguments:
    SourceInfo - the record to copy from
    DestInfo - the record to copy to

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *NewInfo;
    int NumberOfParams;
    int i;
    RPC_STATUS RpcStatus;
    EEUString *ComputerNameToFree = NULL;

    *DestInfo = NULL;
    NumberOfParams = SourceInfo->nLen;
    NewInfo = AllocateExtendedErrorInfoRecord(NumberOfParams);
    if (NewInfo == NULL)
        return RPC_S_OUT_OF_MEMORY;

    // shallow copy all the fields. This is good for most fields
    // we will process the ones that need deep copy further down.
    // we copy everything, but the first param, which may require
    // deep copying
    RpcpMemoryCopy(NewInfo, SourceInfo, sizeof(ExtendedErrorInfo) - sizeof(ExtendedErrorParam));
    // N.B. Zero out the next field before any failure paths
    NewInfo->Next = NULL;
    if (SourceInfo->ComputerName.Type == eecnpPresent)
        {
        RpcStatus = DuplicatePrivateString(&SourceInfo->ComputerName.Name,
            &NewInfo->ComputerName.Name);
        if (RpcStatus != RPC_S_OK)
            {
            FreeEEInfoRecordShallow(NewInfo);
            return RpcStatus;
            }

        ComputerNameToFree = &NewInfo->ComputerName.Name;
        }

    for (i = 0; i < NumberOfParams; i ++)
        {
        RpcStatus = CloneEEInfoParam(&SourceInfo->Params[i],
            &NewInfo->Params[i]);
        if (RpcStatus != RPC_S_OK)
            {
            if (ComputerNameToFree)
                MIDL_user_free(ComputerNameToFree->pString);
            i --;
            for ( ; i >= 0; i --)
                {
                FreeEEInfoPrivateParam(&NewInfo->Params[i]);
                }
            FreeEEInfoRecordShallow(NewInfo);

            return RpcStatus;
            }
        }

    *DestInfo = NewInfo;
    return RPC_S_OK;
}

RPC_STATUS
CloneEEInfoChain (
    IN ExtendedErrorInfo *SourceEEInfo,
    OUT ExtendedErrorInfo **DestEEInfo
    )
/*++

Routine Description:

    Makes an exact deep copy of an ExtendedErrorInfo chain

Arguments:
    SourceEEInfo - the head of the chain to copy from
    DestEEInfo - a pointer to the head of the cloned chain.
        The memory for the head of the cloned chain will be
        allocated by this function and the given pointer
        will be set to point to it.

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *CurrentInfo, *NewInfo, *NewInfoHead = NULL;
    ExtendedErrorInfo *LastNewInfo = NULL;
    RPC_STATUS RpcStatus;

    CurrentInfo = SourceEEInfo;
    while (CurrentInfo != NULL)
        {
        RpcStatus = CloneEEInfoRecord(CurrentInfo, &NewInfo);
        if (RpcStatus != RPC_S_OK)
            {
            if (NewInfoHead != NULL)
                FreeEEInfoChain(NewInfoHead);
            return RpcStatus;
            }
        if (NewInfoHead == NULL)
            NewInfoHead = NewInfo;

        if (LastNewInfo != NULL)
            LastNewInfo->Next = NewInfo;

        // advance both chains
        LastNewInfo = NewInfo;
        CurrentInfo = CurrentInfo->Next;
        }

    *DestEEInfo = NewInfoHead;
    return RPC_S_OK;
}

const ULONG EnumSignatureLive = 0xfcfcfcfc;
const ULONG EnumSignatureDead = 0xfdfdfdfd;

void
InitializeEnumHandleWithEEInfo (
    IN ExtendedErrorInfo *EEInfo,
    IN OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Initializes the common fields of a RPC_ERROR_ENUM_HANDLE
    structure

Arguments:
    EEInfo - the chain we're enumerating from
    EnumHandle - the structure to initialize

Return Value:

    void

--*/
{
    ASSERT(EEInfo != NULL);
    EnumHandle->Signature = EnumSignatureLive;
    EnumHandle->Head = EEInfo;
    EnumHandle->CurrentPos = EEInfo;
}

RPC_STATUS 
RpcpErrorStartEnumerationFromEEInfo (
    IN ExtendedErrorInfo *EEInfo,
    IN OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Starts an eeinfo enumeration using the passed
        EEInfo structure to start the enumeration

Arguments:
    EEInfo - the chain we will enumerate
    EnumHandle - the enumeration handle

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *ClonedEEInfo;
    RPC_STATUS RpcStatus;
    
    RpcStatus = CloneEEInfoChain(EEInfo, &ClonedEEInfo);
    if (RpcStatus != RPC_S_OK)
        return RpcStatus;

    InitializeEnumHandleWithEEInfo(ClonedEEInfo, EnumHandle);

    return RPC_S_OK;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorStartEnumeration (
    IN OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Starts an eeinfo enumeration using the eeinfo on 
    the thread

Arguments:
    EnumHandle - the enumeration handle. Allocated by caller

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *ClonedEEInfo, *EEInfo;
    RPC_STATUS RpcStatus;
    THREAD *Thread;
    
    // get the EEInfo from the Teb
    Thread = RpcpGetThreadPointer();
    if (Thread == NULL)
        return RPC_S_ENTRY_NOT_FOUND;

    EEInfo = Thread->GetEEInfo();

    if (EEInfo == NULL)
        return RPC_S_ENTRY_NOT_FOUND;

    return RpcpErrorStartEnumerationFromEEInfo(EEInfo, EnumHandle);
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorGetNextRecord (
    IN RPC_ERROR_ENUM_HANDLE *EnumHandle, 
    IN BOOL CopyStrings, 
    OUT RPC_EXTENDED_ERROR_INFO *ErrorInfo
    )
/*++

Routine Description:

    Retrieves the next private record from the enumeration
    and converts it to public format

Arguments:
    EnumHandle - the enumeration handle
    CopyStrings - if non-zero, all strings converted to public
        format will be allocated space for on the process heap
        and will be copied there. If FALSE, the strings in the
        public structures will be aliases to the private structure
    ErrorInfo - the public record that will be filled on output

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    RPC_STATUS RpcStatus;
    ExtendedErrorInfo *CurrentRecord;

    ASSERT(EnumHandle != NULL);
    ASSERT(EnumHandle->Head != NULL);
    ASSERT(EnumHandle->Signature != EnumSignatureDead);

    if (EnumHandle->Signature != EnumSignatureLive)
        return ERROR_INVALID_PARAMETER;

    if (EnumHandle->CurrentPos == NULL)
        return RPC_S_ENTRY_NOT_FOUND;

    CurrentRecord = (ExtendedErrorInfo *) EnumHandle->CurrentPos;
    RpcStatus = ConvertPrivateEEInfoToPublicEEInfo(CurrentRecord,
        CopyStrings, ErrorInfo);

    if (RpcStatus == RPC_S_OK)
        {
        EnumHandle->CurrentPos = CurrentRecord->Next;
        }

    return RpcStatus;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorEndEnumeration (
    IN OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Finished the enumeration and frees all resources associated with
    the enumeration

Arguments:
    EnumHandle - the enumeration handle

Return Value:

    RPC_S_OK or RPC_S_* error - can fail only if given invalid parameters

--*/
{
    ExtendedErrorInfo *EEInfoChain;

    ASSERT(EnumHandle != NULL);
    ASSERT(EnumHandle->Head != NULL);
    ASSERT(EnumHandle->Signature != EnumSignatureDead);

    if (EnumHandle->Signature != EnumSignatureLive)
        return ERROR_INVALID_PARAMETER;

    EEInfoChain = (ExtendedErrorInfo *)EnumHandle->Head;
    FreeEEInfoChain(EEInfoChain);
    EnumHandle->Head = NULL;
    EnumHandle->Signature = EnumSignatureDead;

    return RPC_S_OK;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorResetEnumeration (
    IN OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Reset the enumeration so that the next call to
        RpcErrorGetNextRecord returns the first record
        again.

Arguments:
    EnumHandle - the enumeration handle

Return Value:

    RPC_S_OK or RPC_S_* error - can fail only if given invalid
        parameters

--*/
{
    ASSERT(EnumHandle != NULL);
    ASSERT(EnumHandle->Head != NULL);
    ASSERT(EnumHandle->Signature != EnumSignatureDead);

    if (EnumHandle->Signature != EnumSignatureLive)
        return ERROR_INVALID_PARAMETER;

    EnumHandle->CurrentPos = EnumHandle->Head;

    return RPC_S_OK;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorGetNumberOfRecords (
    IN RPC_ERROR_ENUM_HANDLE *EnumHandle, 
    OUT int *Records
    )
/*++

Routine Description:

    Gets the number of records in the chain that it currently
    enumerated

Arguments:
    EnumHandle - the enumeration handle
    Records - on output will contain the number of records

Return Value:

    RPC_S_OK or RPC_S_* error - the function cannot fail unless
        given invalid parameters

--*/
{
    ExtendedErrorInfo *CurrentRecord;
    int Count;

    ASSERT(EnumHandle != NULL);
    ASSERT(EnumHandle->Head != NULL);
    ASSERT(EnumHandle->Signature != EnumSignatureDead);

    if (EnumHandle->Signature != EnumSignatureLive)
        return ERROR_INVALID_PARAMETER;

    CurrentRecord = (ExtendedErrorInfo *) EnumHandle->Head;
    Count = 0;
    while (CurrentRecord != NULL)
        {
        Count ++;
        CurrentRecord = CurrentRecord->Next;
        }

    *Records = Count;
    return RPC_S_OK;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorSaveErrorInfo (
    IN RPC_ERROR_ENUM_HANDLE *EnumHandle, 
    OUT PVOID *ErrorBlob, 
    OUT size_t *BlobSize
    )
/*++

Routine Description:

    Saves the eeinfo in the enumeration to a memory block

Arguments:
    EnumHandle - the enumeration handle
    ErrorBlob - on output the allocated and filled in blob
        containing the eeinfo in binary format
    BlobSize - on output the size of the blob

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ULONG EncodedSize;
    ExtendedErrorInfo *EEInfo;
    handle_t PickleHandle;
    char *TempBuffer;
    RPC_STATUS RpcStatus;
    ExtendedErrorInfoPtr *EEInfoPtr;
    size_t MarshallSize;
    HANDLE ProcessHeap;
    PVOID Buffer;

    ASSERT(EnumHandle != NULL);
    ASSERT(EnumHandle->Head != NULL);
    ASSERT(EnumHandle->Signature != EnumSignatureDead);

    if (EnumHandle->Signature != EnumSignatureLive)
        return ERROR_INVALID_PARAMETER;

    // pickle the eeinfo into a buffer
    RpcStatus = MesEncodeDynBufferHandleCreate(&TempBuffer, &EncodedSize, &PickleHandle);
    if (RpcStatus != RPC_S_OK)
        {
        return RpcStatus;
        }

    EEInfo = (ExtendedErrorInfo *) EnumHandle->Head;
    EEInfoPtr = &EEInfo;

    // get the estimated size
    MarshallSize = ExtendedErrorInfoPtr_AlignSize(PickleHandle, EEInfoPtr);

    ProcessHeap = RtlProcessHeap();

    Buffer = RtlAllocateHeap(ProcessHeap, 0, MarshallSize);
    if (Buffer == NULL)
        {
        MesHandleFree(PickleHandle);
        return RPC_S_OUT_OF_MEMORY;
        }
    TempBuffer = (char *)Buffer;

    // re-initialize the handle to fixed buffer
    RpcStatus = MesBufferHandleReset(PickleHandle, 
        MES_FIXED_BUFFER_HANDLE, 
        MES_ENCODE, 
        &TempBuffer, 
        MarshallSize, 
        &EncodedSize);

    if (RpcStatus != RPC_S_OK)
        {
        MesHandleFree(PickleHandle);
        RtlFreeHeap(ProcessHeap, 0, Buffer);
        return RpcStatus;
        }

    // do the pickling itself
    RpcTryExcept
        {
        ExtendedErrorInfoPtr_Encode(PickleHandle, EEInfoPtr);
        }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
        {
        RpcStatus = RpcExceptionCode();
        }
    RpcEndExcept

    if (RpcStatus != RPC_S_OK)
        {
        MesHandleFree(PickleHandle);
        RtlFreeHeap(ProcessHeap, 0, Buffer);
        return RpcStatus;
        }

    // whack out the rest, to prevent random process data going out on the wire/disk
    RpcpMemorySet((unsigned char *)Buffer + EncodedSize, 0, MarshallSize - EncodedSize);

    MesHandleFree(PickleHandle);
    
    *ErrorBlob = Buffer;
    *BlobSize = EncodedSize;
    return RPC_S_OK;
}

RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorLoadErrorInfo (
    IN PVOID ErrorBlob, 
    IN size_t BlobSize, 
    OUT RPC_ERROR_ENUM_HANDLE *EnumHandle
    )
/*++

Routine Description:

    Creates an enumeration from a blob

Arguments:
    ErrorBlob - the blob as obtained by RpcErrorSaveErrorInfo
    BlobSize - the size of the blob as obtained by RpcErrorSaveErrorInfo
    EnumHandle - the enumeration handle allocated by the caller
        and filled on output

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    RPC_STATUS RpcStatus;
    ExtendedErrorInfo *EEInfo;

    RpcStatus = UnpickleEEInfo((unsigned char *)ErrorBlob, BlobSize, &EEInfo);
    if (RpcStatus != RPC_S_OK)
        return RpcStatus;

    InitializeEnumHandleWithEEInfo(EEInfo, EnumHandle);

    return RPC_S_OK;
}

RPC_STATUS
AddPrivateRecord (
    IN ExtendedErrorInfo *ErrorInfo
    )
/*++

Routine Description:

    Adds the supplied record to the top of the chain in the teb

    N.B. There can be no additional failure paths in the callers
    after this function. This is because it will chain this
    record to the teb, and if we bail out later, the teb will
    point to invalid record.

Arguments:
    ErrorInfo - the eeinfo record to add to the chain

Return Value:

    RPC_S_OK or RPC_S_* error - the function cannot fail if the
        RPC per-thread object has already been allocated for this
        thread

--*/
{
    THREAD *Thread;

    Thread = ThreadSelf();
    if (Thread == NULL)
        return RPC_S_OUT_OF_MEMORY;

    ErrorInfo->Next = Thread->GetEEInfo();
    Thread->SetEEInfo(ErrorInfo);

    return RPC_S_OK;
}

inline LPWSTR
ReplaceWithEmptyStringIfNull (
    IN LPWSTR String
    )
{
    return (String ? String : L"");
}

inline LPSTR
ReplaceWithEmptyStringIfNull (
    IN LPSTR String
    )
{
    return (String ? String : "");
}

void
RpcpErrorAddRecord (
    ULONG GeneratingComponent,
    ULONG Status,
    USHORT DetectionLocation,
    int NumberOfParameters,
    ExtendedErrorParam *Params
    )
/*++

Routine Description:

    Adds an extended error info to the thread. The
    following is a description of how fields are set:
    Next - will be set to the next record.
    ComputerName - will be set to not-present (eecnpNotPresent)
    ProcessID - will be set to the process ID
    TimeStamp - will be set to the current time
    GeneratingComponent - set to GeneratingComponent
    Status - set to Status
    DetectionLocation - set to DetectionLocation
    Flags - set to 0.
    nLen - set to NumberOfParameters
    Params will be copied to the parameters array. The caller can
        allocate them off the stack if it wants.

    N.B. The runtime should never directly call this function. If it
    needs to add records, it should call one of the overloaded 
    RpcpErrorAddRecord functions below. If there isn't one suitable,
    add one. All the RpcpErrorAddRecord functions below are just
    syntactic sugar for this function.

Arguments:
    GeneratingComponent - will be set in the record
    Status - will be set in the record
    DetectionLocation - will be set in the record
    NumberOfParameters - the number of parameters in the Params array
    Params - the parameters to add

Return Value:

    void - this is a best effort - no guarantees. Even if we
    return failure, there's little the caller can do about it.

--*/
{
    ExtendedErrorInfo *NewRecord;
    RPC_STATUS RpcStatus;
    int i;

    LogEvent(SU_EEINFO, 
        (char)GeneratingComponent, 
        ULongToPtr(Status), 
        ULongToPtr(DetectionLocation), 
        (NumberOfParameters > 0) ? Params[0].LVal : 0);

    NewRecord = AllocateExtendedErrorInfoRecord(NumberOfParameters);
    if (NewRecord == NULL)
        return;

    InitializePrivateEEInfo(NewRecord);
    NewRecord->DetectionLocation = DetectionLocation;
    NewRecord->GeneratingComponent = GeneratingComponent;
    NewRecord->Status = Status;
    
    for (i = 0; i < NumberOfParameters; i ++)
        {
        // all parameter types requiring an allocation have already
        // been copied by our caller - no need to clone - we can just
        // do shallow copy
        RpcpMemoryCopy(&NewRecord->Params[i], &Params[i], sizeof(ExtendedErrorParam));
        }

    RpcStatus = AddPrivateRecord(NewRecord);
    if (RpcStatus != RPC_S_OK)
        {
        FreeEEInfoRecord(NewRecord);
        }
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN short Short,
    IN ULONG Long2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;
    Params[1].Type = eeptiShortVal;
    Params[1].IVal = Short;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN short Short,
    IN ULONG Long2,
    IN ULONG Long3
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;
    Params[1].Type = eeptiShortVal;
    Params[1].IVal = Short;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long2;
    Params[3].Type = eeptiLongVal;
    Params[3].LVal = (long)Long3;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        4,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN ULONG Long2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;
    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)Long2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String1,
    IN LPWSTR String2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];
    RPC_STATUS RpcStatus;
    int i;
    LPWSTR Strings[2];

    Strings[0] = ReplaceWithEmptyStringIfNull(String1);
    Strings[1] = ReplaceWithEmptyStringIfNull(String2);
    for (i = 0; i < 2; i ++)
        {
        RpcStatus = ConvertPublicStringToPrivateString(Strings[i],
            &Params[i].UnicodeString);
        if (RpcStatus == RPC_S_OK)
            {
            Params[i].Type = eeptiUnicodeString;
            }
        else
            {
            Params[i].Type = eeptiNone;
            }
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);

}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String1,
    IN LPWSTR String2,
    IN ULONG Long1,
    IN ULONG Long2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];
    RPC_STATUS RpcStatus;
    int i;
    LPWSTR Strings[2];

    Strings[0] = ReplaceWithEmptyStringIfNull(String1);
    Strings[1] = ReplaceWithEmptyStringIfNull(String2);
    for (i = 0; i < 2; i ++)
        {
        RpcStatus = ConvertPublicStringToPrivateString(Strings[i],
            &Params[i].UnicodeString);
        if (RpcStatus == RPC_S_OK)
            {
            Params[i].Type = eeptiUnicodeString;
            }
        else
            {
            Params[i].Type = eeptiNone;
            }
        }

    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long1;
    Params[3].Type = eeptiLongVal;
    Params[3].LVal = (long)Long2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        4,
        Params);

}


void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String1,
    IN LPWSTR String2,
    IN ULONG Long1,
    IN ULONGLONG PVal1
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];
    RPC_STATUS RpcStatus;
    int i;
    LPWSTR Strings[2];

    Strings[0] = ReplaceWithEmptyStringIfNull(String1);
    Strings[1] = ReplaceWithEmptyStringIfNull(String2);
    for (i = 0; i < 2; i ++)
        {
        RpcStatus = ConvertPublicStringToPrivateString(Strings[i],
            &Params[i].UnicodeString);
        if (RpcStatus == RPC_S_OK)
            {
            Params[i].Type = eeptiUnicodeString;
            }
        else
            {
            Params[i].Type = eeptiNone;
            }
        }

    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long1;
    Params[3].Type = eeptiPointerVal;
    Params[3].PVal = (long)PVal1;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        4,
        Params);

}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long1,
    IN ULONG Long2,
    IN LPWSTR String,
    IN ULONG Long3
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];
    RPC_STATUS RpcStatus;

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long1;
    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)Long2;
    Params[3].Type = eeptiLongVal;
    Params[3].LVal = (long)Long3;

    RpcStatus = ConvertPublicStringToPrivateString(
        ReplaceWithEmptyStringIfNull(String),
        &Params[2].UnicodeString);
    if (RpcStatus == RPC_S_OK)
        {
        Params[2].Type = eeptiUnicodeString;
        }
    else
        {
        Params[2].Type = eeptiNone;
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        4,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String,
    IN ULONG Long
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];
    RPC_STATUS RpcStatus;

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;

    RpcStatus = ConvertPublicStringToPrivateString(
        ReplaceWithEmptyStringIfNull(String),
        &Params[1].UnicodeString);
    if (RpcStatus == RPC_S_OK)
        {
        Params[1].Type = eeptiUnicodeString;
        }
    else
        {
        Params[1].Type = eeptiNone;
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String,
    IN ULONG Long1,
    IN ULONG Long2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];
    RPC_STATUS RpcStatus;

    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)Long1;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long2;

    RpcStatus = ConvertPublicStringToPrivateString(
        ReplaceWithEmptyStringIfNull(String),
        &Params[0].UnicodeString);
    if (RpcStatus == RPC_S_OK)
        {
        Params[0].Type = eeptiUnicodeString;
        }
    else
        {
        Params[0].Type = eeptiNone;
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPWSTR String
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[1];
    RPC_STATUS RpcStatus;

    RpcStatus = ConvertPublicStringToPrivateString(
        ReplaceWithEmptyStringIfNull(String),
        &Params[0].UnicodeString);
    if (RpcStatus == RPC_S_OK)
        {
        Params[0].Type = eeptiUnicodeString;
        }
    else
        {
        Params[0].Type = eeptiNone;
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        1,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN LPSTR String
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[1];
    RPC_STATUS RpcStatus;

    RpcStatus = ConvertPublicStringToPrivateString(
        ReplaceWithEmptyStringIfNull(String),
        &Params[0].AnsiString);
    if (RpcStatus == RPC_S_OK)
        {
        Params[0].Type = eeptiAnsiString;
        }
    else
        {
        Params[0].Type = eeptiNone;
        }

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        1,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN ULONG Long2,
    IN ULONG Long3
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;
    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)Long2;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long3;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONGLONG PVal1,
    IN ULONGLONG PVal2,
    IN ULONG Long
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];

    Params[0].Type = eeptiPointerVal;
    Params[0].PVal = PVal1;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal2;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONGLONG PVal1,
    IN ULONGLONG PVal2,
    IN ULONG Long1,
    IN ULONG Long2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];

    Params[0].Type = eeptiPointerVal;
    Params[0].PVal = PVal1;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal2;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)Long1;
    Params[3].Type = eeptiLongVal;
    Params[3].LVal = (long)Long2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        4,
        Params);
}


void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONGLONG PVal1,
    IN ULONGLONG PVal2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];

    Params[0].Type = eeptiPointerVal;
    Params[0].PVal = PVal1;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONGLONG PVal1,
    IN ULONG LVal1
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];

    Params[0].Type = eeptiPointerVal;
    Params[0].PVal = PVal1;
    Params[1].Type = eeptiLongVal;
    Params[1].PVal = LVal1;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN ULONGLONG PVal1
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[2];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = Long;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal1;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        2,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long,
    IN ULONGLONG PVal1,
    IN ULONGLONG PVal2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal1;
    Params[2].Type = eeptiPointerVal;
    Params[2].PVal = PVal2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG Long
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[1];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)Long;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        1,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG LVal1,
    IN ULONGLONG PVal1,
    IN ULONG LVal2
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[3];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)LVal1;
    Params[1].Type = eeptiPointerVal;
    Params[1].PVal = PVal1;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)LVal2;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}

void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG LVal1,
    IN ULONG LVal2,
    IN ULONG LVal3,
    IN ULONGLONG PVal1
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)LVal1;
    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)LVal2;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)LVal3;
    Params[3].Type = eeptiPointerVal;
    Params[3].PVal = PVal1;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}


void
RpcpErrorAddRecord (
    IN ULONG GeneratingComponent,
    IN ULONG Status,
    IN USHORT DetectionLocation,
    IN ULONG LVal1,
    IN ULONG LVal2,
    IN ULONG LVal3,
    IN ULONG LVal4
    )
/*++

    See description of RpcpErrorAddRecord(ULONG, ULONG, 
        USHORT, int, ExtendedErrorParam*) above

--*/
{
    ExtendedErrorParam Params[4];

    Params[0].Type = eeptiLongVal;
    Params[0].LVal = (long)LVal1;
    Params[1].Type = eeptiLongVal;
    Params[1].LVal = (long)LVal2;
    Params[2].Type = eeptiLongVal;
    Params[2].LVal = (long)LVal3;
    Params[3].Type = eeptiLongVal;
    Params[3].PVal = LVal4;

    RpcpErrorAddRecord (GeneratingComponent,
        Status,
        DetectionLocation,
        3,
        Params);
}


RPCRTAPI
RPC_STATUS 
RPC_ENTRY
RpcErrorAddRecord (
    IN RPC_EXTENDED_ERROR_INFO *ErrorInfo
    )
/*++

Routine Description:

    Adds the supplied record to the top of the chain in the teb

Arguments:
    ErrorInfo - the eeinfo record to add to the chain

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *NewRecord;
    RPC_STATUS RpcStatus;

    if (ErrorInfo->Version != RPC_EEINFO_VERSION)
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->ComputerName != NULL)
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->Flags != 0)
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->NumberOfParameters < 0)
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->NumberOfParameters > MaxNumberOfEEInfoParams)
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->DetectionLocation != 0)
        return ERROR_INVALID_PARAMETER;

    // EEInfoGCCOM can come externally. If it's not EEInfoGCCOM, it must be 0
    if ((ErrorInfo->GeneratingComponent != 0) && (ErrorInfo->GeneratingComponent != EEInfoGCCOM))
        return ERROR_INVALID_PARAMETER;

    if (ErrorInfo->ProcessID != 0)
        return ERROR_INVALID_PARAMETER;

    NewRecord = AllocateExtendedErrorInfoRecord(ErrorInfo->NumberOfParameters);
    if (NewRecord == NULL)
        return RPC_S_OUT_OF_MEMORY;

    RpcStatus = ConvertPublicEEInfoToPrivateEEInfo(ErrorInfo, EEInfoDLApi, NewRecord);
    if (RpcStatus != RPC_S_OK)
        {
        FreeEEInfoRecordShallow(NewRecord);
        return RpcStatus;
        }

    RpcStatus = AddPrivateRecord(NewRecord);
    if (RpcStatus != RPC_S_OK)
        {
        FreeEEInfoRecord(NewRecord);
        }

    return RpcStatus;
}

RPCRTAPI
void 
RPC_ENTRY
RpcErrorClearInformation (
    void
    )
/*++

Routine Description:

    Clears the existing eeinfo on the teb (if any)

Arguments:
    void

Return Value:

    void

--*/
{
    ExtendedErrorInfo *EEInfo;
    THREAD *Thread;

    Thread = RpcpGetThreadPointer();
    if (Thread == NULL)
        return;

    EEInfo = Thread->GetEEInfo();
    Thread->SetEEInfo(NULL);
    FreeEEInfoChain(EEInfo);
}

BOOL
KnockOffLastButOneEEInfoRecord (
    IN ExtendedErrorInfo *EEInfo
    )
/*++

Routine Description:

    Will delete the last-but-one record from the chain. If there
    are two or less record, nothing is deleted, and FALSE gets
    returned.

Arguments:
    EEInfo - the extended error info chain

Return Value:
    TRUE - a record was deleted
    FALSE - there were two or less records, and nothing was deleted.

--*/
{
    ExtendedErrorInfo *NextRecord, *LastButOneRecord;
    ExtendedErrorInfo *PreviousRecord;

    LastButOneRecord = NextRecord = EEInfo;
    while ((NextRecord != NULL) && (NextRecord->Next != NULL))
        {
        PreviousRecord = LastButOneRecord;
        LastButOneRecord = NextRecord;
        NextRecord = NextRecord->Next;
        }

    if ((NextRecord == EEInfo) || (LastButOneRecord == EEInfo))
        {
        return FALSE;
        }

    PreviousRecord->Next = NextRecord;
    // indicate that the chain has been broken
    PreviousRecord->Flags |= EEInfoNextRecordsMissing;
    NextRecord->Flags |= EEInfoPreviousRecordsMissing;
    // move the computer name up if necessary
    if ((LastButOneRecord->ComputerName.Type == eecnpPresent)
        && (NextRecord->ComputerName.Type == eecnpNotPresent))
        {
        // N.B. Not covered by unit tests
        LastButOneRecord->ComputerName.Type = eecnpNotPresent;
        NextRecord->ComputerName.Type = eecnpPresent;
        NextRecord->ComputerName.Name.nLength = LastButOneRecord->ComputerName.Name.nLength;
        NextRecord->ComputerName.Name.pString = LastButOneRecord->ComputerName.Name.pString;
        }
    FreeEEInfoRecord(LastButOneRecord);
    return TRUE;
}

RPC_STATUS
TrimEEInfoToLengthByRemovingRecords (
    IN ExtendedErrorInfo *EEInfo,
    IN size_t MaxLength,
    OUT BOOL *fTrimmedEnough,
    OUT size_t *NeededLength
    )
/*++

Routine Description:

    This function removes records, until either two records are
    left, or the pickled length drops below MaxLength. If the
    pickled length is below the MaxLength to start with, no
    records should be dropped. The records are dropped starting
    from the last-but-one, and going backwards (towards the
    current record). The previous and next records should
    have their chain broken flags set, and the computer name
    should be moved up the chain, if the last record has
    no computer name.

Arguments:
    EEInfo - the EE chain
    MaxLength - the length that we need to trim to.
    fTrimmedEnough - will be set to TRUE if the pickled length
        on return from this function fits in MaxLength. Undefined
        if the return value is not RPC_S_OK
    NeededLength - if fTrimmedEnough was set to TRUE, and the
        return value is RPC_S_OK, the current pickled length.
        Otherwise, this value must not be touched (i.e. use
        a local variable until you're sure that both conditions
        are true).

Return Value:
    RPC_S_OK on success.
    != RPC_S_OK on error. On error, fTrimmedEnough is undefined, and
        NeededLength is not touched.

--*/
{
    ULONG EncodedSize;
    handle_t PickleHandle = NULL;
    char *TempBuffer;
    RPC_STATUS RpcStatus;
    ExtendedErrorInfoPtr *EEInfoPtr;
    PVOID Buffer = NULL;
    size_t CurrentlyNeededLength;
    size_t PickleLength;
    size_t BufferLength;
    BOOL Result;

    RpcStatus = MesEncodeDynBufferHandleCreate(&TempBuffer, &EncodedSize, &PickleHandle);
    if (RpcStatus != RPC_S_OK)
        {
        return RpcStatus;
        }

    // our first goal is to drive the pickled length to less than 2 times
    // the MaxLength. Since the actual pickling often takes less than the
    // estimated, we will do the fine tuning by actually pickling and measuring
    // the resulting size. For the rough tuning, we will use the estimate, and
    // knock off it, if we are over the estimate
    CurrentlyNeededLength = MaxLength * 2;
    while (TRUE)
        {
        EEInfoPtr = &EEInfo;

        // get the estimated size
        PickleLength = ExtendedErrorInfoPtr_AlignSize(PickleHandle, EEInfoPtr);
        if (PickleLength <= CurrentlyNeededLength)
            {
            break;
            }

        // knock off the last-but-one element
        Result = KnockOffLastButOneEEInfoRecord(EEInfo);
        if (Result == FALSE)
            {
            *fTrimmedEnough = FALSE;
            goto SuccessCleanupAndExit;
            }
        }

    // here, the PickleHandle should be valid, and ready for actual pickling
    // do the fine-tuned trimming - actually pickle, and see whether it fits
    Buffer = MIDL_user_allocate(PickleLength);
    if (Buffer == NULL)
        {
        RpcStatus = RPC_S_OUT_OF_MEMORY;
        goto CleanupAndExit;
        }

    BufferLength = PickleLength;

    TempBuffer = (char *)Buffer;

    CurrentlyNeededLength = MaxLength;

    while (TRUE)
        {
        // re-initialize the handle to fixed buffer
        RpcStatus = MesBufferHandleReset(PickleHandle, 
            MES_FIXED_BUFFER_HANDLE, 
            MES_ENCODE, 
            &TempBuffer, 
            BufferLength, 
            &EncodedSize);

        if (RpcStatus != RPC_S_OK)
            {
            goto CleanupAndExit;
            }

        RpcTryExcept
            {
            ExtendedErrorInfoPtr_Encode(PickleHandle, EEInfoPtr);
            }
        RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
            {
            RpcStatus = RpcExceptionCode();
            }
        RpcEndExcept

        if (RpcStatus != RPC_S_OK)
            {
            goto CleanupAndExit;
            }

        if (EncodedSize <= CurrentlyNeededLength)
            {
            *fTrimmedEnough = TRUE;
            *NeededLength = EncodedSize;
            goto SuccessCleanupAndExit;
            }

        Result = KnockOffLastButOneEEInfoRecord(EEInfo);
        if (Result == FALSE)
            {
            *fTrimmedEnough = FALSE;
            goto SuccessCleanupAndExit;
            }
        }

SuccessCleanupAndExit:
    RpcStatus = RPC_S_OK;

CleanupAndExit:
    if (Buffer != NULL)
        {
        MIDL_user_free(Buffer);
        }

    if (PickleHandle != NULL)
        {
        MesHandleFree(PickleHandle);
        }
    return RpcStatus;
}

RPC_STATUS
GetLengthOfPickledEEInfo (
    IN ExtendedErrorInfo *EEInfo,
    OUT size_t *NeededLength
    )
/*++

Routine Description:

    Calculate the length of the given eeinfo when pickled. It 
    does that by pickling it in a temporary buffer and 
    checking the resulting length.

Arguments:
    ErrorInfo - the eeinfo chain whose length we need to calculate
    NeededLength - the length in bytes

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ULONG EncodedSize;
    handle_t PickleHandle = NULL;
    char *TempBuffer;
    RPC_STATUS RpcStatus;
    ExtendedErrorInfoPtr *EEInfoPtr;
    PVOID Buffer = NULL;
    size_t MarshallSize;

    RpcStatus = MesEncodeDynBufferHandleCreate(&TempBuffer, &EncodedSize, &PickleHandle);
    if (RpcStatus != RPC_S_OK)
        {
        return RpcStatus;
        }

    EEInfoPtr = &EEInfo;

    // get the estimated size
    MarshallSize = ExtendedErrorInfoPtr_AlignSize(PickleHandle, EEInfoPtr);
    Buffer = MIDL_user_allocate(MarshallSize);
    if (Buffer == NULL)
        {
        RpcStatus = RPC_S_OUT_OF_MEMORY;
        goto CleanupAndExit;
        }

    TempBuffer = (char *)Buffer;

    // re-initialize the handle to fixed buffer
    RpcStatus = MesBufferHandleReset(PickleHandle, 
        MES_FIXED_BUFFER_HANDLE, 
        MES_ENCODE, 
        &TempBuffer, 
        MarshallSize, 
        &EncodedSize);

    if (RpcStatus != RPC_S_OK)
        {
        goto CleanupAndExit;
        }

    RpcTryExcept
        {
        ExtendedErrorInfoPtr_Encode(PickleHandle, EEInfoPtr);
        }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
        {
        RpcStatus = RpcExceptionCode();
        }
    RpcEndExcept

    if (RpcStatus != RPC_S_OK)
        {
        goto CleanupAndExit;
        }

    *NeededLength = EncodedSize;

CleanupAndExit:
    if (Buffer != NULL)
        {
        MIDL_user_free(Buffer);
        }

    if (PickleHandle != NULL)
        {
        MesHandleFree(PickleHandle);
        }
    return RpcStatus;
}

RPC_STATUS
TrimEEInfoToLengthByRemovingStrings (
    IN ExtendedErrorInfo *EEInfo,
    IN size_t MaxLength,
    OUT BOOL *fTrimmedEnough,
    OUT size_t *NeededLength
    )
/*++

Routine Description:

    Try to trim the eeinfo to the given length by whacking any
    strings in the eeinfo chain. After each string is whacked
    a re-measurement is made

Arguments:
    ErrorInfo - the eeinfo chain that we need to fit in MaxLength
        bytes.
    MaxLength - the length we need to trim to
    fTrimmedEnough - non-zero if we were able to trim the length below
        MaxLength.
    NeededLength - the length in bytes of the trimmed eeinfo. Not
        touched if fTrimmedEnough is FALSE.

    N.B. This function should only be called after 
    TrimEEInfoToLengthByRemovingRecords

Return Value:

    RPC_S_OK or RPC_S_* error

--*/
{
    ExtendedErrorInfo *CurrentRecord;
    size_t CurrentLength;
    int i;
    RPC_STATUS RpcStatus;
    BOOL TrimLongParam;
    void *ParameterToTrim;

    // we shouldn't be here if there are more than two records
    if (EEInfo->Next && EEInfo->Next->Next)
        {
        ASSERT(0);
        }

    CurrentRecord = EEInfo;
    while (CurrentRecord != NULL)
        {
        // trim the parameters and remeasure. If still nothing, move on to
        // the computer name.
        for (i = 0; i < EEInfo->nLen; i ++)
            {
            TrimLongParam = FALSE;
            if ((CurrentRecord->Params[i].Type == eeptiAnsiString) 
                || (CurrentRecord->Params[i].Type == eeptiUnicodeString))
                {
                TrimLongParam = TRUE;
                // both string pointers occupy the same memory location,
                // so it is ok to free either
                ParameterToTrim = CurrentRecord->Params[i].AnsiString.pString;
                }
            else if (CurrentRecord->Params[i].Type == eeptiBinary)
                {
                TrimLongParam = TRUE;
                ParameterToTrim = CurrentRecord->Params[i].Blob.pBlob;
                }

            if (TrimLongParam)
                {
                MIDL_user_free(CurrentRecord->Params[i].AnsiString.pString);
                CurrentRecord->Params[i].Type = eeptiNone;

                // remeasure
                RpcStatus = GetLengthOfPickledEEInfo(EEInfo, &CurrentLength);
                if (RpcStatus != RPC_S_OK)
                    return RpcStatus;

                if (CurrentLength <= MaxLength)
                    {
                    *NeededLength = CurrentLength;
                    *fTrimmedEnough = TRUE;
                    return RPC_S_OK;
                    }
                }
            }

        // if the computer name is there, try to trim it. If nothing,
        // move on to the next record
        if (CurrentRecord->ComputerName.Type == eecnpPresent)
            {
            // N.B. Not covered by unit tests
            MIDL_user_free(CurrentRecord->ComputerName.Name.pString);
            CurrentRecord->ComputerName.Type = eecnpNotPresent;

            RpcStatus = GetLengthOfPickledEEInfo(EEInfo, &CurrentLength);
            if (RpcStatus != RPC_S_OK)
                return RpcStatus;

            if (CurrentLength <= MaxLength)
                {
                // N.B. Not covered by unit tests
                *NeededLength = CurrentLength;
                *fTrimmedEnough = TRUE;
                return RPC_S_OK;
                }
            // N.B. Not covered by unit tests
            }

        CurrentRecord = CurrentRecord->Next;
        }

    // N.B. In the current implementation, the minimum fragment length
    // belongs to LRPC, and is 0xb8. At this length, two records
    // with strings stripped always fit. Therefore, we can never be
    // here. The code below is untested, and is left only for future
    // work where we have a transport supporting fragment length
    // which doesn't hold two records with strings stripped
    ASSERT(0);
    // if we are here, obviously we couldn't trim enough
    *fTrimmedEnough = FALSE;
    return RPC_S_OK;
}

void
TrimEEInfoToLength (
    IN size_t MaxLength,
    OUT size_t *NeededLength
    )
/*++

Routine Description:

    Some protocols don't allow transmitting arbitrary lengths
    of information. This function will attempt to trim the pickled
    length of the existing error information so as to fit MaxLength.
    First, it will try to knock off records, starting from the
    last-but-one, and going back. If this is not sufficient, it will
    whack any string arguments/computer names in the record. If this
    is also, not sufficient, it should drop the top record. This should
    leave the total length to be about 128 bytes. All protocols must
    be able to transmit that, as this routine cannot trim it any
    further.
    If MaxLength is larger than the current pickled length, no trimming
    is done, and the actual pickled length will be returned in
    NeededLength

Arguments:
    MaxLength - the maximum length for this chain.
    NeededLength - on success, how much we actually need to transfer
        the existing extended error info. This must be less than
        MaxLength. If the function cannot get estimation for some
        reason (probably out-of-memory), or there is no extended
        error information, it will return 0 in this parameter.

Return Value:
    void

--*/
{
    RPC_STATUS RpcStatus;
    BOOL fTrimmedEnough;
    ExtendedErrorInfo *EEInfo;
    THREAD *Thread;
    ExtendedErrorInfo *LastRecord;

    ASSERT(MaxLength >= MinimumTransportEEInfoLength);

    *NeededLength = 0;
    Thread = RpcpGetThreadPointer();
    if (Thread == NULL)
        return;

    EEInfo = Thread->GetEEInfo();
    if (EEInfo == NULL)
        return;

    RpcStatus = TrimEEInfoToLengthByRemovingRecords(EEInfo, MaxLength, &fTrimmedEnough, NeededLength);
    if (RpcStatus != RPC_S_OK)
        return;

    // if fTrimmedEnough is set, NeededLength should have been set
    if (fTrimmedEnough == TRUE)
        {
        ASSERT(*NeededLength <= MaxLength);
        return;
        }
    ASSERT(*NeededLength == 0);

    RpcStatus = TrimEEInfoToLengthByRemovingStrings(EEInfo, MaxLength, &fTrimmedEnough, NeededLength);
    if (RpcStatus != RPC_S_OK)
        return;

    // if fTrimmedEnough is set, NeededLength should have been set
    if (fTrimmedEnough == TRUE)
        {
        ASSERT(*NeededLength <= MaxLength);
        return;
        }

    // N.B. In the current implementation, the minimum fragment length
    // belongs to LRPC, and is 0xb8. At this length, two records
    // with strings stripped always fit. Therefore, we can never be
    // here. The code below is untested, and is left only for future
    // work where we have a transport supporting fragment length
    // which doesn't hold two records with strings stripped

    ASSERT(0);
    // again, we couldn't trim it enough
    // drop the first record

    // make sure there are exactly two records
    // this is so, because if we have only one record,
    // it should have fit by now. If we had more than two
    // records, there is a bug in the trimming records code
    ASSERT(EEInfo->Next);
    ASSERT(EEInfo->Next->Next == NULL);

    LastRecord = EEInfo->Next;
    FreeEEInfoRecord(EEInfo);
    EEInfo = LastRecord;
    Thread->SetEEInfo(LastRecord);

#if DBG
    RpcStatus = GetLengthOfPickledEEInfo(EEInfo, NeededLength);
    if (RpcStatus != RPC_S_OK)
        return;

    ASSERT(*NeededLength <= MaxLength);
#endif
}

size_t
EstimateSizeOfEEInfo (
    void
    )
/*++

Routine Description:

    Takes the EEInfo from the teb (if any) and calculates the size
    of the pickled eeinfo

Arguments:
    void

Return Value:

    the size or 0 if it fails

--*/
{
    ExtendedErrorInfo *EEInfo;
    THREAD *Thread;
    RPC_STATUS RpcStatus;
    size_t NeededLength;

    Thread = RpcpGetThreadPointer();
    if (Thread == NULL)
        return 0;

    EEInfo = Thread->GetEEInfo();
    if (EEInfo == NULL)
        return 0;

    RpcStatus = GetLengthOfPickledEEInfo(EEInfo, &NeededLength);
    if (RpcStatus != RPC_S_OK)
        return 0;

    return NeededLength;
}

RPC_STATUS
PickleEEInfo (
    IN ExtendedErrorInfo *EEInfo,
    IN OUT unsigned char *Buffer,
    IN size_t BufferSize
    )
/*++

Routine Description:

    This routine does the actual pickling in a user supplied buffer.
    The buffer must have been allocated large enough to hold all
    pickled data. Some of the other functions should have been
    used to get the size of the pickled data and the buffer
    should have been allocated appropriately

Arguments:
    Buffer - the actual Buffer to pickle into
    BufferSize - the size of the Buffer.

Return Value:
    RPC_S_OK if the pickling was successful.
    other RPC_S_* codes if it failed.

--*/
{
    ULONG EncodedSize;
    handle_t PickleHandle = NULL;
    RPC_STATUS RpcStatus;
    ExtendedErrorInfoPtr *EEInfoPtr;

    ASSERT(((ULONG_PTR)Buffer & 0x7) == 0);
    RpcStatus = MesEncodeFixedBufferHandleCreate((char *)Buffer, BufferSize, &EncodedSize, &PickleHandle);
    if (RpcStatus != RPC_S_OK)
        {
        return RpcStatus;
        }

    EEInfoPtr = &EEInfo;

    RpcTryExcept
        {
        ExtendedErrorInfoPtr_Encode(PickleHandle, EEInfoPtr);
        }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
        {
        RpcStatus = RpcExceptionCode();
        }
    RpcEndExcept

    ASSERT(EncodedSize <= BufferSize);

    MesHandleFree(PickleHandle);
    return RpcStatus;
}

RPC_STATUS
UnpickleEEInfo (
    IN OUT unsigned char *Buffer,
    IN size_t BufferSize,
    OUT ExtendedErrorInfo **NewEEInfo
   )
/*++

Routine Description:

    This routine does the actual pickling in a user supplied buffer.
    The buffer must have been allocated large enough to hold all
    pickled data. Some of the other functions should have been
    used to get the size of the pickled data and the buffer
    should have been allocated appropriately

Arguments:
    Buffer - the actual Buffer to pickle into
    BufferSize - the size of the Buffer.

Return Value:
    RPC_S_OK if the pickling was successful.
    other RPC_S_* codes if it failed.

--*/
{
    ExtendedErrorInfo *EEInfo;
    handle_t PickleHandle;
    RPC_STATUS RpcStatus;
    ExtendedErrorInfoPtr *EEInfoPtr;

    RpcStatus = MesDecodeBufferHandleCreate((char *)Buffer, BufferSize, &PickleHandle);
    if (RpcStatus != RPC_S_OK)
        {
        return RpcStatus;
        }

    EEInfoPtr = &EEInfo;
    EEInfo = NULL;
    RpcTryExcept
        {
        ExtendedErrorInfoPtr_Decode(PickleHandle, EEInfoPtr);
        }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
        {
        RpcStatus = RpcExceptionCode();
        }
    RpcEndExcept

    MesHandleFree(PickleHandle);

    if (RpcStatus == RPC_S_OK)
        *NewEEInfo = EEInfo;

    return RpcStatus;
}

void
NukeStaleEEInfoIfNecessary (
    IN RPC_STATUS exception
    )
/*++

Routine Description:

    Matches the given error code to the error code in the
    first record of the eeinfo chain in the teb. If they match
    or if there is *no* Win32<->NT_STATUS correspondence b/n them
    the eeinfo in the teb is nuked

Arguments:
    exception - the error code to match against

Return Value:
    void

--*/
{
    THREAD *Thread;
    ExtendedErrorInfo *EEInfo;
    long ExceptionNtStatus;
    long EEInfoNtStatus;

    Thread = RpcpGetThreadPointer();
    if (Thread)
        {
        EEInfo = Thread->GetEEInfo();
        if (EEInfo && Thread->Context)
            {
            // there is extended info - try to match it to what we have
            ExceptionNtStatus = I_RpcMapWin32Status(exception);
            EEInfoNtStatus = I_RpcMapWin32Status(EEInfo->Status);
            if (EEInfoNtStatus != ExceptionNtStatus)
                {
                // they are not the same - nuke the stale info
                // to prevent confusion
                RpcpPurgeEEInfoFromThreadIfNecessary(Thread);
                }
            }
        }
}

LPWSTR
AllocateAndGetComputerName (
    IN ComputerNameAllocators AllocatorToUse,
    IN COMPUTER_NAME_FORMAT NameToRetrieve,
    IN size_t ExtraBytes,
    IN int StartingOffset,
    OUT DWORD *Size
    )
/*++

Routine Description:

    Allocates space for the computer name and gets it

Arguments:
    Size - on output the size of the string, including
        the terminating NULL

Return Value:
    The computer name or NULL of out-of-memory

--*/
{
    DWORD LocalSize = 0;
    BOOL Result;
    LPWSTR Buffer = NULL;
    DWORD LastError;

    Result = GetComputerNameEx(NameToRetrieve, 
        Buffer, 
        &LocalSize);
    ASSERT(Result == 0);

    LastError = GetLastError();
    if (LastError == ERROR_MORE_DATA)
        {
        if (AllocatorToUse == cnaMidl)
            {
            Buffer = (LPWSTR)MIDL_user_allocate(LocalSize * sizeof(RPC_CHAR) + ExtraBytes);
            }
        else
            {
            ASSERT(AllocatorToUse == cnaNew);
            Buffer = (RPC_CHAR *)new char[LocalSize * sizeof(RPC_CHAR) + ExtraBytes];
            }
        if (Buffer)
            {
            Result = GetComputerNameEx(NameToRetrieve, 
                (RPC_CHAR *)((char *)Buffer + StartingOffset), 
                &LocalSize);
            if (Result == 0)
                {
                if (AllocatorToUse == cnaMidl)
                    {
                    MIDL_user_free(Buffer);
                    }
                else
                    {
                    delete Buffer;
                    }
                Buffer = NULL;
                }
            else
                {
                // sometimes GetComputerNameEx returns the size
                // without the terminating NULL regardless of what
                // the MSDN says. The base group (Earhart, NeillC)
                // know about it but won't change it for now.
                // Code it in such a way that it works regardless
                // of when and if they change it.
                *Size = wcslen((RPC_CHAR *)((char *)Buffer + StartingOffset)) + 1;
                }
            }
        }

    return Buffer;
}

void
AddComputerNameToChain (
    ExtendedErrorInfo *EEInfo
    )
/*++

Routine Description:

    Checks the first record in eeinfo, and if it doesn't have
    a computer name to it, adds it.

Arguments:
    EEInfo - the eeinfo chain to add the computer name to

Return Value:
    void - best effort - no guarantees.

--*/
{
    LPWSTR Buffer;
    DWORD Size;

    if (EEInfo->ComputerName.Type == eecnpNotPresent)
        {
        Buffer = AllocateAndGetComputerName(cnaMidl,
            ComputerNamePhysicalDnsHostname,
            0,      // extra bytes
            0,      // starting offset
            &Size);
        if (Buffer)
            {
            EEInfo->ComputerName.Type = eecnpPresent;
            EEInfo->ComputerName.Name.nLength = (CSHORT)Size;
            EEInfo->ComputerName.Name.pString = Buffer;
            }
        }
}

void
StripComputerNameIfRedundant (
    ExtendedErrorInfo *EEInfo
    )
/*++

Routine Description:

    Checks the first record in eeinfo, and if it does have
    a computer name to it and it is the same as the computer
    name of this machine, remove it. This is done to keep
    the length of the chain short during local calls using
    remote protocols

Arguments:
    EEInfo - the eeinfo chain to remove the computer name from

Return Value:
    void

--*/
{
    LPWSTR Buffer = NULL;
    DWORD Size;

    if (EEInfo->ComputerName.Type == eecnpPresent)
        {
        Buffer = AllocateAndGetComputerName(cnaMidl,
            ComputerNamePhysicalDnsHostname,
            0,  // extra bytes
            0,      // starting offset
            &Size);
        if (Buffer)
            {
            if (Size != (DWORD)EEInfo->ComputerName.Name.nLength)
                goto CleanupAndExit;

            // The strings are Unicode - need to multiply by two
            if (RpcpMemoryCompare(Buffer, EEInfo->ComputerName.Name.pString, Size * 2) == 0)
                {
                MIDL_user_free(EEInfo->ComputerName.Name.pString);
                EEInfo->ComputerName.Type = eecnpNotPresent;
                }
            }
        }

CleanupAndExit:
    if (Buffer)
        {
        MIDL_user_free(Buffer);
        }
}