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