MFC template collection(CArray, CList 등)을 직렬화 할때, Serialize메서드를 호출해서 사용하면 _CrtisVaildHeapPointer(block) 메세지와 함께 exception이 발생하는 것을 경험할 수 있다.

일단 에러 메세지를 이해해보면 일반적으로 동적할당을 해제 할 때 발생하는데, 잘못된 위치를 가리키는 포인터를 해제하려고 할 때 발생한다.

 

이는 Serialize메서드에서 collection의 각 원소를 복사할 때, 포인터 단위로 단순 대입을 하기 때문이다.

예시로 CList의 Serialize 구현부를 따라가보면 원소를 복사할 때 SerializeElements를 사용하는 것을 알 수 있다. 해당 함수의 구현부는 아래와 같다.

template<class TYPE>
void AFXAPI SerializeElements(CArchive& ar, TYPE* pElements, INT_PTR nCount)
{
	ENSURE(nCount == 0 || pElements != NULL);
	ASSERT(nCount == 0 ||
		AfxIsValidAddress(pElements, (size_t)nCount * sizeof(TYPE)));

	// default is bit-wise read/write
	if (ar.IsStoring())
	{
		TYPE* pData;
		UINT_PTR nElementsLeft;

		nElementsLeft = nCount;
		pData = pElements;
		while( nElementsLeft > 0 )
		{
			UINT nElementsToWrite;

			nElementsToWrite = UINT(__min(nElementsLeft, INT_MAX/sizeof(TYPE)));
			ar.Write(pData, nElementsToWrite*sizeof(TYPE));
			nElementsLeft -= nElementsToWrite;
			pData += nElementsToWrite;
		}
	}
	else
	{
		TYPE* pData;
		UINT_PTR nElementsLeft;

		nElementsLeft = nCount;
		pData = pElements;
		while( nElementsLeft > 0 )
		{
			UINT nElementsToRead;

			nElementsToRead = UINT(__min(nElementsLeft, INT_MAX/sizeof(TYPE)));
			ar.EnsureRead(pData, nElementsToRead*sizeof(TYPE));
			nElementsLeft -= nElementsToRead;
			pData += nElementsToRead;
		}
	}
}

 

pElements로 받아온 원소의 포인터를 그대로 pData에 복사한 뒤, 여기에 Write 또는 EnsureRead로 bit-wise read/write를 하고 있다. 만약 원소가 기본 데이터 타입(int, TCHAR 등)이면 큰 문제 없이 동작하겠지만 class라면, 특히 멤버로 또다른 class나 collection을 가지고 있다면 이와 같은 문제가 생길 수 있다.

원소의 멤버로 있는 값이 포인터를 가지고 있다면, 이 포인터 값도 함께 직렬화된다. 불러오기를 통해 가져온 포인터 값은, 현재 runtime 중인 프로세스가 가지고 있는 heap이 아니라, 이전에 저장했던 프로세스가 가지고 있는 heap을 가리키고 있는 포인터일 것이다. 따라서 잘못된 위치를 가리키는 포인터를 사용하여 _CrtisValidHeapPointer exception이 발생한다.

 

이를 해결하기 위해서는 다음과 같이 SerializeElements를 오버로딩하면 된다.

template<>
void AFXAPI SerializeElements(CArchive& ar, MyClass* pElements, INT_PTR nCount)
{
	// Do what you want.
}

또는 그냥 Serialize메서드를 호출하지 말고, 반복문 등을 사용해서 모든 원소를 직접 ar 객체에 << 또는 >>연산자로 써 주면 된다.

 

 

참고 자료


https://docs.microsoft.com/ko-kr/cpp/mfc/reference/collection-class-helpers?view=msvc-160#serializeelements
https://docs.microsoft.com/ko-kr/cpp/mfc/how-to-make-a-type-safe-collection?view=msvc-160#_core_implementing_helper_functions

 

+ Recent posts