C# 클래스 할당시 메모리 구성 디버깅

디스어셈블링 및 실제 메모리를 디버깅하면서 데이터와 객체를 할당시 어떤 구조로 메모리에 올라가는지와

C#에서 클래스를 할당하면, 힙이 어떻게 동작하는지를 보자

 

쉽게 보기 위해서 예제는 x86(32bit)로 컴파일되었고 이를 기준으로 설명합니다.

 

 

예제로 사용할 모습은 이와 같다.

 

아무 클래스나 하나 만들고 그 안에 문자열 하나와 int 하나를 선언, 구조체 역시 메모리를 살펴보기 쉽게 int형 두개로 선언했다.

 

먼저 Case1 메소드를 디버깅해보자

 

Watch창에서 살펴보면 현재 testObject의 상태를 알 수 있고, '&testObject'로 testObject의 실제 메모리 주소를 볼 수 있다.

 

C# 문법에서는 직접적인 포인터를 사용하려면 unsafe 옵션을 이용해서 사용 할수 있으나, 이는 그리 권장하는 바가 아니며 간접적으로 포인터를 사용할수 있는데 이는 IntPtr을 이용하는 방법이다.

 

IntPtr의 경우엔 이후에 더 자세히 다룰 예정이다.

 

 

해당 메모리 주소로 가서 직접 살펴보면, 0x02eeb6b8 이라는 메모리 주소을 담고 있다.

이것으로 testObject 자체는 특정 주소를 담고 있는 포인터라는 것을 알 수 있다.

 

포인터가 가르키는 주소로 가서 살표보면 다음과 같다.

 

하일라이트된 두 영역이 클래스의 맴버변수들을 담고 있는 위치이다.

 

SomethingClass는 처음 1개의 string 변수와 두번째 int 값을 하나 가지고 있는데, 처음 앞 4바이트가 string이고 두번째 0값으로만 채워져 있는 4바이트 영역이 int값의 메모리 공간이다.

 

실제로 이렇게 맴버변수 int fisrt의 값을 1212라고 바꾸면, 메모리상에서 어떤 변화가 생기는지 보자

 

두번째 하이라이트되어 있던 공간의 값이 0000 04bc의 값으로 바뀌었고, 10진수 1212의 16진수 값은 4bc이므로 정확하게 찾은 메모리 위치가 맞는 것을 확인 할 수 있다.

 

그렇다면 첫번째 하이라이트였던 string 영역의 값을 분석해보면, 문자열의 저장은 해당 문자열이 존재하는 영역의 주소값을 가지고 있다.

 

즉 첫번재 하이라이트 4바이트 값은 포인터이다.

 

해당 포인터를 따라가면 이와 같은데, 하이라이트 해둔 부분이 문자열의 총 길이를 나타낸다.

그리고 그 뒤에가 문자열들이 들어 있다.

 

문자열들을 보기 편하게 2바이트 소팅을 해서보면 처음 값이 4e로 이는 아스키코드로 대문자 'N'에 해당한다 그 뒤로 i c e가 순차적으로 들어 있다.

 

간단하게 객체 하나가 할당될때 어떤 일들이 메모리상에서 일어나는지 살펴보았다.

 

다음 포스팅부턴 IntPtr에 대해서 자세하게 쓸려고 한다.

원래 IntPtr을 설명하기 위해서, 이 글을 썼다.

 

가비지 컬렉터로부터 가변 힙을 할당 받아, 이를 포인터에 넣어 사용하거나 또는 이미 생성한 객체를 포인터로 받아 조작하는 것들을 앞으로 쓸 예정이다.

칼루
나만의 강의 2017. 6. 24. 23:47
,

C# 리플렉션으로 Struct(구조체 / ValueType) 수정(Set) 하기

StackOverflow에 매우 좋은 답변으로 달려 있는 내용인데, C#에서 Reflection을 이용해서 구조체(Struct)의 맴버 변수를 Set(Get)하려고 하면 제대로 작동되지 않는다.

 

접근할 수 없는 이유는 Struct는 ValueType으로 Stack에 할당되어 있기에 Reflection 연산으로 접근 할 수 없기 때문이다.

 

이를 의도된 Boxing을 통해서 Heap영역으로 옮긴 후 Reflection을 사용하는 방법이다.

물론 이때 다시 맴버 변수 접근으로 값을 가져오려면 Unboxing을 해야한다.

 

생각보다 퍼포먼스 손해가 많아 보이니, 꼭 필요한 경우나 어쩔수 없는 최후의(?) 상황에서 사용을 하자

 

이해를 돕기 위한 예시 코드는 아래와 같다

 

 

* 코드가 제대로 안나오면 F5(새로고침)해보세요

* 의사코드이므로 컴파일 안될 수 있습니다.

* Ref.https://stackoverflow.com/questions/6608368/why-doesnt-reflection-set-a-property-in-a-struct

칼루
나만의 강의 2017. 6. 19. 15:42
,

[ToolFramewrok] 게임엔진과 툴을 연결하는 구조 1

최근 백수라 집에서 놀다보니, 여행을 가고 싶지만 여러여건상 이것도 쉽진 않군요( 언제나 갈 예정임 ㅋ)

 

뭐 여하튼!!

 

집에서 쉬다보니  컨디션도 꽤 좋아졌고 해서 예전에 KGC2011에서 강연했던 주제를 제대로 정리하고자 하는 시간을 갖으려 합니다. ㅎㅎ 오늘이 그 첫번째 시간이군요. 이게 총 몇개로 이루어질지는 모르겠지만 해보는데까지 해봅시다. 먼저 앞으로의 계획을 대략적으로 정리하자면 아래와 같습니다.

 

- 엔진과 툴의 이종간 연동을 위한 구조 설계( DLL Interop을 이용 C/S모델로 설계해서 C++과 C#을 적절히 사용 )

- WPF(C#) 에서 Navtive DirectX Rendering 하기 (엔진의 Rendering 기능을 연결해서 WPF 컨트롤러에 붙이는 거 )

- 툴 자체를 Plug-In 구조로 설계해서 각각의 기능을 독립적으로 묶고 관리 하며 무한한 기능확장(?) 구조 설계

- TCP/IP를 이용, 원격지에 있는 툴 조종 및 행동 반영

 

아.... 그냥 막 생각하려니까 좀 이상하군요 ㅎㅎ 조금 더 부연 설명을 하자면 별것들 아닌데

첫번째는 KGC2011에서 강연 했던 주요 내용이구요.

두번째도 강연에서 잠깐 나왔던 건데, 그때 당시에는 자세히 다루지 않았죠. (간단함..hWndHost를 이용하는 거)

세번째는 언젠가 설계했던 건데 당장은 잘 기억 안나므로 제대로 정리 할 수 있을까? 싶은 의문이 듬

네번째는 KGC 강연때 마지막에 "이러 이렇게 하면 이런게 된다!" 라고 말했던 걸 만들어서 정리해볼려고 함.

 

막상 이렇게 조금이라도 정리해보니 대단한 여정이 될꺼 같네요(?) 끝까지 갈 수 있을런지......ㅡ_ㅡ

 

먼저 왜 이러한 주제를 택했고 어디서 어떻게 시작이 되었는가를 잠깐 살펴보자면, 이거 한장으로 모든 설명이 끝날 것 같다.

 

 

 

현재에도 많은 게임 엔진을 C++로 만들고, 이 추세는 앞으로 꽤 지속 될꺼라 생각합니다. 다만 C++의 툴 생산성은 꽤나 좋지 못하다고 생각하는데, 그나마 대안이 될만한게 WPF이지 않을까 싶습니다. ㅎ

윈폼도 써보고 MFC도 써봤지만 (MFC는 오래전에 ㅎㅎ ) UI 구성부터 시작해서 제대로 View와 모델을 구분하지는 못한것 같고 가장 크게 다가오는 것은 UI  구성이 꽤나 어렵지요.

 

WPF는 기존 MVC나 MVP와는 좀 다른 MVVM 패턴이라는데 ( http://msdn.microsoft.com/ko-kr/magazine/dd419663.aspx / WPF의 MVVM 패턴 )

 

저도 MVVM이니 MVC 잘 모릅니다. ㅎㅎ 다만 핵심적인 것은 View 코드와 비즈니스로직 코드를 완전히 분리한다 라고 이해 하시면 될 것 같습니다. View코드라 하면 UI를 구성 짓는 코드이고, 비즈니스 코드는 실질적 기능 구현에 쓰이는 코드 입니다

 

자..자세한건 링크를... 저는 몇번 봐도 계속 까먹어서 ㅎㅎㅎ

 

Anyway 원래 주제로 돌아가서 C#으로 툴을 만드는 것 까지는 좋은데, 문젠 C++로 만들어져 있는 게임 엔진 기능을 쓰기가 참 애매합니다. 전통적인 방법 중 하나로 C++/CLI 가 있는데요. CLI 은 Common Language In.... 머시기인데요. C++과 C#의 "중간계" 정도로 보시면 됩니다. ㅎㅎ 물론 이 C++/CLI를 사용해서 연동을 해도 되구요 C/S 모델도 사용 할 수 있습니다. 다만 개인적으로 모호한 "중간계"를 사용하고 싶지 않네요. C++/CLI에서 가장 난해한 부분은 Native 메모리들을 Managed 영역으로 맞춰주는 작업인데 이게 햇갈리는 부분이 좀 있네요. 물론 인터페이스를 단순화 한다면 이러한 부분도 해결은 되겠죠.

 

또 개인적으로 C++/CLI은 거의 사용을 안해봐서 잘 모릅니다.

 

그래서 깔끔하게 순수 C Dll을 택했고, Dll Interop을 통해서 C#에서 함수들을 사용합니다.

 

간단하게 이 구조의 모습은 다음과 같습니다

 

 

밑에 화살표는 무시하고, 툴 <-> DLL( Engine DLL ) 요런 모습입니다.

 

 

이게 현재 Version 0.1ToolFramwork 솔루션 모습입니다. 이건 앞으로 연재를 해가면서 계속 발전 할겁니다. ㅎㅎ RenderingTool 프로젝트는 사실상 빈 깡통이구요. 각 설명을 하자면

 

- ClientLauncher : Client런처 입니다  exe이고 엔진을 lib 또는 dll로 링크 하고 있습니다. WinMain을 가지고 있고, 현재로썬 딱히 하는게 없어요. 엔진 초기화 해서 돌리는 게 전부 입니다. 실제로 게임 로직이 들어가진 않아요.

 

- FrameWork : 아주 옛날에 만들어 둔건데 ㅋㅋ 제 블로그 뒤지면 스샷도 나오죠(...) 이건 원래 두개로 쪼갤려고 했는데 (game 로직 / engine ) 너무 귀찮으니... 그냥 게임 엔진이라고 생각하시면 됩니다. ㅎㅎ 아 이게 언젠가 DirectX 11으로 간단한 렌더러로 바꿀 생각이에요. ㅋ 물론 언제 그런 날이 올진 모르겠군요. 일단 예제 샘플로 쓰기에 좋음

 

- ToolInterface : 실제로 위에 그림에서 "DLL" 기능이고 툴의 기능적 로직들( 게임 엔진을 사용해야 하는 기능들 )이 이곳에 모두 들어갑니다. 그리고 중요한 Dll 통신용 인터페이스들을 Export 하고 있죠. 물론 그외에 다른 함수는 일절 Export 하지 않습니다. 오직 통신에 쓰이는 인터페이스 몇개만 Export 합니다.

 

- RenderingTool : 앤 아직 깡통...

 

이렇게 보니까 매우 심플하죠? 아닌가요 ㅋ 실질적으로 빌드해서 나오는 것은 ToolInterface.dll / Framework.dll / RendringTool.exe 이 3개 입니다. ClientLauncher는 그냥 디버깅용(?)으로 넣어둔 거라 큰 의미는 없고 소스 배포 할 때에도 빼 버릴까 생각 중입니다. ㅎㅎ 괜히 혼란만 가중 시킬꺼 같아서...

 

이제 우리가 살펴봐야 할 것은 ToolInterface 부분이며, 가장 먼저 Export 하는 부분을 봅시당

 

ToolInterface의 파일 뷰

 

// DllExport.h

#pragma once

#define DLL_EXPORT __declspec(dllexport)

extern "C"
{
	// 툴 인터페이스 초기화
	DLL_EXPORT void* InitalizeWindow( ::HINSTANCE applicationInstance, ::HWND hWndParent, int screenWidth, int screenHeight, const char* pAssetPath );

	// 툴 인터페이스 해제
	DLL_EXPORT void Start();
	DLL_EXPORT void Stop();

	DLL_EXPORT bool Send( const char* pCommand, void* pData, unsigned int dataSize );
	DLL_EXPORT bool SendOutput( const char* pCommand, void* pData, unsigned int dataSize, void*& pOutputData, unsigned int& outputDataSize );
	
}

 

딱 정확히 5개의 함수만 익스포트 시킵니다. ㅎㅎ 저걸로 모든 툴의 기능을 다 만들 수 있고 나중에 더 재미난 기능들도 가능하죠. 그리고 이것들은 한번만 만들어두면 앞으로 거의 왠만해선 고칠 일이 없습니다.

여기서 짚고 넘어가야 할 부분은 Send 와 SendOutput 인데요. 사실 이 함수들은 ToolInterface.dll -> WPF(C#) 으로 데이터를 보내는 함수가 아닌, 반대로 WPF(C#) -> ToolInterface.dll 함수들 입니다.

 

이는 WPF 입장에서 봤을 때 Send이기 때문에 이렇게 이름 지은 거구요. ToolInterface.dll 입장에서 봤을 때는 Recv나 Get 정도의 의미입니당. 실제로 저 안에서 하는 일도 데이터를 받아와서 해당 Command로 넘겨주고 기능들을 처리 하는 일입니다.

 

아, 오늘은 여기까지 써야겠네요. 시간이 너무 늦어서 ㅎㅎ 나머진 내일...

칼루
나만의 강의 2014. 3. 26. 02:11
,
Powerd by Tistory, designed by criuce
rss