Skip to content

Instantly share code, notes, and snippets.

@EricWF
Created May 7, 2017 03:17
Show Gist options
  • Save EricWF/9645beee9da1e7795be8b9d2c13e0618 to your computer and use it in GitHub Desktop.
Save EricWF/9645beee9da1e7795be8b9d2c13e0618 to your computer and use it in GitHub Desktop.
/***
*excptptr.cpp - The exception_ptr implementation and everything associated with it.
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* The exception_ptr implementation and everything associated with it.
****/
#ifndef _VCRT_ALLOW_INTERNALS
#define _VCRT_ALLOW_INTERNALS
#endif
#include <internal_shared.h>
#include <eh.h>
#include <ehdata.h>
#include <exception>
#include <malloc.h>
#include <memory>
#include <new>
#include <stddef.h>
#include <stdexcept>
#include <trnsctrl.h>
#include <unknwn.h>
#include <windows.h>
// Pre-V4 managed exception code
#define MANAGED_EXCEPTION_CODE 0XE0434F4D
// V4 and later managed exception code
#define MANAGED_EXCEPTION_CODE_V4 0XE0434352
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
#undef CT_COPYFUNC
#define CT_COPYFUNC(ct) ((ct).copyFunction? CT_COPYFUNC_IB(ct, _GetThrowImageBase()):NULL)
#undef THROW_COUNT
#define THROW_COUNT(ti) THROW_COUNT_IB(ti, _GetThrowImageBase())
#endif // _EH_RELATIVE_OFFSETS
////////////////////////////////////////////////////////////////////////////////
//
// Forward declaration of functions:
//
static void * _StaticAlloc(size_t bytesToAllocate);
extern "C" _CRTIMP2 void * __cdecl __AdjustPointer( void *, const PMD&); // defined in frame.cpp
using namespace std;
class __ExceptionPtr
{
public:
__ExceptionPtr(_In_ const EHExceptionRecord *, bool normal);
static shared_ptr<__ExceptionPtr> _CurrentException();
static shared_ptr<__ExceptionPtr> _CopyException(_In_ const void*, _In_ const ThrowInfo*, bool normal);
static const shared_ptr<__ExceptionPtr>& _BadAllocException();
~__ExceptionPtr();
[[noreturn]] void _RethrowException() const;
private:
static shared_ptr<__ExceptionPtr> m_badAllocExceptionPtr;
static shared_ptr<__ExceptionPtr> _InitBadAllocException();
__ExceptionPtr( _In_ const __ExceptionPtr&); // not implemented
__ExceptionPtr& operator=( _In_ const __ExceptionPtr&); // not implemented
void _CallCopyCtor(_Out_writes_bytes_(size) void* dst, _In_reads_bytes_(size) void* src, size_t size, _In_ const CatchableType * const pType) const;
#if _EH_RELATIVE_OFFSETS
ptrdiff_t _GetThrowImageBase() const
{ return (ptrdiff_t) m_EHRecord.params.pThrowImageBase; }
#endif
union
{
_EXCEPTION_RECORD m_Record;
EHExceptionRecord m_EHRecord;
};
bool m_normal;
};
static_assert(sizeof(std::exception_ptr) == sizeof(std::shared_ptr<__ExceptionPtr>),
"std::exception_ptr and std::shared_ptr<__ExceptionPtr> must have the same size.");
shared_ptr<__ExceptionPtr> __ExceptionPtr::m_badAllocExceptionPtr = __ExceptionPtr::_InitBadAllocException();
shared_ptr<__ExceptionPtr> __ExceptionPtr::_InitBadAllocException()
{
std::bad_alloc _Except;
return __ExceptionPtr::_CopyException(&_Except, static_cast<const ThrowInfo*>(__GetExceptionInfo(_Except)), false);
}
const shared_ptr<__ExceptionPtr>& __ExceptionPtr::_BadAllocException()
{
return __ExceptionPtr::m_badAllocExceptionPtr;
}
////////////////////////////////////////////////////////////////////////////////
//
// void __ExceptionPtr::_CallCopyCtor();
//
// Helper function for calling the copy-ctor on the C++ exception object. It reads
// the passed in throw info and determines the type of exception. If it's a simple type
// it calls memcpy. If it's a UDT with copy-ctor, it uses the copy-ctor.
//
void __ExceptionPtr::_CallCopyCtor(_Out_writes_bytes_(size) void* dst, _In_reads_bytes_(size) void* src, size_t size, _In_ const CatchableType * const pType) const
{
if (CT_ISSIMPLETYPE(*pType) || // this is a simple type
CT_COPYFUNC(*pType) == NULL // this is a user defined type without copy ctor
)
{
// just copy with memcpy
memcpy(dst, src, size);
if (CT_ISWINRTHANDLE(*pType))
{
IUnknown* pUnknown = *(reinterpret_cast<IUnknown**>(src));
if (pUnknown)
{
pUnknown->AddRef();
}
}
}
else
{
try
{
// it's a user defined type with a copy ctor
if (CT_HASVB(*pType))
{
// this exception got a virtual base
#if defined(_M_CEE_PURE)
void (__clrcall *pFunc)(void *, void *, int) = (void (__clrcall *)(void *, void *, int))(void *)CT_COPYFUNC(*pType);
pFunc((void *)dst, __AdjustPointer(src, CT_THISDISP(*pType)), 1);
#else
_CallMemberFunction2((char *)dst,
CT_COPYFUNC(*pType),
__AdjustPointer(src,
CT_THISDISP(*pType)), 1);
#endif // defined(_M_CEE_PURE)
}
else
{
#if defined(_M_CEE_PURE)
void (__clrcall *pFunc)(void *, void *) = (void (__clrcall *)(void *, void *))(void *)CT_COPYFUNC(*pType);
pFunc((void *)dst, __AdjustPointer(src, CT_THISDISP(*pType)));
#else
_CallMemberFunction1((char *)dst,
CT_COPYFUNC(*pType),
__AdjustPointer(src,
CT_THISDISP(*pType)));
#endif // defined(_M_CEE_PURE)
}
}
catch (...)
{
// the copy-Ctor() threw. We need to terminate().
terminate();
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// void __ExceptionPtr::_CurrentException();
//
// Builds an __ExceptionPtr with the current exception inflight. This function
// gets the information from the per-thread-data (PTD).
//
// This function returns the static copy of exception_ptr which points to a
// std::bad_alloc exception in out-of-memory situation.
//
shared_ptr<__ExceptionPtr> __ExceptionPtr::_CurrentException()
{
// there is an exception in flight... copy it
try
{
if (_pCurrentException == NULL || // no exception in flight
__ProcessingThrow != 0 || // we are unwinding... possibly called from a dtor()
((_pCurrentException->ExceptionCode == MANAGED_EXCEPTION_CODE) ||
(_pCurrentException->ExceptionCode == MANAGED_EXCEPTION_CODE_V4)) // we don't support managed exceptions
)
{
// return a NULL exception_ptr
return shared_ptr<__ExceptionPtr>();
}
return make_shared<__ExceptionPtr>(_pCurrentException, true);
}
catch (const std::bad_alloc&)
{
return _BadAllocException();
}
catch (...)
{
// something went wrong, and it wasn't a bad_alloc and
// we are not allowed to throw out of this function...
terminate();
}
}
template <typename T> struct _StaticAllocator {
typedef T value_type;
_StaticAllocator() = default;
template <typename U> _StaticAllocator(const _StaticAllocator<U>&) { }
template <typename U> bool operator==(const _StaticAllocator<U>&) const {
return true;
}
template <typename U> bool operator!=(const _StaticAllocator<U>&) const {
return false;
}
T * allocate(const size_t n) const {
if (n == 0) {
return nullptr;
}
if (n > static_cast<size_t>(-1) / sizeof(T)) {
terminate();
}
return static_cast<T *>(_StaticAlloc(n * sizeof(T)));
}
void deallocate(T *, size_t) const {
// purposely does nothing
}
};
////////////////////////////////////////////////////////////////////////////////
//
// void __ExceptionPtr::_CopyException();
//
// Builds an __ExceptionPtr with the given C++ exception object and its corresponding
// throwinfo.
//
// This function returns the static copy of exception_ptr which points to a
// std::bad_alloc exception in out-of-memory situation.
//
shared_ptr<__ExceptionPtr> __ExceptionPtr::_CopyException(_In_ const void * pExceptObj, _In_ const ThrowInfo* pThrowInfo, bool normal)
{
try
{
// the initial values of EHExceptionRecord need to be consistent with _CxxThrowException()
EHExceptionRecord ehRecord;
EHExceptionRecord* pExcept = &ehRecord; // just an alias for use of macros
// initialize the record
PER_CODE(pExcept) = EH_EXCEPTION_NUMBER;
PER_FLAGS(pExcept) = EXCEPTION_NONCONTINUABLE;
PER_NEXT(pExcept) = NULL; // no SEH to chain
PER_ADDRESS(pExcept) = NULL; // Address of exception. Will be overwritten by OS
PER_NPARAMS(pExcept) = EH_EXCEPTION_PARAMETERS;
PER_MAGICNUM(pExcept) = EH_MAGIC_NUMBER1;
PER_PEXCEPTOBJ(pExcept) = const_cast<void*>(pExceptObj);
ThrowInfo* pTI = pThrowInfo;
if (pTI && (THROW_ISWINRT( (*pTI) ) ) )
{
ULONG_PTR *exceptionInfoPointer = *reinterpret_cast<ULONG_PTR**>(const_cast<void*>(pExceptObj));
exceptionInfoPointer--; // The pointer to the ExceptionInfo structure is stored sizeof(void*) in front of each WinRT Exception Info.
WINRTEXCEPTIONINFO* wei = reinterpret_cast<WINRTEXCEPTIONINFO*>(*exceptionInfoPointer);
pTI = wei->throwInfo;
}
PER_PTHROW(pExcept) = pTI;
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
PER_PTHROWIB(pExcept) = ThrowImageBase;
#endif
//
// If the throw info indicates this throw is from a pure region,
// set the magic number to the Pure one, so only a pure-region
// catch will see it.
//
// Also use the Pure magic number on Win64 if we were unable to
// determine an image base, since that was the old way to determine
// a pure throw, before the TI_IsPure bit was added to the FuncInfo
// attributes field.
//
if (pTI != NULL)
{
if (THROW_ISPURE(*pTI))
{
PER_MAGICNUM(pExcept) = EH_PURE_MAGIC_NUMBER1;
}
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
else if (ThrowImageBase == NULL)
{
PER_MAGICNUM(pExcept) = EH_PURE_MAGIC_NUMBER1;
}
#endif
}
if (normal)
{
return make_shared<__ExceptionPtr>(pExcept, true);
}
else
{
return allocate_shared<__ExceptionPtr>(_StaticAllocator<int>(), pExcept, false);
}
}
catch (const std::bad_alloc&)
{
return _BadAllocException();
}
catch (...)
{
// something went wrong, and it wasn't a bad_alloc and
// we are not allowed to throw out of this function...
terminate();
}
}
////////////////////////////////////////////////////////////////////////////////
//
// void __ExceptionPtr::__ExceptionPtr();
//
// Ctor for __ExceptionPtr. This function copies the EXCEPTION_RECORD that is
// passed in. If the EXCEPTION_RECORD is a C++ exception it will also, allocate
// heap memory for the C++ exception and call the copy-ctor for it.
//
// This function throws std::bad_alloc exception in out-of-memory situation.
//
__ExceptionPtr::__ExceptionPtr(_In_ const EHExceptionRecord * pExcept, bool normal)
: m_normal(normal)
{
//copying the _EXCEPTION_RECORD (for SEH exceptions)
PER_CODE( &this->m_Record) = PER_CODE(pExcept);
PER_FLAGS( &this->m_Record) = PER_FLAGS(pExcept);
PER_NEXT( &this->m_Record) = 0; // We don't chain SEH exceptions
PER_ADDRESS(&this->m_Record) = 0; // Useless field to copy. It will be overwritten by RaiseException()
PER_NPARAMS(&this->m_Record) = PER_NPARAMS(pExcept);
// copying any addition parameters
for (ULONG i = 0; i < this->m_Record.NumberParameters && i < EXCEPTION_MAXIMUM_PARAMETERS; ++i)
{
this->m_Record.ExceptionInformation[i] = ((EXCEPTION_RECORD*)pExcept)->ExceptionInformation[i];
}
// NULLing out any un-used parameters
for (ULONG i = this->m_Record.NumberParameters; i < EXCEPTION_MAXIMUM_PARAMETERS; ++i)
{
this->m_Record.ExceptionInformation[i] = 0;
}
if (PER_IS_MSVC_PURE_OR_NATIVE_EH(pExcept))
{
// this is a C++ exception
// we need to copy the C++ exception object
// nulling out the Exception object pointer, because we haven't copied it yet
PER_PEXCEPTOBJ(&this->m_EHRecord) = NULL;
ThrowInfo* pThrow= PER_PTHROW(pExcept);
if ( PER_PEXCEPTOBJ(pExcept) == NULL ||
pThrow == NULL ||
pThrow->pCatchableTypeArray == NULL ||
THROW_COUNT(*pThrow) <=0
)
{
// No ThrowInfo exists. If this were a C++ exception, something must have corrupted it.
terminate();
}
// we want to encode the ThrowInfo pointer for security reasons
PER_PTHROW(&this->m_EHRecord) = (ThrowInfo*) EncodePointer((void*)pThrow);
// we finally got the type info we want
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
CatchableType *const pType = (CatchableType * const)( *(int*)THROW_CTLIST_IB(*pThrow, _GetThrowImageBase()) + _GetThrowImageBase());
#else
CatchableType *const pType = *THROW_CTLIST(*pThrow);
#endif
void* pExceptionBuffer = NULL;
if (normal)
{
pExceptionBuffer = malloc(CT_SIZE(*pType));
}
else
{
pExceptionBuffer = _StaticAlloc(CT_SIZE(*pType));
}
if (pExceptionBuffer == NULL)
{
throw std::bad_alloc();
}
__ExceptionPtr::_CallCopyCtor(pExceptionBuffer, PER_PEXCEPTOBJ(pExcept), CT_SIZE(*pType), pType);
PER_PEXCEPTOBJ(&this->m_EHRecord) = pExceptionBuffer;
}
//else // this is a SEH exception, no special handling is required
}
////////////////////////////////////////////////////////////////////////////////
//
// void __ExceptionPtr::~__ExceptionPtr();
//
// Dtor for __ExceptionPtr. If the stored exception is a C++ exception, it destroys
// the C++ exception object and de-allocate the heap memory for it.
//
__ExceptionPtr::~__ExceptionPtr()
{
if (!m_normal)
{
// If m_normal is false, *this is m_badAllocExceptionPtr. Since everything for that special
// case is allocated into m_badAllocExceptionPtr itself or with _StaticAlloc, nothing to do.
return;
}
EHExceptionRecord* pExcept = &this->m_EHRecord;
if (PER_IS_MSVC_PURE_OR_NATIVE_EH(pExcept))
{
// this is a C++ exception
// we need to delete the actual exception object
ThrowInfo* pThrow= (ThrowInfo*) DecodePointer((void*)PER_PTHROW(pExcept));
if ( pThrow == NULL )
{
// No ThrowInfo exists. If this were a C++ exception, something must have corrupted it.
terminate();
}
if (PER_PEXCEPTOBJ(pExcept) != NULL) // we have an object to destroy
{
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
CatchableType *const pType = (CatchableType * const)( *(int*)THROW_CTLIST_IB(*pThrow, _GetThrowImageBase()) + _GetThrowImageBase());
#else
CatchableType *const pType = *THROW_CTLIST(*pThrow);
#endif
// calling the d-tor if there's one
if (THROW_UNWINDFUNC(*pThrow) != NULL) // we have a dtor func
{
// it's a user defined type with a dtor
#if defined(_M_CEE_PURE)
void (__clrcall * pDtor)(void*) = (void (__clrcall *)(void *))(THROW_UNWINDFUNC(*pThrow));
(*pDtor)(PER_PEXCEPTOBJ(pExcept));
#elif _EH_RELATIVE_OFFSETS
_CallMemberFunction0(PER_PEXCEPTOBJ(pExcept),
THROW_UNWINDFUNC_IB(*pThrow,(unsigned __int64)PER_PTHROWIB(pExcept)));
#else
_CallMemberFunction0(PER_PEXCEPTOBJ(pExcept),
THROW_UNWINDFUNC(*pThrow));
#endif
}
else if (CT_ISWINRTHANDLE(*pType))
{
IUnknown* pUnknown = *(reinterpret_cast<IUnknown**>(PER_PEXCEPTOBJ(pExcept)));
if (pUnknown)
{
pUnknown->Release();
}
}
}
// de-allocating the memory of the exception object
free(PER_PEXCEPTOBJ(pExcept));
}
}
////////////////////////////////////////////////////////////////////////////////
//
// [[noreturn]] void __ExceptionPtr::_RethrowException() const;
//
// This function rethrows the exception that is stored in the exception_ptr object.
// If the stored exception is a SEH exception, it simply calls RaiseException().
// If the stored exception is a C++ exception, it will try to allocate memory on
// the stack and copy the exception object onto the stack and calls RaiseException().
//
[[noreturn]] void __ExceptionPtr::_RethrowException() const
{
// throwing a bad_exception if they give us a NULL exception_ptr
if (this == NULL)
{
throw std::bad_exception();
}
EXCEPTION_RECORD ThisException = this->m_Record;
EHExceptionRecord* pExcept = (EHExceptionRecord*)&ThisException; // just an alias for use of the macros
if (PER_IS_MSVC_PURE_OR_NATIVE_EH(pExcept))
{
// this is a C++ exception
// we need to build the exception on the stack
// because the current exception mechanism assumes
// the exception object is on the stack and will call
// the appropriate dtor (if there's one), but won't
// delete the memory.
ThrowInfo* pThrow= (ThrowInfo*) DecodePointer((void*)PER_PTHROW(pExcept));
if ( PER_PEXCEPTOBJ(pExcept) == NULL ||
pThrow == NULL ||
pThrow->pCatchableTypeArray == NULL ||
THROW_COUNT(*pThrow) <=0
)
{
// No ThrowInfo exists. If this were a C++ exception, something must have corrupted it.
terminate();
}
PER_PTHROW(pExcept) = pThrow; // update the EXCEPTION_RECORD with the proper decoded pointer before rethrowing
// we finally got the type info we want
#if _EH_RELATIVE_OFFSETS && !defined(_M_CEE_PURE)
CatchableType *const pType = (CatchableType * const)( *(int*)THROW_CTLIST_IB(*pThrow, _GetThrowImageBase()) + _GetThrowImageBase());
#else
CatchableType *const pType = *THROW_CTLIST(*pThrow);
#endif
// alloc memory on stack for exception object
// this might cause StackOverFlow SEH exception,
// in that case, we just let the SEH propagate
void * pExceptionBuffer = alloca(CT_SIZE(*pType));
__ExceptionPtr::_CallCopyCtor(pExceptionBuffer, PER_PEXCEPTOBJ(pExcept), CT_SIZE(*pType), pType);
PER_PEXCEPTOBJ(pExcept) = pExceptionBuffer;
}
//else // this is a SEH exception, no special handling is required
// This shouldn't happen. Just to make prefast/OACR happy
if (ThisException.NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS)
{
ThisException.NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
}
RaiseException( ThisException.ExceptionCode,
ThisException.ExceptionFlags,
ThisException.NumberParameters,
ThisException.ExceptionInformation );
}
////////////////////////////////////////////////////////////////////////////////
//
// _StaticAlloc allocates memory from a buffer local to this TU
//
// No synchronization is necessary because it is called only during the process/
// AppDomain startup code (in __ExceptionPtr::_InitBadAllocException)
//
static char _StaticAllocStorage[
// in allocation order:
MEMORY_ALLOCATION_ALIGNMENT // padding for alignment
+ sizeof(_Ref_count_obj_alloc<__ExceptionPtr, _StaticAllocator<int>>)
+ MEMORY_ALLOCATION_ALIGNMENT
+ sizeof(bad_alloc)];
static size_t _StaticAllocStorageLeft = sizeof(_StaticAllocStorage);
static void * _StaticAlloc(const size_t bytesToAllocate)
{
void * allocBase = _StaticAllocStorage
+ sizeof(_StaticAllocStorage)
- _StaticAllocStorageLeft;
void * const pv = std::align(MEMORY_ALLOCATION_ALIGNMENT, bytesToAllocate, allocBase, _StaticAllocStorageLeft);
if (pv)
{
_StaticAllocStorageLeft -= bytesToAllocate;
return pv;
}
std::terminate();
}
////////////////////////////////////////////////////////////////////////////////
//
// Tiny wrappers for bridging the gap between std::exception_ptr and shared_ptr<__ExceptionPtr>.
// We are doing this because <memory> depends on <exception> which means <exception> cannot include <memory>.
// And shared_ptr<> is defined <memory>. To workaround this, we created a dummy class
// std::exception_ptr, which is structurally identical to shared_ptr<>.
//
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCreate(void* ptr)
{
new (ptr) shared_ptr<__ExceptionPtr>();
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrDestroy(void* ptr)
{
static_cast<shared_ptr<__ExceptionPtr>*>(ptr)->~shared_ptr<__ExceptionPtr>();
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCopy(void* _dst, const void* _src)
{
shared_ptr<__ExceptionPtr>* dst = static_cast<shared_ptr<__ExceptionPtr>*>(_dst);
const shared_ptr<__ExceptionPtr>* src = static_cast<const shared_ptr<__ExceptionPtr>*>(_src);
new (dst) shared_ptr<__ExceptionPtr>(*src);
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrAssign(void* _dst, const void* _src)
{
shared_ptr<__ExceptionPtr>* dst = static_cast<shared_ptr<__ExceptionPtr>*>(_dst);
const shared_ptr<__ExceptionPtr>* src = static_cast<const shared_ptr<__ExceptionPtr>*>(_src);
dst->operator=(*src);
}
_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL __ExceptionPtrCompare(const void* _lhs, const void* _rhs)
{
const shared_ptr<__ExceptionPtr>* lhs = static_cast<const shared_ptr<__ExceptionPtr>*>(_lhs);
const shared_ptr<__ExceptionPtr>* rhs = static_cast<const shared_ptr<__ExceptionPtr>*>(_rhs);
return operator==(*lhs, *rhs);
}
_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL __ExceptionPtrToBool(const void* _ptr)
{
const shared_ptr<__ExceptionPtr>* ptr = static_cast<const shared_ptr<__ExceptionPtr>*>(_ptr);
return !!*ptr;
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrSwap(void* _lhs, void* _rhs)
{
shared_ptr<__ExceptionPtr>* lhs = static_cast<shared_ptr<__ExceptionPtr>*>(_lhs);
shared_ptr<__ExceptionPtr>* rhs = static_cast<shared_ptr<__ExceptionPtr>*>(_rhs);
lhs->swap(*rhs);
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCurrentException(void* ptr)
{
const shared_ptr<__ExceptionPtr> pExcept = __ExceptionPtr::_CurrentException();
__ExceptionPtrCopy(ptr, static_cast<const void*>(&pExcept));
}
[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrRethrow(const void* _ptr)
{
const shared_ptr<__ExceptionPtr>* ptr = static_cast<const shared_ptr<__ExceptionPtr>*>(_ptr);
(*ptr)->_RethrowException();
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCopyException(_Inout_ void* _ptr, _In_ const void* _pExcept, _In_ const void* _pThrowInfo)
{
shared_ptr<__ExceptionPtr>* ptr = static_cast<shared_ptr<__ExceptionPtr>*>(_ptr);
(*ptr) = __ExceptionPtr::_CopyException(_pExcept, static_cast<const ThrowInfo*>(_pThrowInfo), true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment