통합검색
· 마을서비스란?  · 포럼마을  · 일반마을  · 테마마을  · 마을랭킹  · 활동왕
· 덱스퍼트란?  · TECBOX   · PRSBOX   · 이용안내  
· DEXT제품군  · 솔루션베이  · S/W & ESD 컴포넌트
· 프로그램베이
· LiveSeminar  · LiveConference
Visual C++ 포럼마을 입니다.
  마을등급 Visual C++   이 마을은 포럼마을 입니다이 마을은 자유가입제 마을 입니다 마을소개 페이지로 이동 전입신청
마을촌장촌장 나성훈 주민 34036 since 2006-12-29
우리마을 공지사항
질문&답변
강좌&팁
자유게시판
자료실
앨범
개인게시판
마을 게시판
등록된 마을 게시판이
없습니다.
랑데브 게시판
칼럼 게시판
개발자 고충상담
Dev Talk
자유토론방
벼룩시장
재나미 우스개
구인/프로젝트 정보
사람인 채용 게시판
  고객지원 게시판
마이 데브피아
 나의 e-Money 내역
 활동왕 My Page
 스크랩한 게시글보기
 쪽지관리
 주소록관리

 강좌&팁
 네이티브 환경에서 사용자 정의 텍스트 시각화 만들기   | VC++ 일반 2017-03-15 오후 8:48:14
 kyh2984@hotmail.com  kyh2984@hotmail.com님께 메시지 보내기kyh2984@hotmail.com님을 내 주소록에 추가합니다.kyh2984@hotmail.com님의 개인게시판 가기 번호: 8870 추천:0  / 읽음:2,576

비주얼 스튜디오는 자타가 공인하는 최고의 개발 도구중 하나이다. 개발자는 자신의 머릿속의 애플리케이션 수행 순서를 비주얼 스튜디오의 텍스트 에디터를 통해 표현하고 디버거를 통해 자신이 작성한 코드에 대한 일차 검증을 한다. 개발자는 디버깅중 자동(Autos), 지역식(Locals), 조사식(Watch), 메모리(Memory)창과 코드 에디터 영역을 뚫어지게 주시하며 빨간색으로 변하는 텍스트 시각화 영역의 숫자 하나의 값에 따라 스트레스를 받기도 하고, 받았던 스트레스를 한번에 날려 버리기도 한다.

 

 

 

 

 

<화면 1> 비주얼 스튜디오의 기본적인 디버깅 환경의 예

 

 

 

C++언어에서의 디버깅은 지속적으로 스트레스를 받는 시간일 것이다. 이러한 스트레스는 디버깅에 필요한 배경지식, 소스코드의 전체적인 흐름에 대한 이해, 디버깅 툴의 숙련도등에 따라 줄여줄 수 있다. 비주얼 스튜디오가 제공하는 시각화의 사용자 정의 규칙(Visualizer rules) 적용은 디버깅에 익숙한 개발자 중에서도 가장 나중에 접하고 시도해 보는 디버깅 스킬중 하나다. 그래서 짚고 넘어가지 않는다고 해도 개발하고 디버깅하는데는 문제가 있는건 아니지만 시각화 규칙에 대하여 알아본 후 나름대로 프로젝트에 적용해 본다면 디버깅 시간이 단축될 뿐만 아니라 단순 반복하는 디버깅 횟수가 줄어들어 그만큼 개발자가 받는 스트레스를 덜어줄 것이다.

 

 

 

<화면 2> 닷넷계열 언어의 사용자 정의 비주얼라이저의 예

 

 

 

비주얼 스튜디오에서는 디버깅시 개발자가 빈번히 접하게 되는 데이터 시각화(Data Visualizer)에 대한 커스터마이징을 할 수 있는 방법을 제공해 주고 있다. 이를 이용해 간단한 사용자 정의 시각화 모듈을 만들어 디버깅시 큰 도움을 받을 수 있는데, C#과 같은 관리코드의 경우 사용자 정의 시각화 모듈과 비주얼 스튜디오 간의 데이터를 넘겨 주고 받는 부분이 쉽고 간편한데다 구현시DebuggerTypeProxyAttribute,  DebuggerDisplayAttribute, DebuggerBrowsableAttribute 정도의 API만 알아도 디버깅시 표현하기 어려운 이미지와 같은 데이터도 쉽게 시각화 시켜 보여줄 수 있다. 반면 C++은 간편하게 적용할 수 있는 텍스트 시각화 방법과 함께 관리코드보다는 구현 방법이 번거로워 C++로 구현시 제약사항이 있긴 하지만 EEAddin (Expression Evaluator Add-In)을 이용하여 데이터 시각화 기능을 확장할 수 있는 방법을 제공해 주고 있다.

 

 

 

<화면 3> 텍스트 시각화 규칙 도움을 받지 못한 vector 클래스의 예

 

 

 

비주얼 스튜디오의 디버깅창의 텍스트 시각화 사용자 정의 규칙은 autoexp.dat파일을 통하여 정의되고, 정의된 문법을 통해서 자동, 지역식, 조시식 창등에서 변수 값을 조회하는 용도로 사용되어 졌다. 혹시 오랫동안 비주얼 스튜디오를 통하여 개발한 경험이 있는 독자들은 초기 버전의 비주얼 C++에서 STL(Standard Template Library)을 사용하기 힘들었던 경험이 있을 것이다. 당시 비주얼 C++에서는 STL 코드가 보내는 난해한 에러 코드, 워닝 메세지 함께 디버깅시 텍스트 시각화를 통해 내가 원하는 정보를 찾기 위해 인스턴스가 포함하고 있는 맴버 변수들을 한참동안 뒤져야 했었다. 비주얼 스튜디오의 새로운 버전이 나오고 업데이트 될 수록 텍스트 시각화가 보기 편해지고 디버깅시 원하는 정보들이 나왔던 경험이 있을 것이다. 비주얼 스튜디오의 버전이 올라가면서 C++ 타입에 대한 텍스트 시각화의 정보를 개발자가 보기에 편해진 이유는 텍스트 시각화 사용자 정의 규칙을 지속적으로 업데이트 시켜 주었기 때문이다.

 

 

<화면 4> 한눈에 알아보기 쉬워진 vector 클래스의 예

 

 

 

텍스트 시각화 사용자 정의 규칙이 비주얼 스튜디오 2012부터 대대적으로 업그레이드 되었다. 기존 사용자 정의 규칙 정의 기술 방식이였던 autoexp.dat 파일의 단점을 보완하면서 개발자가 사용하기 쉽도록 xml 문법으로 규칙을 기술하도록 하였고, 텍스트 시각화 사용자 정의 규칙 정의시 발생할 수 있는 문제점에 대하여 추적하기 쉬워 졌으며, 정의 파일에 대한 버전관리나 여러 파일로 나누어 프레임워크별, 규칙별, 혹은 프로젝트별 사용자 정의 규칙 적용이 가능해 지는 등 새로워진 텍스트 시각화 사용자 지정 방식을 표현하는 기능인 Natvis 프레임워크(Native visualization framework)를 새롭게 지원해 준다.

 

 

 

텍스트 시각화

 

Natvis 프레임워크가 제공하는 문법을 통해 natvis 파일을 만들고 특정 폴더에 위치 시켜 비주얼 스튜디오의 디버거가 사용자 정의 규칙을 인식했다면 텍스트 시각화 사용자 정의 규칙이 적용된 디버깅 환경을 바로 확인해 볼 수 있다. Natvis 파일은 XML 형식의 텍스트 파일로 되어 있으며 64비트 운영체제에서 기본 폴더에 비주얼 스튜디오 2013을 설치 했다면 Natvis 프레임워크의 스키마 파일은 아래의 경로에서 확인해 볼 수 있다. 32비트 운영체제라면 %PROGRAMFILES(X86)%를 %PROGRAMFILES%로 변경해 접근하도록 한다. 또한 비주얼 스튜디오 2012 버전이라면 폴더의 문자열 중 비주얼 스튜디오 버전의 12.0 대신 11.0으로 접근해야 함을 쉽게 알 수 있을 것이다.

 

 

 

%PROGRAMFILES(X86)%\Microsoft Visual Studio 12.0\Xml\Schemas\1033\natvis.xsd

 

 

 

Natvis 파일이 아래의 두 지정된 경로에 존재할 경우 디버깅 시 디버거가 자동으로 불러들인다. 프로그램 파일 경로의 경우 계정에 관계 없이 전역적으로 사용자 정의 규칙을 반영할 수 있고 사용자 폴더 경로의 경우 사용자별 비주얼 스튜디오의 사용자 정의 규칙을 반영시킬 수 있다.

 

 

 

%PROGRAMFILES(X86)%\Microsoft Visual Studio 12.0\Common7\Packages\Debugger\Visualizers

 

%USERPROFILE%\My Documents\Visual Studio 2013\Visualizers

 

 

 

만일 두 특정 경로이외의 경로에 위치 시키고 싶다면 vsixmanifest파일에 Asset 태그의 NativeVisualizer 타입을 설정하고 경로를 지정한다.

 

 

 

<리스트 1> Asset 엔트리에 NativeVisualizer 타입으로 natvis 파일의 위치를 지정할 수 있다.

 

<?xml version="1.0" encoding="utf-8"?>

 

<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">

 

<Installation>

 

<InstallationTarget Version="[11.0,12.0)" Id="Microsoft.VisualStudio.Premium" />

 

<InstallationTarget Version="[11.0,12.0)" Id="Microsoft.VisualStudio.Pro" />

 

<InstallationTarget Version="[11.0,12.0)" Id="Microsoft.VisualStudio.Ultimate" />

 

</Installation>

 

<Assets>

 

<Asset Type="NativeVisualizer" d:Source="File" Path="Visualizers\directx.natvis" />

 

<Asset Type="NativeVisualizer" d:Source="File" Path="Visualizers\casablanca.natvis" />

 

</Assets>

 

</PackageManifest>

 

 

 

Natvis 프레임워크

 

Nativis 프레임워크는 생각보다 사용하기 쉽고 특유의 XML 기술방식으로 간단하면서 이해하기 쉽다. 작성된 Natvis 파일은 비주얼 스튜디오를 통해 디버깅이 시작되는 순간 디버거에 로드되어 비주얼 스튜디오에 바로 반영된다. 따라서 F5키를 누르는 순간 수정된 Natvis 파일은 적용되므로 Natvis 파일을 수정하면서 디버거에 바로 적용해 사용하거나 테스트 하는 것이 가능하다.

 

 

 

<리스트 2> 기본적인 Natvis 파일의 XML 정의

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="std::vector&lt;*&gt;">

 

...

 

</Type>

 

<Type Name="...">

 

...

 

</Type>

 

</AutoVisualizer>

 

 

 

Nativs 프레임워크를 활용한 사용자 정의 텍스트 시각화를 위한 Natvis 파일을 만들어 보자. 먼저 %USERPROFILE%\My Documents\Visual Studio 2013\Visualizers 폴더에 test.natvis 파일을 생성했다.

 

 

 

<화면 7> Type Name에 C++ 타입을 입력하면 매칭되는 인스턴스의 시각화 영역에 반영된다.

 

 

 

Nativis 파일은 디버거가 인지할 수 있는 폴더에 위치하고 있기만 하면 파일의 개수와 파일명에 대한 제약 사항이 없다. 따라서 클래스별, 혹은 사용자가 만든 프레임워크별, 프로젝트의 버전별등 각자의 상황에 맞는 종류의 Natvis파일을 만들어 활용할 수 있다.

 

 

 

Natvis 파일 디버깅

 

Natvis 프레임워크는 시각화 표현에 대한 일정한 문법을 가지고 있다. 만일 natvis 파일이 문법에 맞지 않는다면 텍스트 시각화가 적용되지 않는것 처럼 보일 것이다.

 

 

<화면 8> Natvis 디버깅을 위한 레지스트리 설정

 

 

 

비주얼 스튜디오는 Natvis 프레임워크를 파싱하고 적용하며 문제가 있을 경우 문제 있는 내용을 출력창(Output)을 통하여 피드백 해 주는 옵션을 설정할 수 있는데, 초기 Natvis 프레임워크를 이용한 문법 작성시 어디에서 문제가 있는 파악하는데 큰 도움이 된다. 디버깅 옵션을 설정하는 방법은 아래의 레지스트리키에 EnableNatvisDiagnostics 항목을 만들고 DWORD 값으로 1을 부여한다.

 

 

 

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0_Config\Debugger

 

 

 

<리스트 3> EnableNatvisDiagnostics의 값을 1로 설정하면 확인할 수 있는 Natvis 프레임워크의 디버깅 메시지

 

Natvis: Parsing natvis xml file: test.natvis.

 

Natvis: test.natvis(4,6): Successfully parsed expression 'memo_._Mysize==0' in type context 'ScheduleEntry'.

 

Natvis: test.natvis(5,6): Successfully parsed expression 'memo_._Mysize!=0' in type context 'ScheduleEntry'.

 

Natvis: test.natvis(8,65): Fatal error: Element '{http://schemas.microsoft.com/vstudio/debugger/natvis/2010}Synthetic' is unexpected according to content model of parent element '{http://schemas.microsoft.com/vstudio/debugger/natvis/2010}Type'.

 

Expecting: {http://schemas.microsoft.com/vstudio/debugger/natvis/2010}AlternativeType,{http://schemas.microsoft.com/vstudio/debugger/n....

 

 

 

EnableNatvisDiagnostics 레지스트리 값을 1로 설정하였다면 Natvis 프레임워크가 Natvis 파일을 분석하는 과정에서 구문 분석 오류나 시각화 과정에서 발생하는 오류에 대해 로그를 남겨주고, 성공적으로 Natvis 문법이 적용되어 성공적으로 시각화가 일어났을 경우에도 로그를 남겨주므로 Natvis 파일을 효율적으로 만들 수 있다.

 

 

 

Natvis 파일의 이해와 활용

 

Natvis 파일은 C++의 타입을 대상으로 시각화할 형태를 간단한 문법으로 기술한다.

 

 

 

<리스트 4> SYSTEMTIME을 상속받은 클래스의 예

 

class ScheduleEntry : public SYSTEMTIME {

 

public:

 

ScheduleEntry() {

 

GetLocalTime(this);

 

}

 

void SetMeno(std::basic_string<wchar_t> const& memo) {

 

memo_ = memo;

 

}

 

private:

 

std::basic_string<wchar_t> memo_;

 

};

 

ScheduleEntry entry1;

 

ScheduleEntry entry2;

 

entry2.SetMeno(L"테스트");

 

 

 

먼저 테스트를 위한 간단한 클래스를 작성하고 브레이크 포인트를 설정한 후 entry1과 entry2의 인스턴스의 시각화가 어떻게 이루어지는지 살펴 보자. <리스트 4>는 간단한 테스트를 위해 작성한 임의의 클래스이다. 이를 작성한 후 소스 코드창에 브레이크 포인트를 설정하고 디버깅 모드로 실행한 뒤 텍스트 시각화 창을 통해 ScheduleEntry 인스턴스를 확인하면 <화면 9>와 같음을 알 수 있다.

 

 

<화면 9> 자동으로 반영되는 텍스트 시각화의 예

 

 

 

일반적으로 디버거는 C++ 타입을 기본적인 시각화 방법으로 해당 타입이 가지고 있는 맴버들을 간략히 한줄로 표시해 주고 트리 확장을 통해 타입이 가지고 있는 맴버를 자세히 표시해 준다. ScheduleEntry 클래스는 맴버로 memo_를 가지고 있고 SYSTEMTIME 구조체를 상속 받았다. 이에 텍스트 시각화는 ScheduleEntry 의 인스턴스에 대한 표시 값(Display value)으로 memo_ 변수가 가지고 있는 값을 중괄호 속에 보여 주고 있고 트리 확장을 통해 SYSTEMTIME 구조체 값과 memo_ 변수를 보여주고 있다.

 

Natvis 문법 중 DisplayString을 이용하면 타입이 가지고 있는 맴버의 정보를 한 줄로 표기해 주는 표시 값 문자열을 변경할 수 있다.

 

 

 

<리스트 5> DisplayString을 설정한 Natvis 파일의 예

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="ScheduleEntry">

 

<DisplayString>Hello World</DisplayString>

 

</Type>

 

</AutoVisualizer>

 

 

 

Natvis 파일의 Type Name에는 시각화를 적용할 C++ 타입을 명시하고 DisplayString 요소에는 표기할 문자열을 적는다. Type Name에는 엔티티코드(Entity Code)나 모든 문자열과 대응되는 값이라는 일반적인 의미로 사용되는 ‘*’와 같은 기호가 올 수 있다. 아래는 템플릿 타입 인자로 모든 타입을 포함하는 템플릿 vector 클래스의 Type Name의 지시 방법이au &lt;, &gt;는 각각 ‘<’ 와 ‘>’로 대응되는 엔티티코드다.

 

 

 

<Type Name=“std::vector&lt;*&gt;”>

 

 

 

<화면 10>은 <리스트 5>의 Natvis 파일이 적용된 텍스트 시각화의 결과를 보여준다.

 

 

<화면 10> DisplayString을 이용한 표시 값 변경

 

 

 

조건에 따라 DisplayString의 표시 값을 변경하고 싶을 때, DisplayString 요소에 Condition 설정 값을 사용한다. Condition 설정에는 Type Name 요소에 기술된 C++ 타입의 맴버를 사용하여 조건식 판단에 따라 DisplayString 적용 여부를 결정한다.

 

 

 

<리스트 6> 조건에 따라 DisplayString 설정을 수행하는 Natvis 파일의 예

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="ScheduleEntry">

 

<DisplayString Condition="memo_._Mysize==0">disable</DisplayString>

 

<DisplayString Condition="memo_._Mysize!=0">enable</DisplayString>

 

</Type>

 

</AutoVisualizer>

 

 

 

디버거는 DisplayString을 순차적으로 수행하면서 DisplayString에 설정된 Condition대로 조건식을 판단하여 표시 값을 선택하며 모든 조건이 만족하지 않을 경우 기본 표시 값을 보여준다. <리스트 6>에는 ScheduleEntry의 맴버인 memo_의 _Mysize 값을 이용하여 조건식을 구성하였다. 디버거는 ScheduleEntry 인스턴스의 표시 값이 필요한 경우 DisplayString 태그를 검색하고 Condition 속성이 부여되어 있으면 순서대로 조건식이 참인 DisplayString을 선택한다. 디버거는 먼저 _Mysize==0인 조건을 만족하는지 살펴 본 후 조건식이 참이라면 "disable"를 표시식으로 출력하고 조건이 거짓이라면 다음번 DisplayString의 조건식을 통하여 "enable" 출력 여부를 판단한다.

 

 

<화면 11> DisplayString의 Condition을 이용하면 표시 값을 선택할 수 있다

 

 

 

맴버가 가지고 있는 값을 표시하고 싶다면 중괄호를 사용한다. <리스트 7>은 중괄호를 사용하여 맴버의 값을 표시하는 방법을 보여주고 있다. ScheduleEntry 클래스는 SYSTEMTIME 구조체를 상속받고 있기 때문에 ScheduleEntry 인스턴스는 wYear, wMonth 등의 SYSTEMTIME 맴버를 가지고 있다.

 

 

 

<리스트 7> 중괄호를 사용하여 맴버 값을 표시한 Natvis 파일의 예

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="ScheduleEntry">

 

<DisplayString Condition="memo_._Mysize==0">disable</DisplayString>

 

<DisplayString Condition="memo_._Mysize!=0">{wYear}/{wMonth}/{wDay}/{{{wHour*60+wMinute}}}</DisplayString>

 

</Type>

 

</AutoVisualizer>

 

 

 

wYear이나 wMonth등의 맴버 값을 표시해 주기 위해 DisplayString 태그에 중괄호를 이용하였다. {wYear}, {wMonth}등 Type Name에 지정된 C++ 타입에서 접근할 수 있는 맴버라면 해당 맴버의 값이 표시 값으로 나타나게 된다. 중괄호 안쪽에는 맴버를 사용한 수식을 평가해 값을 표시 값으로 나타내 줄 수 있다. {wHour*60+wMinute} 이라면 해당 수식을 계산한 값을 텍스트 시각화로 표시해 줄 수 있다. 중괄호 자체를 표시하고 싶다면 중괄호 두 개를 연속으로 사용한다.

 

<화면 12>는 <리스트 4>의 소스 코드를 <리스트 7>의 Natvis 텍스트 시각화로 나타는 모습이다.

 

 

<화면 12> 중괄호를 사용하여 맴버 값을 표시한다.

 

 

 

디버깅을 하다 보면 std::string 타입의 텍스트 시각화에서 타 타입과 다르게 돋보기 모양의 아이콘이 있는 것을 확인할 수 있다. 돋보기 모양의 아이콘의 드롭다운 버튼을 누르면 텍스트 비주얼라이저, XML 비주얼라이저 HTML 비주얼라이저를 선택할 수 있다. 해당 비주얼라이저는 타입이 가지고 있는 텍스트를 각각 텍스트, XML, HTML 형식으로 해석하여 별도의 다이얼로그를 통해 보여주는 역할을 한다.

 

<화면 13> 사용자 정의 텍스트 시각화 영역에 비주얼라이저 버튼 추가의 예

 

 

 

사용자 정의 텍스트 시각화에도 이러한 비주얼라이저 선택 버튼을 추가하고 별도의 비주얼라이저 다이얼로그를 통해 데이터를 표현할 수 있다. StringView 요소에 표현할 맴버 및 스트링을 기술한다. <리스트 8>은 StringView를 이용해 memo_ 맴버를 비주얼라이저를 통하여 볼 수 있도록 한 예이다.

 

 

 

<리스트 8> StringView를 통해 비주얼라이저 버튼을 추가한다.

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="ScheduleEntry">

 

<DisplayString> {wYear}/{wMonth}/{wDay}/{{{wHour*60+wMinute}}}</DisplayString>

 

<StringView>memo_</StringView>

 

</Type>

 

</AutoVisualizer>

 

 

 

비주얼라이저 버튼을 누르면 <화면 14>와 같이 memo_의 내용이 별도의 비주얼라이저 다이얼로그를 통해 표현되는 것을 확인할 수 있다.

 

 

<화면 14> 비주얼라이저 다이얼로그를 통해 데이터를 표현하는 모습의 예

 

 

 

텍스트 시각화에서 확장되는 트리 구조의 데이터 보기는 Expand 예하 요소를 통해 설정이 가능하다. Expand 요소에 Item 태그를 이용하여 트리 구조 확장시 보여지는 항목에 대한 설정을 할 수 있다. <리스트 9>는 일반적으로 std::vector<T>가 텍스트 시각화에 보여주는 형태로 간략하게 표현한 사용자 정의 텍스트 시각화 양식의 예이며 <화면 15>에서 결과를 확인할 수 있다.

 

 

 

<리스트 9> std::vector<T> 클래스의 사용자 정의 텍스트 시각화의 예

 

<Type Name="std::vector&lt;*&gt;">

 

<DisplayString>{{size = {_Mylast - _Myfirst}}}</DisplayString>

 

<Expand>

 

<Item Name="[size]">_Mylast - _Myfirst</Item>

 

<Item Name="[capacity]">(_Myend - _Myfirst)</Item>

 

<ArrayItems>

 

<Size>_Mylast - _Myfirst</Size>

 

<ValuePointer>_Myfirst</ValuePointer>

 

</ArrayItems>

 

</Expand>

 

</Type>

 

 

 

ArrayItems 태그는 데이터가 배열 형태 배치 되어 있는 C++ 타입의 데이터를 보여주기에 좋은 텍스트 시각화 방식이며 Size, ValuePointer, LowerBound, Direction, Rank 태그 등이 올 수 있다. Size와 ValuePointer는 ArrayItems 태그를 시작했다면 필수적으로 와야 하는 하위 태그로서 각각 표현할 배열의 크기와 배열의 시작 주소를 설정할 수 있다. 만일 적절한 Size와 ValuePointer를 주었다면 배열의 시각화 시작은 0번지부터 시작하는데, LowerBound 태그를 통하여 시각화 시작을 설정할 수 있다.

 

 

<화면 15> std::vector<T> 클래스의 텍스트 시각화의 예

 

 

 

Direction과 Rank 태그는 ArrayItems 하위의 태그로 다차원 배열 표현시 값을 설정한다. 비주얼 스토디오 2013에 포함된 concurrency.natvis에는 Concurrency::array에 Direction과 Rank를 사용하여 시각화한 예가 있으며 결과 화면은 <화면 16>을 참고한다.

 

 

 

<화면 16> Concurrency::array를 비주얼 스튜디오 2013에 포함된 Natvis로 시각화 한 예

 

 

 

Natvis 프레임워크에서 사용할 수 있는 매크로

 

Natvis 문법에서 사용할 수 있는 몇가지 매크로가 있다. 그중 $(Type), $T1, $T2은 가장 많이 사용되는 매크로중 하나이며 $i는 숫자로 대입되는 파라미터 이다.

 

 

 

<리스트 10> $(Type)은 텍스트 시각화시 타입 명으로 대체되어 표기된다.

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="Room">

 

<DisplayString>{m_squareFeet}-square foot $(Type)</DisplayString>

 

</Type>

 

</AutoVisualizer>

 

 

 

비주얼 스튜디오는 Natvis 파일에 기술된 $(Type)을 해당 타입으로 대체하여 텍스트 시각화에 표기 한다. 만일 DisplayString 노드에 $(Type)을 사용했다면 타입으로 대체되어 나타난다.

 

 

 

<화면 17> $(Type)이 타입명으로 대체되어 표기된 예

 

 

$T1, $T2등은 Type에 템플릿 클래스 타입이 기술되었을 때, 인자화 된 파라미터 타입으로 대체된다.

 

 

 

AlternativeType 노드

 

Natvis의 클래스나 구조체등의 자료형을 지정은 Type 노드를 사용한다. Type 노드에 지정되야 하는 타입은 반드시 타입을 정의할 때 사용된 타입이어야 한다. 예를 들어 윈도우 기본 타입인 RECT를 Natvis 파일에 정의하기위해 RECT를 Type Name에 지정하면 올바르게 디버거가 인식하지 않는다. RECT는 tagRECT의 별칭이기 때문인데, 코딩상으로는 정의에 지정된 타입과 별칭을 선언할 때 차이점이 없지만 Natvis 파일에는 정확히 타입을 정의할 때 사용했던 타입명을 지정해야 한다. 마찬가지 이유로 Natvis 파일에PRECT, NPRECT, LPRECT,  LPCRECT등은 사용할 수 없다.

 

 

 

<리스트 11> Type 노드에는 반드시 타입을 정의할 때 사용한 타입명을 지정해야 한다.

 

// windef.h

 

typedef struct tagRECT {

 

LONG left;

 

LONG top;

 

LONG right;

 

LONG bottom;

 

} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

 

typedef const RECT FAR* LPCRECT;

 

 

 

<!-- 올바르게 MyStructW1 타입을 지정 한 Natvis 파일 -->

 

<Type Name="tagRECT">

 

<!-- 올바르지 않은 Natvis 파일의 Type Name 지정 -->

 

<Type Name="RECT">

 

 

 

코딩을 하다 보면 동일한 클래스 타입을 다른 이름으로 선언하거나 비슷한 기능을 하는 클래스지만 디버깅시 텍스트 시각화는 동일하게 보여 지는 경우가 있다. 예를 들어 CArray로부터 파생된 CPtrArray, CStringArray등은 각각 다른 클래스 타입이지만 디버깅시 CArray와 동일한 시각화를 보여주어도 관계없으며 오히려 디버깅 시 이러한 클래스 타입은 동일한 텍스트 시각화가 표시 되어야 혼란스럽지 않다. 이렇게 같거나 혹은 유사하지만 핵심 맴버는 동일한 클래스 타입의 경우 각 클래스 별로 별도의 Type을 기술하지 않고, 하나의 Type의 항목에 AlternativeType으로 지정할 수 있다.

 

 

 

<리스트 12> AlternativeType으로 지정된 타입은 Type Name으로 지정된 타입과 동일한 텍스트 시각화를 표기해 준다.

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="CArray&lt;*,*&gt;">

 

<AlternativeType Name="CObArray"></AlternativeType>

 

<AlternativeType Name="CByteArray"></AlternativeType>

 

//...

 

</AutoVisualizer>

 

 

 

<Expand> 노드

 

비주얼 스튜디오의 디버거가 제공하는 클래스 타입의 기본적인 텍스트 시각화는 Natvis의 DisplayString에 표기되는 영역에 표기할 타입의 맴버 값이 순서대로 표기가 되고, 트리 구조로 하단에 각 맴버들에 대한 자세한 값과 타입이 명시되는 단순한 모습이다.

 

<화면 18> 기본적인 텍스트 시각화의 화면

 

 

 

Expand의 Item 노드는 사용자 정의 텍스트에서 디버거에서 타입이 가지고 있는 항목을 트리 구조로 나타낼 수 있도록 Item을 지정할 수 있다. 클래스 타입이 가지고 있는 많은 맴버들 중에서 개발자가 디버깅시 꼭 필요한 맴버들만 선별하여 보여주거나 디버깅시 필요한 값들로 가공한 임의의 정보를 텍스트 시각화로 보여줄 수 있다.

 

 

 

<리스트 13> Item 노드를 통해 보여주고 싶은 내용을 만들 수 있다

 

// 타입 정의

 

struct Rectangle {

 

int height;

 

int width;

 

};

 

 

 

<!-- Natvis의 Rectangle 텍스트 시각화 지정 -->

 

<Type Name="Rectangle">

 

<DisplayString>A rectangle with height = {height} and width = {width}</DisplayString>

 

<Expand>

 

<Item Name="height">height</Item>

 

<Item Name="width">width</Item>

 

<Item Name="area">height * width</Item>

 

</Expand>

 

</Type>

 

 

 

Expand 노드를 통해 자식 항목들을 보여 지게끔 Natvis 파일을 구성하면 비주얼 스튜디오의 디버거가 보여주는 클래스 타입의 기본적인 텍스트 시각화는 [Raw View] 항목을 Expand 노드의 마지막 항목으로 자동으로 추가 시킨다.

 

 

 

<그림 1> Item 노드를 통해 디버깅시 보여주는 텍스트 시각화 항목을 선정한 모습의 예

 

 

 

배열 형태의 타입을 텍스트 시각화로 표현

 

만일 텍스트 시각화를 하는 클래스 타입이 배열 형태의 자료형을 가지고 있다면 ArrayItems 노드를 이용해 쉽게 표현해 줄 수 있다. 개발자들에게 친숙한 STL 벡터를 Natvis 문법으로 구성해 살펴보면 ArrayItems 노드의 기능을 쉽게 이해할 수 있다.

 

 

 

<리스트 14> STL 벡터의 Natvis 구현의 예

 

<Type Name="std::vector&lt;*&gt;">

 

<DisplayString>{{size = {_Mylast - _Myfirst}}}</DisplayString>

 

<Expand>

 

<Item Name="[size]">_Mylast - _Myfirst</Item>

 

<Item Name="[capacity]">(_Myend - _Myfirst)</Item>

 

<ArrayItems>

 

<Size>_Mylast - _Myfirst</Size>

 

<ValuePointer>_Myfirst</ValuePointer>

 

<LowerBound>0</LowerBound>

 

</ArrayItems>

 

</Expand>

 

</Type>

 

 

 

ArrayItems 항목에는 Size와 ValuePointer 이 두 개의 항목을 반드시 기술해야 하는데, ValuePointer가 가리키고 있는 항목을 Size 만큼 자동으로 텍스트 시각화로 표기해 준다 LowerBound 항목은 텍스트 시각화가 표기하는 첫 번째 항목의 순번을 지정할 수 있는데, 별도로 항목을 기술하여 지정하지 않을 경우 0번이 기본값으로 지정된다.

 

 

<그림 2> 텍스트 시각화가 적용된 STL 벡터의 디버깅 화면

 

 

 

ArrayItems의 Rank를 이용하면 텍스트 시각화 영역에 다차원 배열을 표현해 줄 수 있다. Rank 노드를 이용해 차원을 지정했다면 텍스트 시각화는 Size를 이용해 다차원에 특화된 방법으로 크기를 판단한다. 만일 각 차원의 크기가 같다면 상수를 대입할 수 있겠지만 차원의 크기가 각각 다르다면 $i 라는 지정된 파라미터를 사용하여 코드에 사용된 특정 변수를 참조할 수 있다. $i는 지정된 차원만큼 순회하면서 크기를 판단한다. 예를 들어 Rank에 2를 명시해 2차원의 배열 형태의 텍스트 시각화가 이루어진다면 $i는 0과 1로 차례로 대입되면서 0차원의 크기와 1차원의 크기를 차례로 받아들인다.

 

 

 

<리스트 15> ArrayItems의 Direction과 Rank를 이용한 다차원 표현

 

// 타입 정의

 

class vector2 : public std::vector<int> {

 

public: vector2() {ranks[0] = 3;ranks[1] = 4;}

 

private: int ranks[2];

 

};

 

<!-- Natvis의 Rectangle 텍스트 시각화 지정 -->

 

<Type Name="vector2">

 

<DisplayString>size = {_Mylast - _Myfirst}</DisplayString>

 

<Expand>

 

<ArrayItems>

 

<Direction>Forward</Direction>

 

<Rank>2</Rank>

 

<Size>ranks[$i]</Size>

 

<ValuePointer>_Myfirst</ValuePointer>

 

</ArrayItems>

 

</Expand>

 

</Type>

 

 

 

Rank에 2가 지정되어 2차원을 표기하고자 하고, 클래스에 ranks 맴버에는 0차원의 크기 3과 1차원의 크기 4가 기술되어 있다. 이를 참고하여 텍스트 시각화가 이루어 진다면 Rank가 2이므로 [x, y] 형태로 항목들을 표현하게 되고, 1차원의 크기 만큼 4개의 항목이 0차원의 크기만큼 3번 반복되어 지정된다. 따라서 Rank가 2, 사이즈가 각각 3, 4로 지정된 포인터를 표현한 텍스트 시각화는 [x, y] 형태로 총 12개의 크기가 부여되어 표기 된다.

 

 

 

<화면 19> Forward가 지정된 [x, y] 형태로 총 12개의 크기가 부여된 텍스트 시각화의 예

 

 

 

여기에 개발자의 취향이나 프로젝트 성격에 맞게 인덱싱 하는 방법을 변경할 수 있는데, Direction 노드를 통하여 Forward가 지정된다면 순번이 뒷자리에서 앞자리 순으로 증가되는 형태가, Backword가 지정된다면 앞자리가 증가되고 뒷자리가 증가되는 형태로 정해진다.

 

 

 

<리스트 16> IndexListItems를 이용하면 $i 파라미터를 이용하여 표기 항목을 선택 할 수 있다.

 

<Expand>

 

<Item Name="[size]">_M_vector._M_index</Item>

 

<IndexListItems>

 

<Size>_M_vector._M_index</Size>

 

<ValueNode>*(_M_vector._M_array[$i])</ValueNode>

 

</IndexListItems>

 

</Expand>

 

 

 

만일 ValueNode에 지정된 포인터의 값을 순서대로 출력하는 것이 아닌, 특정 위치의 노드만 표현하고 싶을 때는 IndexListItems 노드를 사용한다. ValueNode에 $i 파라미터를 이용하여 특정 위치만을 찾아 내고 있음을 주목한다.

 

 

 

링크드 리스트나 트리 형태의 타입을 표현

 

클래스 타입 안쪽의 데이터 컨테이너가 링크드 리스트 형태의 자료구조일 경우 링크드 리스트 특성상 포인터의 포인터를 찾아 들어가는 형태로 텍스트 시각화가 이루어 질 것이다. 이를 LinkedListItems 노드를 사용하여 배열 형태로 보기 좋게 만들어 줄 수 있다.

 

 

 

<리스트 17> 맴버의 자료구조 형태가 링크드 리스트일 경우의 예

 

template<typename T>

 

class CLinkedList {

 

// ...

 

struct CNode {

 

T Value;

 

CNode* Next;

 

};

 

size_t Count;

 

CNode* Head;

 

};

 

 

 

Head는 CNode를 링크드 리스트 형태의 노드로 관리하게 되는데, 비주얼 스튜디오는 기본적으로 Head부터 물려 있는 링크드 리스트의 포인터를 트리 구조로 관리하는 형식으로 텍스트 시각화를 만들어 주어 디버깅시 특정 노드를 찾는데 어려움을 겪을 수 있다.

 

 

<화면 20> 링크드 리스트 형태의 텍스트 시각화의 예

 

 

 

이때 LinkedListItems 노드를 이용해 NextPointer에 다음 노드를 가리키는 변수를 기술하면 마치 배열 형태의 텍스트 시각화 표현처럼 디버깅시 이해하기 쉽도록 만들 수 있다.

 

 

 

<리스트 18> LinkedListItems를 이용한 텍스트 시각화 문법의 예

 

<Type Name="CLinkedList&lt;*&gt;">

 

<DisplayString>{{Count = {Count}}}</DisplayString>

 

<Expand>

 

<Item Name="[Count]">Count</Item>

 

<LinkedListItems>

 

<Size>Count</Size>

 

<HeadPointer>Head</HeadPointer>

 

<NextPointer>Next</NextPointer>

 

<ValueNode>Value</ValueNode>

 

</LinkedListItems>

 

</Expand>

 

</Type>

 

 

 

링크드 리스트 특성을 Natvis 파일에 기술해 주면 비주얼 스튜디오가 자동으로 시각화 시켜 주는데, 첫 노드를 가리키고 있는 되는 부분을 HeadPointer로, 다음 노드를 가리키고 있는 포인터를 NextPointer로, 표현할 값을 ValueNode로 기술한다.

 

 

 

<화면 21> 링크드 리스트가 보기 좋게 표현된 텍스트 시각화의 모습

 

 

 

트리 구조의 자료형은 디자인하고 설계할 때만 트리 형태의 구조 모습이 중요하지만 실제 자료형 디자인이 끝난 후 코드로써 디버깅시에는 이를 배열 형태로 묶는 모습이 보기 편리 하다.

 

 

 

<리스트 19> std::map을 Natvis 문법으로 구성한 예

 

<Type Name="std::map&lt;*&gt;">

 

<DisplayString>{{size = {_Mysize}}}</DisplayString>

 

<Expand>

 

<Item Name="[size]">_Mysize</Item>

 

<Item Name="[comp]">comp</Item>

 

<TreeItems>

 

<Size>_Mysize</Size>

 

<HeadPointer>_Myhead-&gt;_Parent</HeadPointer>

 

<LeftPointer>_Left</LeftPointer>

 

<RightPointer>_Right</RightPointer>

 

<ValueNode Condition="!((bool)_Isnil)">_Myval</ValueNode>

 

</TreeItems>

 

</Expand>

 

</Type>

 

 

 

TreeItems을 이용하면 트리 타입의 자료형을 배열과 같이 디버깅시 보기 편하도록 테스트 시각화 설정을 해 줄 수 있는데, 트리구조 특유의 포인터들을 LeftPointer와 RightPointer로만 지정해 놓으면 나머지는 비주얼 스튜디오가 알아서 시각화를 표현해 준다.

 

 

 

<Expand> 노드에 특정 아이템 추가와 삭제

 

비주얼 스튜디오의 텍스트 시각화는 포인터 변수일 경우 해당 포인터 변수가 가리키고 있는 객체를 트리 구조 형태로 표현해 준다. 따라서 텍스트 시각화를 통해 보고 싶은 값이 클래스나 구조체 타입에 포인터가 가리키고 있고, 해당 클래스는 포인터 변수를 통해 접근해야 하는 상황이라면 포인터의 포인터를 통하여 접근하는 것과 같은 형태로 텍스트 시각화가 이루어 지게 되고, 이때 만들어 지는 트리 구조 형태는 번거로운 한번의 클릭을 더 요구하게 된다.

 

ExpandedItem 노드는 특정 항목을 생략하고 하위 항목들을 상위 항목으로 끌어 올려 텍스트 시각화를 만들어 준다.

 

 

 

<화면 22> 특정 항목을 생략하는 ExpandedItem을 사용한 텍스트 시각화의 예



 

 

이와 반대로 Synthetic 노드를 이용하면 가상의 노드를 만들어 줄 수 있다.

 

 

 

<리스트 20> ExpandedItem와 Synthetic을 사용하여 특정 항목을 삭제하고 추가하는 예

 

<Type Name="std::auto_ptr&lt;*&gt;">

 

<DisplayString>auto_ptr {*_Myptr}</DisplayString>

 

<Expand>

 

<ExpandedItem>_Myptr</ExpandedItem>

 

<Synthetic Name="AutoPointer">

 

<DisplayString>This is Auto Pointer</DisplayString>

 

</Synthetic>

 

</Expand>

 

</Type>

 

 

 

HResult의 특정 값에 대응되는 임의의 설명 추가

 

많은 수의 윈도API가 HRESULT를 반환값으로 사용한다. LRESULT가 4바이트인데 반해 HRESULT는 32비트형으로 다양한 메세지를 담아 낼 수가 있기 때문이다. HRESULT는 상위 16바이트는 오류코드, 하위 16바이트는 정보를 담고 있으며, 미리 정의된 HRESULT를 피해 임의의 HRESULT를 반환값으로 활용하는 애플리케이션의 API도 많다.

 

 

 

<리스트 21> HResult 노드로 특정 HRESULT 값과 대응되는 설명을 추가할 수 있다

 

<HResult Name="DXGI_ERROR_DEVICE_HUNG">

 

<HRValue>0x887A0006</HRValue>

 

<HRDescription>The application's device failed due to badly formed commands sent by the application. This is an design-time issue that should be investigated and fixed.</HRDescription>

 

</HResult>

 

 

 

마이크로소프트에서는 HRESULT를 API의 리턴 값으로 활용하는 방법을 권장하고 있으며, 미리 정의된 HRESULT 값을 피해 프로젝트 상황에 맞도록 추가적인 리턴 값을 정의해 활용할 수 있다. HResult 노드를 이용하면 특정 HRESULT 값에 설명을 추가 할 수 있으며, 디버깅시 이를 텍스트 시각화를 통해 나타낼 수 있다.

 

 

 

<UIVisualizer> 노드

 

텍스트 시각화 영역에는 그래픽 시각화 도우미(Graphical Visualizer Plugin)를 통하여 디버깅 값에 대한 별도의 모듈을 사용할 수 있다. 그래픽 시각화 도우미는 일반적으로 텍스트 시각화 영역 밖의 별도의 윈도우를 통해서 표현되므로 더욱 화려하고 직관적인 정보 전달이 가능하다.

 

 

<화면 23> 그래픽 시각화 도우미의 예

 

 

 

그래픽 시각화 도우미를 만드는 방법은 VSPackages를 이용하여 별도의 비주얼 스튜디오 플러그인을 만들어야 한다. 플러그인이 만들어 졌다면 플러그인이 노출하고 있는 GUID를 통하여 Natvis 파일의 UIVisualizer의 ServiceId에 기술하여 연동시켜 준다.

 

 

 

<리스트 22> UIVisualizer의 ServiceId를 통해 VSPackages로 작성된 그래픽 시각화 도우미를 연동 시킨다

 

<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1"

 

MenuName="Add to Image Watch"/>

 

<Type Name="My8BitRGBImage">

 

<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />

 

</Type>

 

 

 

EEAddIn을 이용한 사용자 정의 텍스트 시각화

 

EEAddIn은 텍스트 시각화 항목을 가장 강력하게 커스터마이징 할 수 있는 방법이다. 비주얼 스튜디오의 디버거는 디버깅 시 텍스트 시각화가 필요할 경우 Natvis 파일을 참고 하는데, 사용자 정의 텍스트 시각화 Natvis 파일에 EEAddIn을 참고 하도록 DLL 파일과 노출하고 있는 함수 명을 기술 할 수 있다. 이 기능을 사용하면 일반적인 조사식의 항목의 Natvis 프레임워크가 제공해 주는 맴버를 조회하고 연산값을 출력하는 정도에 그치는 제한적인 XML 문법에서 벗어나 메모리를 직접 엑세스 하거나 복잡한 소스 코드와 연계하여 계산된 값을 만들어 텍스트 시각화 영역에 표기해 주거나 다른 프로그램과 연계하여 복잡한 형태의 자료 시각화 표기를 할 수 있게 해 준다.

 

 

 

<리스트 23> 비주얼 스튜디오에서 호출하는 EEAddIn의 기본적인 함수 형식

 

typedef HRESULT(WINAPI *CUSTOMVIEWER)(DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved);

 

 

 

비주얼 스튜디오의 디버거는 EEAddIn DLL에서 노출하는 특정한 함수 형식의 함수를 호출할 수 있으며 함수 형식은 CUSTOMVIEWER 함수포인터를 참고 한다.

 

 

 

<리스트 24> WIN32_FIND_DATA의 사용자 정의 텍스트 시각화를 위한 EEAddIn의 DLL 코드의 예

 

typedef struct tagDEBUGHELPER

 

{

 

    DWORD dwVersion;

 

    HRESULT(WINAPI *ReadDebuggeeMemory)

 

        (struct tagDEBUGHELPER *pThis, DWORD dwAddr

 

        , DWORD nWant, VOID* pWhere, DWORD *nGot);

 

    DWORDLONG(WINAPI *GetRealAddress)(struct tagDEBUGHELPER *pThis);

 

    HRESULT(WINAPI *ReadDebuggeeMemoryEx)

 

        (struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr

 

        , DWORD nWant, VOID* pWhere, DWORD *nGot);

 

    int (WINAPI *GetProcessorType)(struct tagDEBUGHELPER *pThis);

 

} DEBUGHELPER;

 

extern "C" __declspec(dllexport)

 

HRESULT OS_WIN32_FIND_DATA(DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings

 

, char *pResult, size_t max, DWORD reserved)

 

{

 

    WIN32_FIND_DATA findData = { 0 };

 

    DWORD bytesRead = 0;

 

    HRESULT hr = S_FALSE;

 

    hr = pHelper->ReadDebuggeeMemoryEx(pHelper, dwAddress

 

        , sizeof(WIN32_FIND_DATA), &findData, &bytesRead);

 

    if (FAILED(hr)) {

 

        return hr;

 

    }

 

    if (findData.dwFileAttributes > FILE_ATTRIBUTE_NO_SCRUB_DATA) {

 

        return S_FALSE;

 

    }

 

    TCHAR * buffer = new TCHAR[max];

 

    if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {

 

        StringCbPrintf(buffer, max, TEXT("%s (디렉토리) ")

 

            , findData.cFileName);

 

    }

 

    else {

 

        LARGE_INTEGER size;

 

        size.HighPart = findData.nFileSizeHigh;

 

        size.LowPart = findData.nFileSizeLow;

 

        double dblSize = (double)size.QuadPart;

 

        if (size.QuadPart > 1024 * 1024 * 1024) {

 

            StringCbPrintf(buffer, max, TEXT("%s (%.2lf GB) ")

 

                , findData.cFileName, dblSize / (1024 * 1024 * 1024));

 

        }

 

        else if (size.QuadPart > 1024 * 1024) {

 

            StringCbPrintf(buffer, max, TEXT("%s (%.2lf MB) ")

 

                , findData.cFileName, dblSize / (1024 * 1024));

 

        }

 

        else if (size.QuadPart > 1024) {

 

            StringCbPrintf(buffer, max, TEXT("%s (%.2lf KB) ")

 

                , findData.cFileName, dblSize / 1024);

 

        }

 

        else {

 

            StringCbPrintf(buffer, max, TEXT("%s (%I64u B) ")

 

                , findData.cFileName, size.QuadPart);

 

        }

 

    }

 

    if (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {

 

        StringCbCat(buffer, max, TEXT("|읽기전용"));

 

    }

 

    // ... 중략

 

    if (findData.dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL) {

 

        StringCbCat(buffer, max, TEXT("|가상 "));

 

    }

 

    USES_CONVERSION;

 

    StringCbPrintfA(pResult, max, "%s", T2A(buffer));

 

    delete[] buffer;

 

    return S_OK;

 

}

 

 

 

디버거가 호출하는 콜백 함수의 첫번째 인자는 비주얼 스튜디오가 전달하는 텍스트 시각화 하고자 하는 객체의 주소다. 이를 ReadDebuggeeMemory 함수를 이용해 콜백 함수의 컨텍스트로 가져와서 가공하고자 하는 텍스트 시각화 형태로 코딩한다. 두번째 인자는 DEBUGHELPER라는 함수 포인터들의 구조체 값이 넘어 오는데, 디버거와 디버기간의 별도의 컨텍스트 설정이나 연동 없이 두번째 인자로 넘겨진 함수 포인터들의 함수를 사용할 수 있다.

 

pResult와 max 인자는 각각 텍스트 시각화에 표현할 문자열 결과 값과 결과 값 버퍼에 할당된 길이를 의미 하는데 비주얼 스튜디오 2013 현재 아직 멀티 바이트만 지원하도록 되어 있다.

 

 

 

<리스트25> _WIN32_FIND_DATAW 구조체에 대하여 EEAddIn의 시각화를 이용하는 Natvis 파일 정의의 예

 

<?xml version="1.0" encoding="utf-8"?>

 

<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

<Type Name="_WIN32_FIND_DATAW">

 

<DisplayString LegacyAddin="NatvisAddIn.dll" Export="OS_WIN32_FIND_DATA"></DisplayString>

 

</Type>

 

</AutoVisualizer>

 

 

 

만들어진 DLL 파일을 통해 Natvis 파일의 LegacyAddin과 Export를 설정하면 텍스트 시각화 영역에 표기되는 정보를 가공할 때 메모리를 직접 엑세스하여 프로그래밍을 통해 더욱 풍부한 표현이 가능해 지도록 할 수 있다.

 

 

 

<화면 24> EEAddIn을 이용한 사용자 정의 텍스트 시각화의 예

 

 

 

알수록, 쓸수록 유용한 natjmc, natstepfilter 파일

 

Natvis 파일의 기본 폴더인 프로그램 경로와 사용자 경로의 Visualizers 두 폴더를 보면 확장자가 natvis인 파일 이외에 natjmc, natstepfilter 파일이 있는것을 확인할 수 있다. natjmc는 비주얼 스튜디오 2013에서 추가된 내 코드만 디버깅(Just my code) 기능과 관련된 설정 설정파일인데, 내 코드만 디버깅 기능은 디버깅시 라이브러리 코드 혹은 외부 모듈 코드가 콜스택이나 시각화 도구에 포함되어 복잡해지는 상황을 개선하기 위해 외부 모듈이나 라이브러리는 포함되지 않도록 할 수 있다.

 

 

<화면 25> 내 코드만 디버깅 기능을 활성화 했을 때의 콜스택 변화

 

이때 콜스택등에 표기되는 모듈과 표기되지 않는 모듈의 설정이 default.natjmc에 포함되어 있다. natjmc 파일 또한 사용자가 임의로 편집할 수 있으며 편집된 결과는 디버깅시 바로 확인할 수 있다.

 

디버깅시 StepInto를 사용할 때 가장 불편한 점 중 하나는 내가 원하지 않는 함수의 정의부로 진입하는 것이다. 일례로 비주얼 스튜디오 2008때 특정 클래스의 인스턴스를 만드는 코드에서 StepInto를 실행했다면 개발자의 의도는 클래스의 생성자 정의부로 진입하기 위함이였을 것이다.

 

MyClass * a = new MyClass();

 

그러나 디버거는 new의 정의부로 커서를 옮겨 주는데, 이와 같이 내가 원하지 않는 함수 정의부로 옮겨지는 StepInto의 단점을 보완해 줄 수 있는 기능이 스탭필터(Step filter)기능이다.

 

 

 

<화면 26> StepInto시 원하지 않는 함수 정의부로 진입하는 예

 

 

 

스탭 필터 기능은 비주얼 스튜디오 2003 이전 사용자는 autoexp.dat에 Execute Control 섹션을 이용하였고 그 외의 비주얼 스튜디오는 특정 레지스트리를 통하여 스탭 필터 기능을 설정할 수 있었다.

 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\NativeDE\StepOver

 

KEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\NativeDE\StepOver

 

natstepfilter 확장자 파일은 레지스트리에 기술했던 스탭 필터 설정을 별도의 xml파일로 옮긴 것으로, 레지스트리에 비해 설정하기 간편하고, 파일별 분류를 할 수 있으며 XML의 장점을 이용해 기능을 확장시킨 스탭 필터 기능이다.

 

 

 

Reference

 

1. Create custom views of native objects in the debugger

 

http://msdn.microsoft.com/en-us/library/jj620914.aspx

 

2. Writing debugger type visualizers for C++ using .natvis files

 

http://code.msdn.microsoft.com/Writing-type-visualizers-2eae77a2

 

3. Debugger Type Visualizers for C++ in Visual Studio 2012

 

http://blogs.msdn.com/b/vcblog/archive/2012/07/12/10329460.aspx

 

 

코멘트쓰기
  좋음   놀람   궁금   화남   슬픔   최고   침묵   시무룩   부끄럼   난감
* 코멘트는 500자 이내(띄어쓰기 포함)로 적어주세요.
목록 보기   지금 보고 계시는 글을 회원님의 my Mblog >> 스크랩에 넣어두고 다음에 바로 보실 수 있습니다.  
회사소개  |   개인정보취급방침  |  제휴문의  |   광고문의  |   E-Mail 무단수집거부  |   고객지원  |   이용안내  |   세금계산서
사업자등록번호 안내: 220-81-90008 / 통신판매업신고번호 제 2017-서울구로-0055호 / 대표: 홍영준, 서민호
08390, 서울시 구로구 디지털로32길 30, 1211호 / TEL. 02_6719_6200 / FAX. 02-6499-1910
Copyright ⓒ (주) 데브피아. All rights reserved.