windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/eeinfo.cxx
2020-09-26 16:20:57 +08:00

3559 lines
82 KiB
C++

/*++
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);
}
}