CLOSE SEARCH
POSTS TAGGED WITH: MFC

_CrtIsValidHeapPointer(pUserData) Error

_CrtIsValidHeapPointer(pUserData) Error 해결방안.

http://www.tomatowax.com/ZeroboardXE/6689


추가로..

만약 MFC + cxImage 개발환경에서 위와 같은 오류가 발생하는 경우에는

이미지 데이터의 유효성을 반드시 검사해 봐야한다.

특히 메모리에 존재하는 버퍼에서 이미지를 생성하는 경우에 실제 원본과 메모리 상의 버퍼의 바이트수가 다를 경우 위의 오류가 발생한다.


GDI+ 초기화

gdiplus.lib 추가

#pragma comment(lib, “gdiplus.lib”)


gdiplus.h

Gdiplus 네임스페이스 사용


App  클래스에서

ULONG_PTR m_GDIPlusToken 멤버 추가


InitInstance()에서

GdiplusStartupInput gsi;

GdiplusStartup(&m_GDIPlusToken, &gsi, NULL);


ExitInstance()에서

GdiplusShutdown(m_GDIPlusToken);


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

MFC 클래스간의 상호 참조

CWinApp 파생클래스의 인스턴스 참조

CWinApp* AfxGetApp()


메인 프레임 윈도우 참조

CWnd* AfxGetMainWnd()


CFrameWnd에서 CDocument 참조

CDocument* GetActiveDocument()


CDocument, CView 상호 참조

POSITION GetFirstViewPosition(), CView* GetNextView()

CDocument* GetDocument()


CFrameWnd, CView 상호 참조

CView* GetActiveView()

CFrameWnd* GetParentFrame()


도큐먼트 템플릿 참조

POSITION GetFirstDocTemplatePosition()

CDocTemplate* GetNextDocTemplate(POSITION& pos)


CMultiDocTemplate에서 CDocument 참조

POSITION GetFirstDocPosition()

CDocument* GetNextDoc(POSITION& pos)


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

DEBUG_NEW와 메모리 진단

MFC는 디버그 모드시에 모든 new 연산자를 DEBUG_NEW 연산자로 변경시킨다.

DEBUG_NEW는 다음과 같이 정의 되어있으며, 파일이름과 라인번호를 저장하고 CMemoryState를 이용해서 동적메모리의 할당과 해제를 추적한다.


#define DEBUG_NEW new(THIS_FILE, __LINE__)

CMemoryState는 메모리 유출을 감지기능을 제공하는 구조체이다.(디버그 모드 시에만 적용된다)


struct CMemoryState
{

enum blockUsage {freeBlock, objectBlock, bitBlock, crtBlock, ignoredBlock, nBlockUseMax};

_CrtMemState m_memState;
LONG_PTR m_lCounts[nBlockUseMax];
LONG_PTR m_lSizes[nBlockUseMax];
LONG_PTR m_lHighWaterCount;
LONG_PTR m_lTotalCount;

CMemoryState();


//메모리 스냅샷 정보를 현재 객체로 저장

void Checkpoint();

// 두 메모리의 스냅샷 정보를 비교, 다를 경우 0이아닌 값을 리턴

BOOL Difference(const CMemoryState& oldState, const CMemoryState& newState);
void UpdateData();


// 메로리 할당 정보 표시

void DumpStatistics() const;

// 마지막 Checkpoint() 함수 호출이 후 할당된 모든 메모리 정보를 표시
void DumpAllObjectsSince() const;


};


CMemoryState를 이용한 메모리 릭 검출기능은 new를 사용하여 생성된 모든 동적 메모리에 적용될 수 있다. 메모리 릭 검출 시에 필요한 파일과 라인번호는 DEBUG_NEW에 의해 새로 정의된 new 함수가 제공한다.

Checkpoint()함수는 추적지점을 설정(현재 메모리 스냅샷을 객체에 저장)하고 Difference()함수는 두 지점간의 메모리 상태를 비교하며, DumpStatistics() 함수는 메모리 상태를 출력한다. Checkpoint() 함수를 호출한 이후의 모든 메모리 유출 상태를 살펴보려면 DumpAllObjectsSince() 함수를 호출하면 된다.


MFC 프로그램에 아래와 같은 소스를 추가한 후 디버깅을 시작하면
CMemoryState msOld;
msOld.Checkpoint();
CPoint* pt = new CPoint();
CRect* rt = new CRect();
msOld.DumpAllObjectsSince();


출력창에 아래와 같은 메모리 관련 정보(메모리 스냅샷)가 표시된다.














또 다른 방법은 아래 소스와 같다.


CMemoryState msOld, msNew, msDiffer;
msOld.Checkpoint();
CPoint* pt = new CPoint();
CRect* rt = new CRect();
CString s(TEXT(“Memory leak dectection”));
msNew.Checkpoint();

if (msDiffer.Difference(msOld, msNew))
{

TRACE(“\n\n메모리가 새고 있어!\n\n”);
msDiffer.DumpStatistics();
TRACE(“\n\n”);

}


결과는 다음과 같다.



msOld.Checkpoint() 호출시의 메모리 상태와 msNew.Checkpoint() 호출시의 메모리 상태가 msDiffer.Differenct() 함수에서 비교되고 할당 후 해제되지 않은 메모리가 발견되어 0이 아닌 값을 리턴한다. 그러므로 위의 예제에서와 같이 TRACE() 매크로에서 설정한 매크로와 msDiffer.DumpStatistics() 함수가 호출된다. 만약 위의 예제에서 msNew.Checkpoint()함수 호출전에 다음과 같은 코드로 생성한 객체를 정리해주면


delete pt;

delete rt;

s.Empty();


msDiffer.Difference() 함수 호출에서 메모리 릭이 검출되지 않아 if문안의 코드들은 실행되지 않는다.



참고로 메모리 진단 기능 활성화 상태는 AfxEnableMemoryTracking()라는 전역함수를 통해 변경할 수 있다. 기본적으로 활성화 되어 있지만 이 함수에 FALSE를 인자로 주어 호출할 경우 메모리 진단 기능이 비활성화 된다.


MFC에는 afxMemDF라는 int형 전역변수가 하나 존재하는데 이 변수에 플래그 값을 지정하여 특정 메모리 진단 기능을 설정/해제 할 수 있다. 기본값은 afxMemDF = allocMemDF이다. alloMemDF 플래그는 메모리 진단 기능을 사용하겠다는 의미이다. 이 외에 delayFreeMemDF 플래그는 delete 연산자로 해제된 메모리 블럭의 실제 해제를 프로그램 종료시까지 지연시켜 주는데 프로그램 종료전에 메모리의 상태를 출력해 보면 프로그램이 할당하는 최대 메모리 량을 확인할 수 있다. checkAlwaysMemDF 플래그는 메모리의 할당과 해제 시마다 AfxCheckMemory() 함수를 호출하여 메모리 유출이 있을경우 출력창에 정보를 출력한다. 위의 세개의 플래그는 |(OR) 연산자를 이용하여 플래그를 결합할 수 있다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

ASSERT_VALID(), AssertValid()

ASSERT_VALID() 매크로는 객체의 내부 유효성을 확인하기 위해 그 객체에 존재하는 AssertValid() 함수를 호출하는 기능을 가지고 있다. 디버그 시에만 동작하며 유효성 검사가 실패할 경우 ASSERT()와 동일한 경고박스를 출력해준다.


ASSERT_VALID() 매크로는 다음과 같이 선언되어 있다.

#define ASSERT_VALID(pOb)  DEBUG_ONLY((::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)))


실제 사용시엔 pOb부분의 유효성을 검사할 클래스의 이름을 넣어주면 된다.


AssertValid() 함수는 CObject 클래스에 아래와 같이 선언되어 있다.

virtual void AssertValid() const;


이 함수는 실질적으로 객체의 내부의 유효성을 검사하는 역활을 한다. 위의 ASSERT_VALID() 매크로를 사용하면 이 함수가 호출되는 것이다. const 함수이므로 유효성 검사 중에는 객체의 상태가 변경될 수 없다. 그리고 이 함수는 “Shallow check”를 수행한다. 예를 들어 객체가 다른 객체의 포인터를 멤버로 가지는 경우 그 멤버의 NULL 여부만 확인할 뿐 그 포인터가 지시하는 객체의 유효성은 확인하지 않는다.

사용자가 작성하는 새로운 클래스에서 이 기능을 사용하고자 할 경우 CObject 클래스로부터 상속한 후 AssertValid() 함수를 재정의 해 주면 된다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

ASSERT()

ASSERT(booleanExpression)는 디버그 시에 booleanExpression식을 평가해서 그 결과가 FALSE일 경우 ASSERT 경고박스를 보여준다. 릴리즈 시에는 해당 코드가 주석처리 되므로 실행되지 않으며, 릴르즈 모드에서도 booleanExpression식을 평가하고자 할 경우에는 VERIFY()매크로를 사용하면 된다.


ASSERT() 매크로는 아래와 같은 형식으로 사용된다.

HANDLE hFile = CreateFile(…..);

ASSERT(hFile != INVALID_HANDLE_VALUE);


hFile 핸들변수에 대입된 CreateFile의 결과가 INVALID_HANDLE_VALUE일 경우 Debug Assertion Failed! 경고박스를 출력해준다.


ASSERT()는 실제로 다음과 같이 선언되어 있다.

#define ASSERT(f)

DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))


AfxAssertFailedLine()함수는 ASSERT() 매크로의 평가식이 FALSE일 경우 현재 활성화된 스레드를 정지시키고, ASSERT 경고박스에 파일의 이름과 줄 번호를 보여주는 역활을 한다.


AfxDebugBreak()함수는 제어권을 VC++ 디버거에 넘기는 역활을 한다.


MFC 프로그램에 다음과 같은 ASSERT() 매크로가 사용될 경우(조금 억지스러운 예제이지만)


void CAppWizardView::OnLButtonDown(UINT nFlags, CPoint point)
{
ASSERT(point.x < 500);
}


사용자가 클릭한 x좌표가 500 미만일 경우에는 아무 문제없이 디버그가 진행되지만, 500이상의 좌표일 경우에는 아래와 같은 경고박스가 경고음과 함께 출력된다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

TRACE()

TRACE() 매크로는 매개변수로 지정한 문자열을 VS의 디버그 윈도우에 출력해준다.


프로그램의 OnLButtonDown()에 다음과 같이 TRACE() 매크로를 사용하면

void CAppWizardView::OnLButtonDown(UINT nFlags, CPoint point)
{
TRACE(TEXT(“Mouse position : %d, %d\n”), point.x, point.y);
}

디버그 시에 출력창에 아래와 같이 지정한 문자열이 출력된다.













TRACE() 메크로는 디버그 모드에서만 동작하며, 릴리즈 모드에서는 매크로가 확장되지 않아 소스 파일에 TRACE()와 관련된 그 어떤 코드도 포함되지 않는다.


위의 예제와 같은 TRACE(exp)의 형식으로 사용할 수도 있고, 아래와 같이  MFC Trace flag와 Tracing level을 지정해서 사용할 수도 있다.
TRACE(DWORD category, UINT level, LPCSTR lpszFormat, … )


TRACE() 매크로와 동일한 기능을 하는 다음과 같은 매크로도 있다. TRACE()와 비교해 사용하는 매개변수에 차이가 있다.


TRACE0(exp) : 하나의 문자열만을 사용한다.

TRACE1(exp, param1) : 하나의 형식 문자열(formatted string)과 파라미터를 사용한다.

TRACE2(exp, param1, param2) : 하나의 형식 문자열과 두개의 파라미터를 사용한다.

TRACE3(exp, param1, param2, param3) : 하나의 형식 문자열과 세개의 파라미터를 사용한다.


비슷한 기능을 하는 ATLTRACE2() 매크로도 있으며 TRACE()와 사용법은 동일하다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE

DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE 매크로는 CObject로 부터 상속받은 클래스에 동적생성 기능을 적용시켜준다. 이 매크로를 추가하면 CRuntimeClass의 CreateObject() 함수를 통해 클래스를 동적으로 생성할 수 있게 된다.


DECLARE_DYNCREATE()는 다음과 같이 DECLARE_DYNAMIC() 매크로와 CreateObject()라는 정적 함수 선언 하나로 구성되어 있다.


#define DECLARE_DYNCREATE(class_name) \

DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();


class Child : public CObject

{

DECLARE_DYNCREATE(myClass)

(나머지 생략)

}


위와 같이 사용된 매크로는 아래와 같이 확장된다.

class Child : public CObject

{

public:
static const CRuntimeClass classCChild;
virtual CRuntimeClass* GetRuntimeClass() const;

static CObject* PASCAL CreateObject();

}



IMPLEMENT_DYNCREATE() 매크로는 static CObject* PASCAL CreateObject() 함수의 정의와 IMPLEMENT_ RUNTIMECLASS() 매크로로 구성되어 있다.


#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject, NULL)


IMPLEMENT_DYNCREATE(CChild, CObject) 매크로는 아래와 같이 확장된다.


CObject* PASCAL CChild::CreateObject()

{

return new CChild;

}

(IMPLEMENT_RUNTIMECLASS() 매크로 확장 생략)



위의 두 개의 매크로를 사용한 클래스는 RCTI기능이 추가된 동적생성이 가능하다.

다시 위의 매크로를 살펴보면 CreateObject() 함수가 두개가 존재함을 알 수 있다. 첫번째는 CRuntimeClass의 멤버인 CreateObject() 함수로, 매크로의 유효성을 살펴본 후 (좀더 자세히 m_pfnCreateObject 멤버의 값의 NULL 여부 확인), DECLARE_DYNCREATE() 매크로로 추가된 두번째 CreateObject() 함수를 호출한다. 두번째 함수가 실질적인 클래스의 동적 생성 코드를 포함하고 있다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, RUNTIME_CLASS

DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC 매크로는 CObject에서 상속받은 클래스에 RTCI(RunTime Class Information) 혹은 Runtime Information 기능을 추가한다. 헤더파일에서 DECLARE_DYNAMIC() 매크로를 사용했다면 구현파일에서 반드시 IMPLEMENT_DYNAMIC() 매크로를 포함해 주어야 한다.


DECLARE_DYNAMIC 매크로는 afx.h 파일에 아래와 같이 선언되어 있다.


#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \

public: \
static const CRuntimeClass class##class_name; \
static CRuntimeClass* PASCAL GetThisClass(); \
virtual CRuntimeClass* GetRuntimeClass() const; \

#else
#define DECLARE_DYNAMIC(class_name) \
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \

#endif


_AFXDLL의 defined/undefined 상태에 따라서 두가지 경우로 선언되어 있다. 간단히 설명하면 MFC라이브러리가 동적으로 링크될 경우 _AFXDLL은 defined 상태가 되고 정적으로 링크된 경우에는 undefined가 된다. 현재 프로젝트의 속성 페이지에서 “구성 속성 –> 일반 –> 프로젝트 기본값 –> MFC사용” 메뉴에서 라이브러리 링크 방식을 설정할 수 있다.


IMPLEMENT_DYNAMIC() 매크로는 다음과 같다.


#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)


IMPLEMENT_DYNAMIC() 매크로는 IMPLEMENT_RUNTIMECLASS()라는 또 다른 매크로를 호출한다.


IMPLEMENT_RUNTIMECLASS() 매크로는 다음과 같다.


#ifdef _AFXDLL


#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \

CRuntimeClass* PASCAL class_name::_GetBaseClass() \
{ return RUNTIME_CLASS(base_class_name); } \

AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
&class_name::_GetBaseClass, NULL, class_init }; \

CRuntimeClass* PASCAL class_name::GetThisClass() \
{ return _RUNTIME_CLASS(class_name); } \

CRuntimeClass* class_name::GetRuntimeClass() const \
{ return _RUNTIME_CLASS(class_name); }


#else

#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \

AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL, class_init }; \

CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }

#endif



_AFXDLL이 undefined 된 상태만 살펴보도록 하자!


#define DECLARE_DYNAMIC(class_name) \
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \


DECLARE_DYNAMIC() 매크로는 클래스에 CRuntimeClass형의 정적 멤버와 이 멤버의 포인터를 리턴하는 GetRuntimeClass() 메소드를 추가한다.

아래와 같은 클래스에서 사용된 DECLARE_DYNAMIC() 매크로는

class CChild : public CObject

{

DECLARE_DYNAMIC(CChild)

//(나머지는 생략)

};


다음과 같은 코드로 확장된다.

class CChild : public CObject

{

public:
static const CRuntimeClass classCChild;
virtual CRuntimeClass* GetRuntimeClass() const;

//(나머지는 생략)

};


이제 CChild 클래스는 실행시(run-time)에 GetRuntimeClass()라는 함수를 통해 객체의 정보를 얻을 수 있는 기능을 가지게 되었다.



#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \

AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL, class_init }; \

CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }


IMPLEMENT_DYNAMIC() 매크로는 단순히 IMPLEMENT_RUNTIMECLASS() 매크로를 호출하는 역활만 하고 있다. IMPLEMENT_RUNTIMECLASS() 매크로를 살펴보면 ①에서 DECLARE_DYNAMIC() 매크로에 의해 선언된 CRuntimeClass형 정적 멤버를 초기화 하고, ②에서 초기화된 정적 멤버의 포인터를 리턴하는 함수를 구현하고 있다.


위의 CChild 클래스 예제에서, 이 클래스의 구현 파일에 IMPLEMENT_DYNAMIC(CChild, CObject)라는 매크로를 사용하면 다음과 같은 코드로 확장된다.


AFX_COMDAT const CRuntimeClass CChild::classCChild =

{

“CChild”,

sizeof(CChild),

0xFFFF,

NULL,

RUNTIME_CLASS(CObject),

NULL,

class_init // AFX_CLASSINIT 구조체와 관련된 변수인데 그 내용은 잘 모르겠음ㅠㅠ

};

CRuntimeClass* CChild::GetRuntimeClass() const
{

return RUNTIME_CLASS(CChild);

}

DECLARE_DYNAMIC(), IMPLEMENT_DYNAMIC() 매크로의 확장이 완료되면 실행시에 GetRuntimeClass() 함수를 통해 객체의 정보를 얻어올 수 있다.


마지막으로 RUNTIME_CLASS() 매크로는 다음과 같이 선언되어 있다.


#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

#ifdef _AFXDLL
#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
#else
#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
#endif


이 매크로는 매개변수로 넘어온 class_name 클래스의 정적 CRuntimeClass 멤버의 주소로 확장된다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.