static 클래스가 있는데 Initialize함수로 명시적 초기화를 하고 있다. 이 클래스의 함수를 다른 static 클래스의 생성자에서 호출하면 the type initializer for threw an exception등의 원인 파악이 힘든 오류 발생


1. 가능하면 static 클래스/생성자 사용하지 말자
2. 어쩔 수 없이 쓰더라도 수동초기화 하는 정적 클래스의 함수는 호출하지 않기
3. static 생성자는 항상 예외처리하여 vs ouput창에 trace log남기기

'library > .NET' 카테고리의 다른 글

예외처리  (0) 2019.06.27
Panel Resets Scroll Position after Focus is Lost and Regained  (0) 2012.10.06
wcf 프로젝트 설정  (0) 2011.09.05
블로그 이미지

란마12

,

예외처리

library/.NET 2019. 6. 27. 12:31
  1. 꼭 필요한 경우에만 예외를 잡자.
  2. 예외를 연속적으로 던져버리고 싶다면(스택추적정보를 깨뜨리지 않고) throw를 사용하자.
  3. 잡은 예외에 뭔가 더해서 던지고 싶다면 항상 inner exception으로 original exception을 전달하자.
    예) throw new ApplicationException("operation failed!", ex);

'library > .NET' 카테고리의 다른 글

static 클래스 초기화 주의  (0) 2020.08.13
Panel Resets Scroll Position after Focus is Lost and Regained  (0) 2012.10.06
wcf 프로젝트 설정  (0) 2011.09.05
블로그 이미지

란마12

,

MFC랑 CRT에 중복선언되어 발생되는 문제.

두 라이브러리를 모두 써야 한다면

프로젝트속성 -> 링커 -> 일반 -> 강제 파일 출력 -> /FORCE:MULTIPLE


MSDN의 다음 해결책은 별 도움이 되지 않았다.

http://support.microsoft.com/kb/148652/ko

블로그 이미지

란마12

,

다중행 처리를 위해 다음과 같이 시도했으나 안됨.

edtMemo.ModifyStyle(0, ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN);


대화상자 편집기의 해당 Edit속성창에서 관련 속성들을 true로 변경해야 됨.

Create 시점에서 해당 속성을 가지고 호출해야 하는 듯?


블로그 이미지

란마12

,

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\autoexp.dat

[Visualizer]섹션에 다음 내용 붙여넣기


[Visualizer]
;---------------------------------------------------------------------
;  MFC Types
;---------------------------------------------------------------------
CArray<*,*>|CObArray|CByteArray|CDWordArray|CPtrArray|CStringArray|CWordArray|CUIntArray|CTypedPtrArray<*,*>{
  preview([$c,!])
	children(
				#(
					#array (
						expr: $c.m_pData[$i],
						size: $c.m_nSize
							),
					#(raw : [$c,!])
				)
			)
}

CList<*,*>|CObList|CPtrList|CStringList|CTypedPtrList<*,*>{
  preview([$c,!])
    children(
        #(
			#list	(
				head: $c.m_pNodeHead,
				next: pNext
					) : $e.data,
			#(raw : [$c,!])
		)
	)
}

CMap<*,*,*,*>::CAssoc{
preview(#("key= ",$e.key,", value= ", $e.value))
}

CMap<*,*,*,*>|CMapPtrToWord|CMapPtrToPtr|CMapStringToOb|CMapStringToPtr|CMapStringToString|CMapWordToOb|CMapWordToPtr|CTypedPtrMap<*,*,*>{
    children (
        #(
            #array (
                expr : ($c.m_pHashTable)[$i],
                size : $c.m_nHashTableSize
            ) : #(
                #list (
                    head : $e,
                    next : pNext
					)
				)
        )
    )
}

http://www.codeproject.com/Articles/51610/Visualizing-MFC-Containers-in-autoexp-dat

블로그 이미지

란마12

,

A, B 두 프로그램이 있다.

A에서 짧은 경로를 사용하여 B를 실행하면 B에서 호출한 GetModuleFileName 함수는 짧은 경로를 반환한다.


The string returned will use the same format that was specified when the module was loaded. Therefore, the path can be a long or short file name, and can use the prefix "\\?\".


http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx

블로그 이미지

란마12

,

yjh_common.source.utility.test_debugging 참고


CRT(C Runtime) Debugger사용방법


일반적으로 가장 잡기 힘든 버그의 하나로서 메모리 누수, 메모리 Overwrite등을 꼽을 수 있다. 이런 문제점을 해결하기 위해 CRT(C Runtime library)에서는 여러가지 다양한 메모리 관련 디버그 함수들을 제공한다. 그러나 이것들이 디폴트로 사용하기 힘들게 꺼져 있기 때문에 대부분의 프로그래머들은 이 사실을 알지 못하는 경우가 많다. 그래서 이 글에서는 CRT의 디버그 관련 함수들에 대해 알아보고 어떻게 사용하는 것이 좋은지에 대해 논해 보려고 한다.


John Robbins(필자가 가장 좋아하는 프로그래머 중의 한명)가 지은 Debugging Applications 이라는 책에도 좋은 내용들이 있으니 참고하기 바란다. 그러나 여기 나온 팁은 그 책에는 나와 있지 않은 것이다. Numega Bounds Checker나 Rational Purify등의 툴이 비싸서 엄두도 못내는 분들께는 좋은 내용이 되리라 믿는다


메모리 누수


개요


CRT가 출력해 주는 메모리 누수 리포트 결과를 간혹 본적이 있는 사람도 있을 것이다. 만약 MFC를 주로 사용한다면 이와 같은 메시지를 자주 볼 수 있다. 왜냐하면 MFC에서는 기본적으로 CRT 디버그 기능을 켜기 때문이다. 메시지 내용은 다음과 같다


Dumping objects ->

{65} normal block at 0x003748B0, 48 bytes long.

Data: <@H7 @H7 @H7 > 40 48 37 00 40 48 37 00 40 48 37 00 CD CD CD CD

{64} normal block at 0×00374840, 48 bytes long.

Data: < H7 H7 H7 > B0 48 37 00 B0 48 37 00 B0 48 37 00 CD CD CD CD

Object dump complete.


이 메시지의 내용은 두개의 블록이 할당된다음 해제되지 않았다는 것을 의미한다. 그렇다면 우선 이 메시지들에 대해 대략 알아보도록 하자


우선 {64}, {65}와 같은 것은 메모리에 할당된 순서로서, 각각 64번째, 65번째 할당된 메모리가 해제되지 않았음을 나타낸다. Normal block이라 함은 사용자에 의해 일반적인 new, delete, malloc등으로 할당된 메모리라는 뜻이다. 그리고 그 메모리 포인터 주소를 알려주고 있으며, 몇 바이트짜리 메모리 블록인지도 나와 있다


Data:… 라인은 그 메모리의 선두번지로부터 실제 메모리의 내용이 어떤 값을 포함하고 있는지를 나타내는 것이다


그러나 위의 메시지를 보았듯이 실제로는 아무 도움이 되지 않는다는 것을 금방 알수 있을것이다. 왜냐하면 메시지의 내용이 너무 암호와같이 복잡하다는데 문제가 있다. 몇줄짜리 프로그램이라면 또 모르겠으나, 거대한 프로그램인 경우 실제로 64,65번째 할당된 메모리를 순서대로 추적하는 것은 너무나도 어려우며, 어떤 포인터에 어느 번지의 메모리가 할당되었는지를 확인하는 것도 불가능에 가깝다고 할 수 있다. 또 자신이 만든 클래스나 구조체가 몇 바이트짜리인지를 일일이 확인한다는 것도 불가능하다.


그렇다면, 이 문제를 어떻게 해결해야 할 것인가? 실제로 CRT에는 여러가지 도우미 함수들을 포함하고 있어서 이 문제를 좀 더 쉽게 해결할 수 있도록 해 준다. 그러나 제대로 알고 사용하지 않는다면 결국에는 또다른 암호 코드만을 추가로 더 얻게 될 뿐이라는 것도 알아야 한다


샘플코드


#include “stdafx.h”

int main()

{

int *p = new int;

return 0;

}


위의 코드는 int *를 할당하고 해제하지 않았으므로 명백한 메모리 누수이다. 그러나 이 프로그램을 빌드하고 디버그해봐도 프로그램 종료시 아무런 메시지를 남기지 않는다. 즉 메모리가 샌것인지, 혹은 Overwrite가 일어났는지 등을 확인할 길이 전혀 없다. 그러면 프로그램 일부를 수정해 보도록 한다


#include “stdafx.h”

#include <crtdbg.h>

int main()

{

int *p = new int;

_CrtMemDumpAllObjectsSince(0);

return 0;

}


_CrtMemBumpAllObjectsSince를 사용하기 위해 crtdbg.h를 인클루드 하고, 프로그램 종료 직전에 함수를 호출했다 디버그를 시작하고 프로그램을 종료하면 출력결과는 다음과 같다


Dumping objects ->

{64} normal block at 0×00374838, 4 bytes long.

Data: < > CD CD CD CD

Object dump complete.

‘[2288] crtdbg.exe: 기본’ 프로그램이 0 (0×0) 코드에서 끝났습니다.


위에서 설명한것과 비슷한 종류의 메시지가 포함되어 있는 것을 알 수 있을 것이다. 이 메모리는 64번째 할당된 일반 메모리이며 0×00374838번지에 4바이트 할당되었음을 알 수 있다. 또 데이터 내용은 16진수로 CD CD CD CD이다 이 정보만으로도 많은 것을 알 수 있다. 예를들어 데이터가 CD CD CD CD라는 것은 할당만 해놓고 전혀 초기화를 하지 않았다는 의미이다. 단순한 위의 프로그램 만으로도 사용자가 처음 할당한 메모리가 64번째만에 할당되었다. 이유가 무엇일까? 이유는 간단하다. main함수가 호출되기 이전에 이미 많은 메모리 할당 요청이 있었고, 그것은 프로그램을 실행시키기 위해 운영체제나, CRT가 이미 사용했기 때문이다. 위의 프로그램은 단순하기 때문에 어디서 메모리가 샜는지 한눈에 척 알 수 있다. 그러나 메모리를 수십~수백번씩 할당했다 해제하는 일반 애플리케이션에서는 어떻게 정확히 64번째 할당된 메모리를 찾아낼 수 있을까? CRT에서 내준 정보는 CRT를 이용해 분석 가능하다 main함수가 처음 시작되기 전에 다음의 함수를 사용하도록 하자


_CrtSetBreakAlloc(64);

int *p = new int;


이 함수는 64번째 메모리 할당이 발생할 경우 프로그램 실행을 중지하고 디버거로 제어를 넘기라는 의미이다. 실제로 프로그램을 실행시켜 보면, “crtdbg.exe의 0x00411cb7에 처리되지 않은 예외가 있습니다. 사용자 중단점”과 같은 메시지를 출력하면서 프로그램 실행을 중지하게 된다. 브레이크 포인트가 가리키는 위치는 CRT의 메모리 할당 함수 내부이며, Call Stack을 따라가 보면 어느곳에서 할당이 일어났는지 바로 알수 있게된다.


전역 변수와 CRT 디버깅


다음과 같은 프로그램을 보도록 하자


#include “stdafx.h”

#include “crtdbg.h”

#include <crtdbg.h>

class A

{

public:

A() { p = new int; }

private:

int *p;

};

A a;

int APIENTRY _tWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPTSTR lpCmdLine,

int nCmdShow)

{

_CrtSetBreakAlloc(64);

_CrtMemDumpAllObjectsSince(0);

return 0;

}


이 프로그램을 디버그 해 보면 똑 같은 메모리 누수를 보고하긴 하지만, 그러나 64번째 메모리 할당에서 정지하지 않는다. 이유가 무엇일까?


이유는 간단하다. A라는 클래스의 인스턴스인 a가 전역변수로 선언되었기 때문에 main함수가 호출되기 이전에 생성되기 때문이라는 것이다. a가 이미 생성되고 난 다음에 브레이크 포인트를 설정하니, 브레이크 포인트가 먹힐리가 없다. 그럼 방법이 없는 것일까?


답은 간단하다. 모든 할당이 일어나기 직전에 프로그램을 정지시켜놓고, 64번째 메모리 할당이 일어날 때 브레이크 하라는 명령을 주면 된다. 그럼 어떻게 하면 될까? 다음과 같이 하도록 한다.


1. CRT소스에서 WinMainCRTStartup()함수를 찾아낸다. 이 함수는 실질적인 main함수이며, 프로그램이 로드되기 전에 가장먼저 실행된다. 이 함수 내부에서 여러분이 정의한 main 또는 WinMain함수를 호출하게 된다. 이 함수는 파일의 찾기 기능을 이용하거나, 또는 crt0.c파일을 바로 열어서 찾아도 된다. 그러나 더 간단한 방법은 main함수에 BP를 찍어놓고, 한번 실행시킨다음 call stack을 거슬러 올라가는 방법이다.

2. WinMinCRTStartup함수의 시작부분에 BP를 찍어놓고 다시 디버거를 시작시킨다

3. Watch창을 열어 _crtBreakAlloc 변수를 확인해 본다. 아마 -1일 것이다.

4. 이 변수값을 원하는 메모리 할당 번지(위의 경우64)로 바꾼다.

5. 다시 실행시키면 64번째 메모리 할당을 하기 전에 정지한다.


이 기술은 코드를 재 컴파일 하지 않아도 디버거 상에서 바로 브레이크 포인트를 수정할 수 있다는 장점이 있다. 현재 이 방법 보다 간단하게 할 수 있는 방법은 현재 연구중에 있다. 좋은 결과가 나오는대로 다시 여러분들에게 알려드리도록 하겠다.


이상 몇가지 메모리 누수를 찾아내는 방법을 살펴보았다. 그러나 주의할 것은 반드시 crtdbg.h를 같이 인클루드 해야 한다는 것이며 _DEBUG매크로가 정의되어 있을때에만 제대로 동작한다는 것이다. 오늘은 이 정도로 글을 마무리할 생각이며 남은 몇 개의 팁들은 필자 시간나는대로(-_-) 정리해서 올릴 계획이다. 그러므로 너무 기다리지 않는 것이 정신 건강상 좋을 것이다. 참고로 필자는 매우 게으르다 -.-


요즘 CRT의 디버그 기능을 연구하기 시작하면서, 그동안 정말 좋은 기능들을 여럿 묵혀놓았다는 느낌을 지울수가 없습니다. 어렵게 메모리 관련 디버깅 루틴을 만들지 않아도, 너무나도 정확히 메모리 관련 에러를 잡아주니 STL을 처음 쓸 때 만큼이나 편리하게 느껴지더군요. 그럼 그동안 제가 연구한 것에 대해 보고드리도록 하겠습니다


지난 내용의 메모리 누수가 아닌 것 까지 모두 보고하는 문제


지난번 예제에서 약간만 더 수정해 보자


#include “stdafx.h”

#include <crtdbg.h>

#include <list>

using std::list;

typedef list<int> IntList;

typedef IntList::iterator IntListIter;

IntList myList;

int main()

{

_CrtMemDumpAllObjectsSince(0);

return 0;

}


위의 프로그램은 메모리 누수를 한 개 보고한다. 위치는 myList의 생성자이다. 그러나 정말 그것이 샌것일까? 그렇다면 STL은 항상 메모리 누수가 있다는 말인가.. 이것저것 고민하던 중에, 진짜 Main함수는 WinMainCRTStartup()라는 사실이 생각났고, 디버그 상에서 WinMainCRTStartup() 메소드의 끝까지 따라가 보았다. 그랬더니 다음과 같은 루틴을 찾을 수 있었다. (처음에는 숨겨진 함수를 찾았다고 생각했으나, 알고봤더니 MSDN에 이미 문서화 되어있는 함수였다. 역시 소스보다는 문서를 찾아보는 것이 우선이다 -.-)


/* Dump all memory leaks */

if (!fExit && _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)

{

fExit = 1;

_CrtDumpMemoryLeaks();

}


_CrtSetDbgFlag이라는 함수는 CRT디버거의 전역 플랙을 셋팅하는 함수이다. 위에서 보다 시피 이미 CRT의 메인함수가 종료할 때 메모리 누수를 검사하고 있었던 것이다. 다만 디폴트로 꺼져 있었으며 열쇠는 _CrtSetDbgFlag이라는 함수가 쥐고 있다. MSDN에서 찾아본 결과 다음과 같다.


_CrtSetDbgFlag함수는 다섯개의 Flag이 있다.

_CRTDBG_ALLOC_MEM_DF : 디폴트로 켜져 있으며, 디버그 버전에서 모든 메모리 할당이 일어날 때마다 추적 가능하도록 특별한 기능을 추가해 둔다. 이 플랙이 켜져 있어야 메모리 누수를 안전하게 검사 할 수 있다.

_CRTDBG_DELAY_FREE_MEM_DF : delete, free등으로 삭제되어도 바로 삭제되지 않고, CRT의 메모리 관리소에 남아 있다가 프로그램 종료시에 완전히 삭제된다.

_CRTDBG_CHECK_ALWAYS_DF : 모든 메모리관련 연산에서 _CrtCheckMemory를 호출한다. 이 메소드는 이후에 다시 살펴볼 것임

_CRTDBG_CHECK_CRT_DF : CRT가 내부적으로 할당한 블록도 메모리를 체크할 때 포함한다. 일반적으로는 CRT가 할당한 블록은 메모리 체크에서 제외된다. 일반적으로 사용하지 않는다

_CRTDBG_LEAK_CHECK_DF : 프로그램이 완전히 종료되기 직전에 아직 해제되지 않은 메모리가 있는지 검사한다. 프로그램의 종료 포인트가 여러군데 있는 경우에 사용하면 일일이 _CrtDumpMemoryLeaks 메소드를 호출하지 않아도 자동적으로 메모리 누수를 검사할 수 있게된다.


이 중에서 첫번째 플랙만을 제외하고는 모두 디폴트로 꺼져있다. 그러므로 다음과 같이 메모리 검사 기능을 켜도록 한다

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

위의 프로그램에서는 다음을 삭제한다 _CrtMemDumpAllObjectsSince(0);

_CrtMemDumpAllObjectsSince 함수는 실제로는 특정 지점에서 지점간에 할당되어 있는 메모리들을 보고해 주는 함수이다. 인자로 0을 넘겨주면 처음부터 지금까지 할당되어 있는 메모리들을 보고해 준다. CRT가 아직 전역으로 할당된 메모리를 완전히 삭제하기도 전에 호출했기 때문에 STL의 메모리가 샌 것 처럼 보인것이다.


20141120 연준희 추가

이렇게 해도 계속 샌 것 처럼 보이는 경우가 있다.

Solution:
The solution is to ignore what is (likely) a false report, or change the static std::string objects to static std::string* pointers. Then make a static initializer/destructor that news/deletes those string objects. This means the memory is allocated on the stack, and comes/goes in the scope of the program. The other thing you do is file a bug with Microsoft and put a few comments on the documentation website that indicates the statics are still leaking and they haven’t really fixed it yet. :)

Links:
Bug report I submitted with response here 
<https://connect.microsoft.com/VisualStudio/feedback/details/697181/crt-memory-leak-detection-routines-still-report-statically-allocated-stl-objects-as-leaks#tabs>

MS article about CRT memory debug operations that says this bug had been fixed.
http://msdn.microsoft.com/en-us/library/x98tx3cf.aspx

출처: http://mattfife.com/?p=481


CRT 메모리 블럭


다음으로 넘어가기 전에 다음을 짚어보고 넘어가기로 하자. 디버그 버전에서는 메모리가 할당되거나 사용되기 직전에 특정한 값들로 할당된 메모리가 채워진다는 것을 알고 계실것이다. 의미는 다음과 같다


0xFD : 메모리 블록 양 끝의 버퍼에 생성된다

0xCD : 새로 생성된 버퍼에 저장되는 기본값이다

0xCC : 스택에 변수가 생성되면 우선 이값으로 채워진다

0xDD : 삭제된 메모리 블록은 이 값으로 채워진다


다음 예제를 보자


int main()

{ // 함수 시작지점

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

int *p = new int [10]; // 메모리가 할당되는 지점

printf(“%d”, *p);

delete [] p; // 메모리가 삭제되는 지점

return 0;

}


함수 시작지점에서 포인터 p는 초기값으로 0xCCCCCCCC를 갖게 된다. 디스어셈블 해보면 모든 지역변수들을 0xCC로 채워넣는 것을 볼 수 있다. 이후 메모리가 할당되는 지점에서 아마도 sizeof(int) * 10 바이트의 메모리가 할당될것이다. Sizeof(int)가 4바이트라면(32비트 운영체제에서) 40바이트가 생성되는 것이다.(디버그 버전이라는 가정하에) 이렇게 생성된 40바이트에 0xCD값이 채워진다. 만약 어떤 포인터의 값을 읽었을 때 값이 0xCDCDCDCD와 같은 형식이거든 초기화안된 메모리를 읽었다고 생각하면 된다.


그런데, 위의 할당지점에서 실은 48바이트가 생성된다. 40바이트 할당 명령을 주었는데 왜 48바이트의 메모리가 할당되었는가 이유는 위의 0xFD값에 있다. 40바이트의 블록을 생성시키고 그 블록의 앞과 뒤에 각각 0xFDFDFDFD를 삽입시켰기 때문이다. 이 값이 채워지는 이유는 다음과 같이 접근하는 경우


p[-1] = 10; // underwrite

p[11] = 10; // overwrite


0xFD로 채워진 메모리의 일부분이 깨질 것이고, 나중에 사용할 메모리 체크 명령에 의해 overwrite/underwrite가 발생했다는 사실을 알 수 있게된다.


마지막으로 메모리가 삭제되는 지점에서는 이론 대로라면 p값이 0xDD로 채워질 것이다. 그러나 실제로 필자의 컴퓨터에서는 0xFEEE가 채워졌다. 왜 그런지는 좀더 연구해 보고 알려드리도록 하겠다. -.- 오늘은 일단 여기까지 접고 연구결과가 더 나오는대로 여러분께 보고하는 시간을 갖도록 하려한다.. 그럼 다들 즐푸하시길..


지난번에 이어 이번에는 메모리에 어떤 문제가 있었는지를 체크해주는 _CrtCheckMemory함수에 대해 연구해 보도록 하겠습니다.

지난 강좌에 대해

강좌를 진행하기 전에 지난번 강좌 마지막 부분에서 메모리가 삭제될 때 0xDD값 대신에 0xFEEE값이 채워지는 이유를 찾아본 결과 CRT에서는 0xDD값을 정확히 채워 넣는다는 것을 확인하였다. _CRTDBG_DELAY_FREE_MEM_DF 플랙이 디폴트로 꺼져 있기 때문에 삭제했다는 표시를 하고 난 다음 바로 운영체제에서 삭제(진짜 삭제) 해버렸기 때문이다. 이부분은 뒤에서 다시 알아보도록 하겠다.

_CrtCheckMemory()

지난번에 알아보기를 CRT에서 메모리를 할당하려 할 때 몇가지 정보블럭들을 설정한다는 것을 알았을것이다. 자 이제는 설정된 정보블럭들을 검사할 차례이다. 문제는 일일이 디버거의 Watch창이나 Memory창을 이용해 블록이나 스택이 깨졌는지를 확인해야 한다는 것이다. 이런 기법은 아마도 잘 알고 있을 것이다. 특정 메모리 주소를 가르키게 해놓고 의심되는 코드를 실행시켰을 때 Memory창의 내용이 빨간색으로 변하는 모양을 살펴서, 엉뚱한 부분까지 쓰거나, 원치않은 결과를 얻는지를 확인하는 것이다. 문제는 이것이 너무나도 수동적이기 때문에 CRT에서는 _CrtCheckMemory라는 도우미 함수를 곁들였다. 이 함수는 몇가지 경우에 있어서는 아주 쓸만하다. 사용법을 보자

int _tmain(int argc, _TCHAR* argv[])

{

if(_CrtCheckMemory() == 0)

return -1;

return 0;

}

그냥 메모리 체크를 하기 원하는 위치에 _CrtCheckMemory함수를 삽입하기만 하면된다. 만약 0을 리턴한다면 할당된 메모리에 어떤 문제가 발생했다는 것을 의미한다. 그러나 위의 코드는 문제가 하나 있는데 바로 모든 CRT의 디버그 함수들은 DEBUG버전에서만 의미를 가지기 때문에 RELEASE버전에서는 아무 의미없는 코드가 된다는 것이다. 일단 보기에도 두 줄에 걸쳐 표기되어 있으므로 흉하다. 그러므로 다음과 같이 코딩하도록 한다.

_ASSERTE( _CrtCheckMemory( ) );

_ASSERTE는 CRT에서 제공해주는 매크로이다. 또는 assert.h의 assert함수를 이용해도 좋다. MFC등을 사용한다면 ASSERT등을 사용해도 좋고 Robbins의 SUPER_ASSERT를 사용해도 좋다. 각각 약간씩의 차이점이 있기는 결과는 거의 같다. 그러니 여기서는 CRT를 사용한다는 일관성을 유지하기 위해 _ASSERTE를 사용하도록 하겠다.

단순히 위와같이 의심갈때마다 호출해 주기만 한다면, CRT는 지금까지 등록된 모든 할당된 메모리들을 검사해 문제가 발생했는지를 확인한다. 그럼 어떤 종류의 에러들을 잡아주는지 다음의 예제들을 통해 알아보도록 하자

예제1. 경계를 넘어서서 쓰는 경우

int _tmain(int argc, _TCHAR* argv[])

{

int *p = new int[10];

p[-1] = 100; // 둘다 모두 오류이다

p[10] = 100;

_ASSERTE( _CrtCheckMemory( ) );

return 0;

}


위의 예제에서는 정수 타입 10개짜리 배열을 할당하고 11번째 멤버에 쓰기를 시도하였다. 이부분에는 지난 강좌에서 알려 드렸듯이 0xfd값이 채워져 있는 영역이다. 주어진 메모리 체크 함수는 0xfd값이 있어야 할 자리에 다른 값이 있는 경우 0을 리턴한다.


예제2. 삭제된 포인터에 접근을 시도하는 경우


int _tmain(int argc, _TCHAR* argv[])

{

// _CRTDBG_DELAY_FREE_MEM_DF 플랙을 반드시 켜야된다

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF);

int *p = new int[10];

delete [] p;

p[0] = 10;

_ASSERTE( _CrtCheckMemory( ) );

return 0;

}


위 예제에서는 이미 삭제된 포인터를 다시 접근하고 있다. 이것은 디폴트로 비활성화된 플랙을 사용하므로 위의 예제에서처럼 프로그램 시작전에 _CRTDBG_DELAY_FREE_MEM_DF 플랙을 켜줘야한다. 이 플랙이 켜지게 되면 CRT는 삭제 명령(free함수)이 호출되는 경우 바로 삭제처리하지 않는다. 대신 삭제 처리했다는 표시(0xdd값)만을 남겨둔다음 필요할 때 이값이 깨졌는지 검사한다. 디버그 버전이라면 이 플랙은 반드시 켜두도록 한다. 물론 메모리 부하가 약간 더 있겠지만 심각한 오류를 검출하는데는 꼭 필요한 플랙이다. 어차피 디버그 버전은 디버깅이 최고의 목표이니까..


이 두가지 기능만해도 일반적인 프로그래머들이 겪는 대부분의 메모리 문제는 해결된다. 아! 필자가 지금까지 봐온 대부분의 메모리 관련 문제들은 거의 60%이상이 초기화가 되지 않았거나 쓰레기값이 들어있는 포인터 접근 문제였다. 이문제는 위의 함수가 잡아주지 않는다. 그럼 쓰레기값이 들어있는 포인터 접근 문제는 어떻게 해결하겠는가? 아시는 분들은 다 아실것이다 0xC0000005 오류가 바로 정답이다. 또 대부분의 컴파일러의 경우 컴파일하는 도중에 이미 초기화 안된 변수를 사용했다고 여러분들에게 알려줄 것이다.


다음에는 구간과 구간 사이에서 메모리 문제를 발견하는 방법에 대해 다룰 것이다. 전체 프로그램에서 문제를 발견했다면 그 범위를 점차 좁혀나가는 것이 중요하다. 아쉽지만 필자 개인적인 사정으로 인해 조금 늦어질지도 모르겠다.


Memory Leak Check



Visual C++에서는 MFC나 C 런타임 라이브러리 내에서 메모리 누수를 검사할 수 있는 간단한 방법을 제공한다. MFC는 사용자가 특별히 작업해주지 않아도 메모리 누수를 보고해주는 기능이 포함되어 있는 반면, MFC를 사용하지 않는 프로그램들은 메모리 누수를 검사하도록 사용자가 직접 코드를 추가해주어야 한다.


간단히 절차를 말하면, 다음과 같다.


전처리 부분의 가장 위쪽, 보통 PCH 다음에 #define _CRTDBG_MAP_ALLOC를 선언하고, #include “crtdbg.h”를 포함한다.


#include “stdafx.h”
#define _CRTDBG_MAP_ALLOC
#include “crtdbg.h”


그리고, 필요한 함수는 _CrtSetDbgFlag(), _CrtDumpMemoryLeaks(), _CrtSetReportMode() 세가지이다. 먼저, _CrtSetDbgFlag()를 사용하여 기본적인 디버그 설정을 한 다음, 메모리 누수를 검사할 덤프 시점에 _CrtDumpMemoryLeaks()를 호출하는 방식이다. 콘솔 프로그램을 작성한다면, 보통 main() 함수에서 return 이전 시점이 될 것이다.


여기서, 프로그램이 종료되는 위치를 고정적으로 선택할 수 없다면, _CrtDumpMemoryLeaks()를 종료되는 부분에 모두 작성하는 대신, _CrtSetDbgFlag() 함수를 사용할 수 있다. 이 함수의 파라미터 설정에 따라 어느 시점에서 메모리 누수를 검사할 수 있는지 설정하는 것이 가능하다. 예를 들어서, C++의 소멸자를 사용한 메모리 해제는 일반적인 경우라면 메모리 누수라고 보고될 것이다. 즉, 스마트 포인터와 같은 것을 작성하는 도중이라면, 소멸자의 행동까지 추적하지 않기 때문에 메모리 누수라고 보고될 것이다. 이 경우, _CrtSetDbgFlag()의 파라미터를 적절히 설정하는 것으로 메모리 누수 검사 시점을 선택할 수 있다.


_CrtSetReportMode()는 사용해도 그만이고, 사용하지 않아도 그만이다. 중요한 것은 _CrtSetDbgFlag()와 _CrtDumpMemoryLeaks()이다.


예를 든다면, 다음과 같다.


#define _CRTDBG_MAP_ALLOC
#include “crtdbg.h”

int main()
{
    int DbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
    DbgFlag |= _CRTDBG_LEAK_CHECK_DF;
    _CrtSetDbgFlag(DbgFlag);

    char* pMem = new char(64);

    _CrtDumpMemoryLeaks();
    return 0;
}


여기서, _CrtDumpMemoryLeaks()를 프로그램의 종료 시점에서 호출하고 싶다면, DbgFlag에 _CRTDBG_ALLOC_MEM_DF 플래그를 추가로 설정해주어야 한다. 즉, 위의 코드와 같은 경우, _CrtDumpMemoryLeaks() 다음에 힙을 정상적으로 해제하는 코드가 있더라도 메모리 누수를 보고하게 된다. _CRTDBG_ALLOC_MEM_DF 플래그를 설정하면, _CrtDumpMemoryLeaks()를 호출하지 않더라도 Output 윈도우에 메모리 누수 여부를 알려줄 것이다.


MSDN Document : http://support.microsoft.com/kb/601929/ko,
http://msdn.microsoft.com/en-us/library/5at7yxcs(VS.71).aspx

http://enddl22.net/wordpress/?p=2106

'library > CRT' 카테고리의 다른 글

컴파일 옵션  (0) 2013.02.22
메모리 누수 체크  (0) 2011.10.27
errno  (0) 2011.01.08
유니코드를 위한 함수  (0) 2011.01.08
시간과 날짜와 관련된 라이브러리 함수들  (0) 2010.12.15
블로그 이미지

란마12

,

자식 컨트롤이 겹칠 때 특정 컨트롤을 맨앞으로 오게 하고 다른 컨트롤과 이벤트나 UI가 겹쳐지지 않게 하는 방법


겹쳐지는 관련 컨트롤 모두에 WS_CLIPSIBLINGS | WS_CLIPCHILDREN 스타일 추가

BringWindowToTop함수 호출

블로그 이미지

란마12

,

컴파일 옵션

library/CRT 2013. 2. 22. 18:28

1. 단일 스레드는 더 이상 지원하지 않음.


2. /MT, /MTd

 1) CRT 정적 연결


3. /MD, /MDd

 1) CRT 동적 연결

 2) 버전별 dll필요(예: msvcr100.dll, msvcp100.dll)


4. 정적 연결은 배포가 편리하지만 메모리/파일용량 사용이 비효율적.

    현재같이 시스템 성능이 좋은 환경에서는 배포가 편리한 정적 연결이 좋은 것같은데 이견있으신 분 댓글 부탁.


Standard C++ Headers

ALGORITHMBITSETCOMPLEXDEQUE
FSTREAMFUNCTIONALIOMANIPIOS
IOSFWDIOSTREAMISTREAMITERATOR
LIMITSLISTLOCALEMAP
MEMORYNUMERICOSTREAMQUEUE
SETSSTREAMSTACKSTDEXCEPT
STREAMBUFSTRINGSTRSTREAMTYPEINFO
UTILITYVALARRAYVECTORXIOSBASE
XLOCALEXLOCINFOXLOCMONXLOCNUM
XLOCTIMEXMEMORYXSTRINGXTREE
XUTILITYYMATH.H

You cannot mix calls to the old iostream library and the new Standard C++ Library in Visual C++ 4.2.

Compile optionLibraries linked with
/ML (VC++ .NET 2003 and earlier)LIBC.LIB, LIBCP.LIB
/MLd (VC++ .NET 2003 and earlier)LIBCD.LIB, LIBCPD.LIB
/MTLIBCMT.LIB, LIBCPMT.LIB
/MTdLIBCMTD.LIB, LIBCPMTD.LIB
/MDMSVCRT.LIB, MSVCPRT.LIB
/MDdMSVCRTD.LIB, MSVCPRTD.LIB

Summary Table for CRT DLLs Used

Import library linked withDLLs used (Visual C++ 5.0|6.0)DLLs used (Visual C++ .NET 2002|Visual C++ .NET 2003)DLLs used (Visual C++ 2005|Visual C++ 2008
MSVCRT.LIBMSVCRT.DLLMSVCR7(0|1).DLLMSVCR(80|90).DLL
MSVCRTD.LIBMSVCRTD.DLLMSVCR7(0|1)D.DLLMSVCR(80|90)D.DLL
MSVCPRT.LIBMSVCP(5|6)0.DLLMSVCP7(0|1).DLLMSVCP(80|90).DLL
MSVCPRTD.LIBMSVCP(5|6)0D.DLLMSVCP7(0|1)D.DLLMSVCP(80|90)D.DLL
MSVCIRT.LIBMSVCIRT.DLL
MSVCIRTD.LIBMSVCIRTD.DLL


http://support.microsoft.com/kb/154753/en-us

'library > CRT' 카테고리의 다른 글

CRT(C Runtime) Debugger사용방법  (0) 2014.05.14
메모리 누수 체크  (0) 2011.10.27
errno  (0) 2011.01.08
유니코드를 위한 함수  (0) 2011.01.08
시간과 날짜와 관련된 라이브러리 함수들  (0) 2010.12.15
블로그 이미지

란마12

,

http://daddygoms.tistory.com/518

http://en.wikipedia.org/wiki/Regular_expression

'library' 카테고리의 다른 글

STL string vs CRT char  (0) 2011.01.04
블로그 이미지

란마12

,