블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2738)
Unity3D (817)
Programming (475)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
05-05 09:19

define DEBUG_OUTPUT
//------------------------------------------------------------------------
// 2010/01/22 [19:56:32]
// jedikim72
// - 디버깅출력함수
//------------------------------------------------------------------------
#ifdef DEBUG_OUTPUT
 #define debugLog(...) { char acTemp[2048]; \
                                      char acTemp2[2048]; \
                                      NiSprintf(acTemp, 2048, __VA_ARGS__); \
                                      NiSprintf(acTemp2, 2048, "[L%d]:%s - %s", __LINE__, __FUNCTION__, acTemp ); \
                                      OutputDebugString( acTemp2 ); \
                                    }
#else
 #define debugLog(...) ((void)0)
#endif
//------------------------------------------------------------------------

 

빌딩모드와 상관없이 디버깅 메세지를 출력하는 메써드가 필요해서 부랴부랴 만든 매크로.

(기존 매크로는 사용하기 불편해서... - -a)

 

매크로 가변인자를 사용하여 가변인수를 받아 출력할 수 있다.

사용예) debugLog("%.2f, %.2f, %.2f\n", pos.x, pos.y, pos.z);

또한 VS IDE 출력창 뿐만 아니라, 실행파일만 독립적으로 실행해도 DebugView 유틸을 통해 디버깅 출력 메세지를 볼 수 있다는 장점이 있다.

 

 

이제까진 TraceWindow를 써서 출력했는데, 프로젝트에 매번 관련 모듈 삽입하는 것도 귀찮고 해서 아예 매크로로 빼버려 간단한 구문 삽입만으로 간편하게 작업을 완료할 수 있게 했다.

 

 

 

DebugView와 함께 구동시킨 작업중인 데모 스크린샷

그나저나,

DebugView 유틸은 첨부파일로~!



출처  : http://jedikim72.blog.me/10079110907

반응형
Posted by blueasa
, |
반응형
Posted by blueasa
, |

C++ 을 배운지 꽤 오래 되었지만, 아직도 코드 리뷰를 통해 다른 개발자들의 코드를 볼 때면 한숨이 나오는 경우가 많습니다.

이런 경우가 반복되다 보니, 예전에 몇 가지 코딩 규칙 비슷하게 만들어놓은 자료가 있어 포스팅해 봅니다.  자료가 만들다 말아 내용이 중구난방이네요..  앞으로도 계속 내용 보강해 나가겠습니다.

 

    포인트 핸들 및 윈도우 핸들 체크

-       함수로 전달된 포인터 인자나, new 로 생성한 포인터 등 모든 포인터는 사용하기 직전 포인터 검사를 수행

-       가급적 STL 컨테이너를 사용해서 아예 포인터를 사용하지 않는 것이 정신건강에 이롭다. (STL 자체적으로 메모리 생성 및 소멸)

-       메모리 LEAK 을 막기 위해 SMART POINT 와 같은 Wrapper 클래스를 만든다.

-       (윈도우 프로그램의 경우)윈도우 API 호출 직전 항상 윈도우 핸들이 정상인지 IsWindow 와 같은 함수로 검사한다.

 

    DRY(Don't Repeat Your Code)원칙

-       같은 코드를 여기 저기 중복해서 사용하지 말아야 한다. (☞ 실용주의 프로그래머 참고). 중복해서 사용할 경우 코드 량이 늘어날 뿐만 아니라 유지보수 cost 가 같이 늘어 나고, 추후 추가/개선/변경 등의 요구사항으로 인한 모듈 수정 시 여러 소스를 함께 수정해야 한다.

-       동일한 코드를 공용 루틴(함수)으로 개발 – 2~3줄 짜리 복잡한 if 문도 bool 를 리턴 하는 함수로 개발(Refactoring 참고)

 

    클래스간 종속성 최소화

-       대규모 프로젝트에서는 공통모듈이 변경되어도 이를 사용하는 모듈이 문제가 없도록 반드시 공통모듈은 인터페이스 기반(데이터가 없는, virtual 가상함수로만 구성된)으로 구현한다. (The C++ Programming Language 참고)

-       A 클래스 → B 클래스 → C 클래스를 사용할 경우 각 클래스는 자기가 직접 다루는 클래스(A B, B C) 만 알아야 한다. A 클래스는 C 클래스를 직/간접적으로 include 해서는 안되며 C 클래스의 변경으로 인해 A 클래스가 컴파일 되어서는 안 된다. A 클래스가 C 를 직접 접근할 경우가 있다면 B 클래스에 위임함수를 만들어 해결한다. (☞ 실용주의 프로그래머 참고)

-       B 클래스가 A 클래스로 어떤 데이터를 전달할 경우가 있다면(주로 이벤트 발생시 값을 전달하기 위해) A 클래스의 최소 인터페이스만을 B 클래스에 전달하여(COM 의 이벤트 소스 방식) 종속성을 최소화 한다. (Developer’s Workshop To COM and  ATL 3.0 참고)

 

    함수 인자 전달

-       함수의 인자는 최대 7개를 넘지 않아야 한다(Code Complete 2 참고)

-       클래스 인스턴스를 함수 인자로 넘겨서는 안 된다(특히 함수 인자로 CString 을 넘기는 경우가 많음. CString → const CString& CString*, const char* 등으로 수정한다. )

 

    오류조건을 먼저 검사하여 오류 체크로 인해 코드 가독성이 떨어지지 않도록 하자.

-       특정 오류조건이 발생하면 더 이상 함수 수행에 의미 없을 경우, 이러한 오류 조건들은 함수초입단계부터 먼저 확인해서 바로 리턴 한다

-       위와 같이 오류조건이 먼저 확인되면 프로그램 코드에서 if depth 가 줄어 들고 코드 읽기가 수월해 지는 장점이 있다.

Before

After

void CClass::OnTest()

{

 ...

 ...

 if (m_pWndMain)

 { 

  DoSomething1();

 }

 ...

 if (m_pWndMain)

 { 

  DoSomething2();

 }

 

}

void CClass::OnTest()

{

 if (m_pWndMain == NULL)

  return;

 ...

 ...

 DoSomething1();

 ...

 DoSomething2();

 

}

 

    긴 함수 쪼개기 (Refactoring 참고)

-       긴 함수는 작은 여러 개의 함수로 쪼갠다(Refactoring 책에 의하면 2-3줄 짜리 함수로까지 쪼개어서 코드 가독성을 높이도록 하고 있음)

-       작은 함수로 쪼개다 보면, 작은 함수를 재활용하게 될 수 있고

-       긴 함수보다 코드 읽기가 수월해 지며

-       결국 유지보수가 쉬워진다

 

    enum 값 선언 시 _START, _END 를 활용하자( CODE COMPLETE2 참고)

-       enum 으로 선언된 값들을 변수나 for loop 를 이용하여 반복 계산할 경우가 종종 있는데 이 경우 enum 시작과, 끝을 나타내는 값을 enum 선언에 포함하면 유용하게 사용할 수 있다.

-       추후 enum 에 값을 추가해야 할 경우 _END 값을 사용한 배열은 코드 변경없이 크기가 늘어남

-       For 루프 등에 _START, _END 을 사용할 수 있어 유지 보수에 유리

Before

After

enum WEEK_NAME

{

           SUNDAY                     = 0,

           MONDAY                    = 1,

           TUESDAY                    = 2,

           WEDNESDAY     = 3,

           THURSDAY       = 4,

           FRIDAY            = 5,

           SATERDAY        = 6,

};

 

int nWeeks[SATERDAY + 1];

}

enum WEEK_NAME

{

           WEEK_STAR       = 0,

           SUNDAY                     = 0,

           MONDAY                    = 1,

           TUESDAY                    = 2,

           WEDNESDAY     = 3,

           THURSDAY       = 4,

           FRIDAY            = 5,

           SATERDAY        = 6,

           WEEK_END        = 6,

};

 

int nWeeks[WEEK_END + 1];

 

 

    복잡한 제어문을 가진 루틴을 피하자  

-       If 문은 2 depth 이상 들어 갈 경우 refactoring 하라 (CODE COMPLETE2)

     대부분 코드를 제대로 이해하지 못해 복잡한 코드를 작성하고 있다.

     개발자 90% 2 depth 이상 들어가는 if 문장을 이해하지 못한다

-       IF~ELSE IF ~ELSE IF ~ …. ELSE 가 길게 이어질 경우 SWITCH 문으로 바꾼다

-       IF 문이 길게 늘어 질 경우 함수로 분리한다

-       C++ factory 개념으로 상속을 이용한 클래스를 만드는 방법도 활용


반응형
Posted by blueasa
, |

Visual C++ (.NET 기준)에서 디버깅 시 유용하게 사용할 수 있는 컴파일옵션들을 정리해 보았습니다.  하나하나 다 외우질 못해 프린트해서 그때그때 사용하고 있습니다. ^^

도움이 될 지 모르겠네요.

 

/EP /P C/C++ Preprocessor Generate Preprocessed File
(파일전처리)
전처리 코드에 대한 내용을 소스파일.i 파일로 볼 수 있다. 
/C C/C++ Preprocessor Keep Comments
(주석유지)
위를 설정하고 이것도 설정하면 선택하면 전처리코드 위의 주석도 유지하면서 보여 줄 것이다.
/X C/C++ Preprocessor Ignore Standard Include Path
(표준 포함경로무시)
표준 인클루드 경로 무시
/Zp C/C++ Code Generation Struct Member Alignment
(구조체 맴버 맞춤)
#pragma pack 지시자를 사용하는 것이 더 좋을 것 같다.
/Wp64 C/C++ General Detect 64-bit Portability Issues
(64비트 이식성 문제 검색)
나중 64비트 호완성을 돕기위해 설정을 해 두는 것이 좋다.
/RTC C/C++ Code Generation Smaller Type Check
(작은 형식 검사)
/RTCc : 값을 작은 데이터 형식으로 변환할 때 데이터손실이 없는 지 검사한다.
Basic Runtime Checks
(기본 런타임 검사)
/RTCu : 초기화되지 않은 변수참조를 추적한다.
/RTCs : 모든 지역변수들을 0xCC로 초기화 하여 훌륭한 스택 프레임 검사가 로컬변수 언더런과 오버런을 감지하고 스택 충돌에 대해서 스택 포인터를 검증하도록 한다.
/RTCsu : 위의 두개 모두 포함
/GS C/C++ Code Generation Buffer Security Check
(버퍼 보안 검사)
바이러스 작성자들의 가장 일반적인 공격인 리턴주소를 악성코드로 리다이렉트 할 수 있도록 하는 버퍼오버런공격을 막아주는 것이다.
/O(num) C/C++ Optimization Optimization
(최적화)
/O1(크기최소화), /O2(속도최대화:디폴트)
마이크로소프트 제품은 모두 /O1으로 컴파일..이것으로 컴파일 할 때 같이 SWS(Smooth Working Set)유틸리티를 사용해서 공통적으로 사용하는 함수들을 앞으로 배치되도록 해야 한다.(속도도 높이기 위해)
/GL Main General Whole Program Optimization
(전체프로그램최적화)
설정하면 링커스위치에 /LTCG(밑에 참조 바람)를 설정
/showincludes C/C++ Advanced Show Includes
(포함 파일 나열)
컴파일 할 때 포함되는 include 파일들이 어떤 위치의 것인지를 Output 화면으로 모두 보여준다.
/MAP Linker Debugging Generate Map File
(맵파일 생성)
맵파일을 생성하는 스위치로 유용한 정보를 얻기위해서는 모두 사용해야 한다.
/MAP:LINES Linker Debugging Map Lines
(맵파일에 줄 번호 정보 포함
/MAP:EXPORTS Linker Debugging Map Exports
(맴파일에 익스포트 함수정보 포함)
/NODEFAULTLIB Linker Input Ignore All Default Libraries
(라이브러리 무시)
많은 시스템 헤더파일들이 어떤 파일이 링크되는지 지정하기 위해 #pragma comment (lib#,립이름)을 포함하는데 이를 모두 무시하도록 지시한다.
/OPT:NOWIN98 Linker Optimization Optimize for Windows98
(Windows98에 맞게 최적화)
98,ME를 지원하지 않는 다면 설정하는 것이 좋다. 응용프로그램의 크기를 줄일 수 있다.
/ORDER Linker Optimization Function Order
(함수에 순서 지정)
SWS(Smooth Working Set) 을 실행한 후 함수의 순서를 포함하고 있는 파일을 지정할 수 있다.
/VERBOSE Linker General Show Process
(진행 메시지 표시)
링크되는 라이브러리의 모든 진행 메세지를 표시
/VERBOSE:LIB Linker General Show Process
(진행 메시지 표시)
검색된 라이브러리만 나타내는 진행 메세지를 표시
/LTCG Main General Whole Program Optimization
(전체프로그램최적화)
/GL스위치와 짝을 이룬다. 링크시간코드를 생성.
/RELEASE Linker Advanced Set Checksum
(체크섬 설정)
별로 사용되지 않지만 WinDBG를 사용할 때 책섬을 조사할 때 필요하기 때문에 미리 넣어 두는 게 좋다.
/PDBSTRIPPED Linker Debugging Strip Private Symbols
(전용 기호 제거된 심볼)
스택을 탐색하고 해독하기 위해서 필요한 특별한 Frame Pointer Omission(FPO) 데이터를 PDB파일에 포함시키기 위해 설정한다. 각종 변수나 소스 줄정보는 빼고 필요한 FPO데이터와 공용함수들만 남겨놓는다(stripped PDB파일). 이것과 함께 일반 PDB 파일을 같이 생성할 수 있다. 나중에 Dr.Watson 등에서 PDB파일을 로드할 수 있기 위해서는 파일명을 어플리케이션 명과 같게 해줄 필요가 있다.

반응형
Posted by blueasa
, |

//////////////////////////////////////////////////////////

//VC++의 출력창을 이용한 디버깅 수단1

#include <Windows.h>

//void main()...

OutputDebugString(msg);                                         //VC출력창에 문자가 출력 된다.

 

//////////////////////////////////////////////////////////

//별도의 Console창을 이용한 디버깅 수단2

//void main()...

AllocConsole();                                                             // 콘솔창 만든다.

char strBuffer[256]="";

DWORD(또는 size_t) dwWrite;
HANDLE m_hOut = GetStdHandle(STD_OUTPUT_HANDLE);                            // 콘솔창의 핸들 가져옴.
WriteConsole(m_hOut, strBuffer, strlen(strBuffer), &dwWrite, NULL);// 콘솔창에 쓸때
FreeConsole();                                                              // 콘솔창을 없앰.

 

//////////////////////////////////////////////////////////

//별도의 Console창을 이용한 디버깅 수단3

#include <stdio.h>                                                        //또는 #include <iostream>

//void main()...

AllocConsole();

freopen("CONOUT$", "wt", stdout );                       //이것을 사용하면 출력함수 사용이 가능하다

printf("");                                                                        //또는 std::cout << "";

FreeConsole(); 

 

 //////////////////////////////////////////////////////////

 //별도의 Console창을 이용한 디버깅 수단4

//단 WinMainCRTStartup은 컴파일이 되기전에는 생성되지 않으므로 컴파일 후 삽입해주자
 #pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")

#include <stdio.h>                                                          //또는 #include <iostream>

//void main()...

printf("");                                                                         //또는 std::cout << "";

 
쉽고 간단한 방법이라면 1번 방법과 3번 방법이 젤 좋아 보인다.
그중에서 3번이라면 C++ 기초문법 중 하나처럼 별도의 랩핑이나 (기능)클래스 생성 없이
출력창에 간단히 로그를 남길 수 있어 편할 듯 하다.
반응형
Posted by blueasa
, |

유용한 전처리기

Tip & Tech / 2011. 1. 7. 03:13

전처리기.

프리컴파일러(Free가 아니다 : PreCompiler)

 

프로그래머들이 자주 쓰는 전처리기 기능 3가지만 소개하고자 한다.

이것은 쓰라고 강요하기보단 다른 사람(그사람이 현업에서 근무할 가능이 높으므로) 코드를 이해하는데

조금이나마 도움을 주기 때문이다.

 

#pragma comment()

 

ex)

#pragma comment(lib, "dxguid.lib")

 

Project -> Setting -> Link(단축키:ALT+F7)에서 "dxguid.lib"와 "ddraw.lib"와 같은 lib파일을

링크해야 하는 과정을 대신해준다.

 

#pragma warning ()

 

ex)

#pragma warning (disable:4244)

 

4244의 경고 메세지를 출력창에 안보이도록 해준다.

참고로 4244는 무리한(캐스팅연산자가 없이하는) 형변환에 대한 경고이다.

 

#pragma message()

 

ex)

#define _QUOT(x) # x
#define QUOT(x) _QUOT(x)
#define OutputMessage(x)  message( __FILE__ "(" QUOT(__LINE__) ") : " #x )

 

void main()

{

  #pragma OutputMessage(하하하)

}

 

위모양대로 했을경우 컴파일러 출력창에 메세지를 출력해주며 파일과 라인번호를 입력하면

에러났을때 처럼 링크기능이 있다.

cpp의 컴파일 순서를 체크(물론 VC에서는 이에 해당하는 기능도 제공한다)해볼때 유용할거 같다.

[출처] 유용한 전처리기|작성자 나르메

반응형
Posted by blueasa
, |

VS도움말에도 나와있는 내용이지만 프로젝트가 거대해지면 어쩔 수 없이 헤더파일이 이리저리 엉키고 중복선언하게되는데, 그렇게되면 잘 작동하던 Intellisense가 갑자기 작동하지 않고 상태표시줄에 아래와 같은 메시지가 뜨는 것을 볼 수가 있다.

그림 1 : 에러메시지. (Intellisense가 정상작동하는데도 .또는 ->가 뒤바뀌어서 같은 메시지가 뜨는 경우도 있다. )

 

사실 아래와 같은 Intellisense가 뜨는게 정상인데 말이다.

그림 2 : Intellisense

이럴 땐 VS를 종료하고 프로젝트폴더 안을 보면 프로젝트명.ncb의 파일이 존재하는데 가차없이 삭제해주고 VB를 재실행하면 해결된다.

 

(레퍼런스)----------------------------------------------------------------------

Visual Studio 도움말

-----------------------------------------------------------------------------

반응형
Posted by blueasa
, |

1. NiPick에서의 충돌 검사

일반적으로 충돌 처리를 NiPick을 이용해서 많이 합니다.  그런데 NiPick에는 심각한 문제가 있습니다.  그것은 거리와 상관없이 해당 방향에 있는 모든 오브젝트에 대해서 검사를 한다는 것이죠.

 

NiPick 충돌 검사 비용을 줄이기 위해서는 트라이앵글 충돌검사비용을 줄여주어야 합니다.  그렇게 하기 위해서는 경계구에서 거리가 먼경우(경계구의 중심점과 NiPick.Ray의 origin 위치사이의 거리에서 경계구의 반지름을 뺀 값)에는 아예 그 안에 들어가지 않게 하는 것입니다.  이렇게 하면 속도향상을 상당부분 줄 수 있습니다.

 

NiPick을 이용한 충돌은 기본적으로 카메라 충돌 검사, 서있는 위치 알아오기, 이동방향에 대한 충돌 등 다양하게 적용되겠죠.

 

2. Update 비용

Update 비용은 매우 심각한 편입니다.  이 비용을 줄이기 위해서는 씬그래프를 잘 구성해주어야 합니다.  움직이지 않는 오브젝트는 업데이트가 최초 1회만 실행하면 됩니다.  UpdateSelect, UpdateRigid 등도 모두 낭비라는 사실이죠.

 

그러면 어떻게 하면 될까요? 

 

모델링을 할 때, 노드 이름을 정할 때, 다음과 같은 규칙을 정합니다.  노드 이름에 특별한 기능이 들어가 있는 경우에는 @를 붙여봅니다.  그런다음, 1 = pick, 2 = animation 라고 규정을 하죠.

 

노드이름이 @12tree 라고 한다면, pick이 되면서 animation이 되는 노드입니다.  즉 이런 노드는 따로 보관을 합니다.  그런 후에 pick이 필요하면 pick만 모아둔 트리에서 pick을 합니다.  Update가 필요하면 animation만 모아둔 트리에서 합니다.  이렇게 하면 성능을 많이 향상시킬 수 있습니다.

 

3. 캐릭터

캐릭터에는 기본적으로 skin 애니메이션이 들어가있습니다.  캐릭터쪽을 향상시키기 위해서는 Update를 최소로 불러주는 것이 좋겠죠.  이 경우 UpdateRigid와 Update를 혼용해서 사용하면 유리합니다.  위치이동은 해주어야 하니까요.  거리와 시야각에 따라서 Update 안하거나, updateRigid와 Update의 횟수를 조절해주면 도움이 될 것으로 보입니다.

 

4. 컬링 & 렌더링

이 부분은 제가 테스트해본 역사가 없습니다만, 듀얼 프로세스 시스템에서는 컬링 프로세스와 렌더링 프로세스를 분리해준다면 괄목할만한 성능 향상이 이루어질 것이라 생각됩니다.

 

5. 컬러스페이스

처음에 제작하는 사람중에서 가장 성능향상에 걸림돌이 되는 것은 컬러스페이스 문제입니다.  DDS를 사용하면 컬러스페이스 문제를 줄여줄 수 있습니다.  그러나 호환성 부분은 당연히 떨어집니다.  컬러스페이스가 서로 다른 경우 게임브리오는 텍스처를 생성할 때마다, 컬러스페이스를 변환합니다.  NiDevImgConverter가 이역할을 하고 있습니다.  컬러스페이스는 픽셀단위로 작업되기 때문에 매우 느립니다.

 

6. 그룹핑

같은 재질, 같은 텍스처를 사용한다면, 폴리곤을 합치는 것이 일반적으로 유리합니다.  그러나 폴리곤의 덩치가 커지면 그려지는 횟수가 많아지므로 이에 대해서 잘 판단해야 합니다.  이에 대해서는 게임브리오 문서에도 나와있습니다.  culling과 clipping의 싸움을 적절하게 조화해주어야 합니다.

 

7. 캐싱

거대존을 사용하는 경우에는 모델링 데이터 및 텍스처 데이터의 캐싱이 들어가게 됩니다.  일반적으로 캐싱할 관리자를 프로그램한 후에 필요한 데이터가 있는 경우 그 데이터를 Clone 함수를 이용해서 가져오게 됩니다.  그런데 여기서 문제가 있습니다.  게임브리오는 노드 트리 구조에서 보았을 때, 인스턴스 복사가 안 되고, 노드 트리 자체를 모두 복사하게 됩니다.  노드들이 많은 경우에는 메모리 낭비가 심하게 되어서 전체적으로 성능이 떨어지게 됩니다.  예를 들어서 성(castle) 오브젝트가 있다고 한다면, 성 오브젝트는 무수히 많은 노드들로 이루어질겁니다.  그런데 사용하는 것은 오직 한번뿐인데 Clone을 이용해서 가져온다면.. 두개의 복사본이 있기 때문에 그만큼 메모리 낭비가 심해집니다.

그래서 오브젝트 캐싱을 할 때에는 참조횟수를 지정해서 처음 참조가 되는 경우에는 자신의 포인터를 바로 넘겨주고, 두번째부터는 clone을 해주는 것이 좋습니다.



출처 : http://cafe.naver.com/dxgameprogramming/249

반응형
Posted by blueasa
, |
반응형
Posted by blueasa
, |
배포시에는 Dependency Walker(depends.exe)와 같은 도구를 사용하여 종속 DLL의 목록을 확인한다. 또한 2005이상의 VS(Visual Studio)는 manifest를 꼭 확인해서 사용한 dll과 버전을 확인해야한다. 

VC++ Library
그림 1(VS2005 기준)

그림은 VS2005를 기준으로 하고 있지만 다른 버젼도 위와 같은 규칙으로 명명되고 있다. xx는 각각의 버전을 나타내며 플랫폼 별로 x86,ia64,x64용이 따로 있다. u는 Unicode버전을 나타내고 m은 managed code를 사용했을때 사용되며 배포시 .NET Framework이 필요하다. 배포폴더에 manifest가 존재하면 같이 배포한다.

참고
Visual C++ Libraries as Shared Side-by-Side Assemblies - MSDN

64-bit 모드(IA64, X64)에 관해서


컴파일러와 라이브러리의 버젼을 나타내는 매크로

Predefined Macros - MSDN

_MSC_VER(MS 컴파일러)

  Visual Studio 4.0  1000
  Visual Studio 5.0  1100
  Visual Studio 6.0  1200
  Visual Studio .NET 2002  1300
  Visual Studio .NET 2003  1310
  Visual Studio 2005  1400
  Visual Studio 2008  1500

_MFC_VER(MFC 버젼)

 Visual Studio 6.0   0x0600
 Visual Studio .NET 2002   0x0700
 Visual Studio .NET 2003  0x0710
 Visual Studio 2005  0x0800
 Visual Studio 2008  0x0900

_ATL_VER(ATL 버젼)

 Visual Studio 6.0   0x0300
 Visual Studio .NET 2002   0x0700
 Visual Studio .NET 2003  0x0710
 Visual Studio 2005  0x0800
 Visual Studio 2008  0x0900


VC 6 이하(~ VS6)
VC6관련 DLL은 98이후부터는 운영체제에 포함되어 있다. 그러므로 현재는 배포시에 거의 문제될게 없다.

참고
ActiveX 배포를 위한 cab파일(MS) - inf파일에 링크를 추가해준다.
Vcredist.exe로 Visual C++ 응용 프로그램용 최신 런타임 구성 요소가 설치된다 - MSDN고객지원

VC 7.1(VS 2003)
VC7.1관련 DLL은 최신 운영체제라고 해서 더는 기본 내장을 해주지않기 때문에 응용 프로그램이 알아서 자기 디렉터리나 윈도우 시스템 디렉터리에다 구비해야 한다.

 vcredist_x86_2003.zip


VC 8이후(VS 2005~) 

Side by Side Asembly
2005부터는 side by side asembly라는 기술이 도입되어 VC관련 라이브러리들이 Windows 디렉토리 밑에 WinSxS (Windows Side-by-Side)라는 공유 폴더(native assembly cache)로 관리된다. side by side asembly는 DLL 충돌문제를 해결해서 각각의 어플리케이션에게 독립적인 DLL환경을 제공하기 위한것으로 여러버젼의 DLL들이 등록될수 있다. 복잡한 이과정을 간단하게 해결하는 방법으로 VC8부터는 재배포 패키지(Redistributable Package)라는 배포용 설치파일이 도입되어 공용 라이브러리를 자동으로 설치/등록해준다. 또한 프로젝트에서 자동 생성해주는 Manifest Flie에 사용된 DLL과 버전이 자동으로 입력되니 배포시는 꼭 확인하도록 한다. 
프로그래머나 사용자입장에선 관련 DLL을 포함만 하면 되는 기존 작업과 달리 설치파일이 하나 더생겼으니 귀찮은 작업이지만 최신 운영체제의 기본 관리 방법이기도 하고 .Net의 기술과도 무관하니 지금 정리하도록 하자.


최신 라이브러리를 Manifest에 기술하기(VC9)
배포방법을 알아보기 전에 알아두어야 할사항이 있다. VC9는 VC8과 달리 sp1같은 최신 라이브러리를 개발자PC에 설치했다고 해도 프로젝트가 최신라이브러리를 사용한다고 명시해주지 않으면 기존라이브러리를 기본으로 사용하므로 주의해야한다. 이것은 개발자가 sp1을 설치했어도 프로젝트는 기존 DLL버전을 사용하므로 배포시 sp1용 재배포 패키지를 사용하면 제대로 실행될 수 없음을 나타낸다. 방법은 아래와 같다.

'_BIND_TO_CURRENT_VCLIBS_VERSION'를 프로젝트 설정에서 선언해준다.
-> stdafx.h의 상단에 #define으로 입력해도 일반적인 프로젝트에선 상관없지만 프리컴파일드 헤더를 사용안하거나 외부라이브러리가 프로젝트에 미리 세팅되었을 경우 두개의 버전을 중복 사용하는 경우가 발생할 수 있으므로 프로젝트 설정에서 선언하길 권장한다. 이값은 CRT,MFC,ATL,OPENMP 4개의 '_BIND_TO_CURRENT...' 선언을 다 쓰겠다고 선언하는것이고 _BIND_TO_CURRENT_VCLIBS_VERSION의 기본값은 0으로 세팅되어있다.

Manifest File(2008 기준)


참고

응용 프로그램 재배포 및 특정 라이브러리에 바인딩 - MSDN

최신 라이브러리를 메니페스트에 기술하기


배포방법
그림1과 참고에 MSDN링크가 2005의 공용DLL 목록과 설명이다. side by side asembly로 인해 배포문제가 좀 복잡해져서 depends 만을 확인해서 관련 DLL을 포함한다고 제대로 실행된다는 보장을 받을 수 없게되었다. 일반적인 방법과 재배포 패키지를 사용하는 방법을 알아보자.

1. 정적 라이브러리(Static Library)를 사용한다.
실행파일이 커지긴 하나 제일 간단하다. MFC 라이브러리를 사용시는 MFC라이브러리를 정적으로 포함하면 CRT도 자동으로 /MT로 변경된다. 용량은 기본 MFC 다이얼로그 프로젝트가 52k정도에서 308k 정도로 커진다.
ATL과 OPENMP까지 사용한다면 웹에서 배포되는 ActiveX 같은 상황이면 용량때문에 고민해봐야할 문제이고 정적라이브러리도 동적라이브러리와 똑같다고는 하지만 몇가지 버그가 있으므로 주의하자.

참고
MFC Static으로 소켓사용시..

2. Manifest를 참조해서 기본 DLL을 배포한다.(Private Assembly)
배포방법이 바뀌었다곤 하나 관련 DLL을 포함해서 배포할수도 있다. VS의 설치폴더에 있는 공용DLL(Private Assembly)을 같이 배포하면 된다. 우선 Manifest로 사용한 DLL을 확인한후 "..\Microsoft Visual Studio X\VC\redist\"에서 관련 DLL(그림1참고)을 프로젝트 폴더에 포함한다. 이방법도 쉬운편이긴 하나 3MB정도의 크기인 재배포 패키지에 비하면 사용하는 라이브러리가 많을 수록 용량이 너무 큰편이다.(기본 CRT와 MFC라이브러리만 4.76MB - 2008sp1 기준)

3. 인스톨쉴드(Install Shield), 설치 프로젝트를 이용해서 재배포 패키지가 자동으로 설치되게 한다.
 -> 모든 프로젝트를 이렇게 만들긴 좀 귀찮다;;

4. .Net Framework(최신)를 설치한다.
.Net Framework를 설치하면 CRT관련 DLL만 같이 설치된다.(fx 3.5설치시 8.0과 9.0이 같이 설치됨)
managed로 컴파일했다면 .Net Framework는 필수이므로 고민할 문제가 아니지만 용량이 100메가도 훨씬 넘는다..;;

5. 재배포 패키지 - 아래에서 따로 설명한다.

위 방법 외에도 몇가지가 더 있으며 아래링크를 참조하기 바란다. 
RedistributingVisualCppRunTimeLibrary
Bootstrapper for the VC++ 2005 Redists (with MSI 3.1) - codeproject

재배포 패키지(Redistributable Package)
재배포 패키지는 Windows Installer 3.1을 필요로 하는 인스톨 파일이다. 한번 실행하기만 하면 자동으로 설치되지만 사용자 입장에선 불편한 사항이다. 하지만 Windows Installer의 Command line을 이용해서 몰래설치하는 방법이 있다. 이를 이용해서 런처형식의 프로그램을 만들면 Windows Installer 3.1의 설치유무와 재 배포 패키지 자동 설치를 하고 프로그램을 실행하게 할 수 있다. 좀더 범용적으로 사용하면 파일로 설치필요 설정값을 받아와서 COM관련 등록이나 웹배포시 ActiveX등록등을 자동으로 하게 만들수도 있다. 관련 소스가 정리되는 대로 블로그에 올리도록 하고 우선 VC6으로 재배포 패키지의 설치유무와 자동 실행하기 위한 코드를 설명하겠다.

1) 재배포 패키지 설치 유무 확인
재배포 패키지는 설치폴더에서 받거나(..\Microsoft Visual Studio X\SDK\v2.0\BootStrapper\Packages\vcredist_x86) 아래 링크를 통해 MS의 다운로드에서 받는다. 

2-1) 일반 적인 인스톨러 사용처럼 추가/제거 목록에 표시되므로 레지스트리 값으로 확인할 수 있다.
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ProductCode"

2-2) MsiQueryProductState API를 이용해서 확인한다.
리턴값으로 INSTALLSTATE_DEFAULT인지만 확인하면 설치유무를 판단할 수 있다.

위 2개의 방법에서 모두 필요한 ProductCode는 아래와 같다.

Visual C++ 2005 runtime files

Visual C++ 2005 SP1 runtime files

Visual C++ 2008 runtime files

Visual C++ 2008 SP1 runtime files

위코드는 영문 버전이고 한글버전은 다르고 설치시 중복될수 있다. ProductCode와 추가/제거에 표시되는 이름외에 다른점은 없다. 참고로 2008 sp1 x86 한글버전은 {887868A2-D6DE-3255-AA92-AA0B5A59B874}이다.
Window7에서도 테스트 했음.

참고
How to detect the presence of the VC 8.0 runtime redistributable package
How to detect the presence of the Visual C++ 9.0 runtime redistributable package
NSIS로 VC8.0 Redistributables 체크방법
MsiQueryProductState를 통한 VC 2005 Redistribute 라이브러리 설치체크 방법


2) 재배포 패키지 몰래 설치하기(Command Line)
명령행으로 실행시 Windows Installer 로 만들어진 패키지는 /?로 명령어 종류를 볼수 잇다.

Windows Installer V3.0.1.4001.5512 기준으로 캡쳐한화면


VS2005
VC2005의 경우는 재배포 패키지가 VS기본 폴더에 존재하는 설치파일과 웹에서 받은 파일이 좀 다른데 웹에서 받은 재배포 패키지의 경우 압축이 한번더 되어있어서 인자값 설정 방법이 다르다. 또한 압축이 몇번에 걸쳐 되어있으므로 한글로 계정이 되어있어 한글 경로가 있을 경우 잘 설치가 안될수도 있다. 필자는 2008을 사용하므로 2005에 대한 사항은 관련 링크로 정확한 정보를 얻자.
How to perform a silent install of the Visual C++ 8.0 runtime files (vcredist) packages 
- VS기본 폴더에 포함된 재배포 패키지 설치
VC 8.0 런타임 (vcredist) 패키지 몰래 인스톨하기 - 웹에서 받은 재배포 패키지 설치(번역)
Visual C++ 2005 재배포 패키지가 설치안될때



VS2008
2008에서는 
<full path>\vcredist_x86.exe /qb
<full path>\vcredist_x86.exe /qb!
<full path>\vcredist_x86.exe /q   -> 화면에 전혀 보이지 않음
위 3가지 방법중 선택해서 사용하면 되며 Vista이상의 경우는 /q로 설정해도 UAC가 보이므로 필자는 /qb!를 사용한다. 직접 사용해보고 판단하자.

참고
How to perform a silent install of the Visual C++ 2008 redistributable packages

99%'s Code
위 재배포 패키지 몰래설치하기 코드를 간단하게 정리했음.

1. 설치유무 판단후 재배포 패키지를 설치하고 원하는 파일을 실행한다.
01. // 2008 sp1
02.    CString csProduct = "{887868A2-D6DE-3255-AA92-AA0B5A59B874}";  
03.//{9A25302D-30C0-39D9-BD6F-21E6EC160475}
04. 
05.    INSTALLSTATE t = MsiQueryProductState(csProduct);
06.    if(INSTALLSTATE_DEFAULT != t)
07.    {
08.        AfxMessageBox("재배포 패키지를 설치하겠습니다.");
09. //    WinExec("vcredist_sp1_x86.exe /q",SW_SHOW);
10.        WinExec("vcredist_sp1_x86.exe /qb!",SW_SHOW);
11.        AfxMessageBox("설치 완료");
12. // xp에선 설치완료후 정확하게 뜨지만 Windows7에선 완료되지 않았는데 뜬다.
13.    }
14.    else
15.        AfxMessageBox("이미 설치됨");
16.    
17.    // 아래 레지스트리값의 유무로 판단할수도 있다.(win7에서도 똑같다.)
18.    //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{887868A2-D6DE-3255-AA92-AA0B5A59B874}
19.    
20.    while(INSTALLSTATE_DEFAULT != MsiQueryProductState(csProduct))
21.        Sleep(30);
22.    WinExec("test.exe",SW_SHOW);    // 원하는 프로그램 실행

2. 제거
    WinExec("vcredist_sp1_x86.exe /qu",SW_SHOW);
    AfxMessageBox("제거 완료");

 
반응형
Posted by blueasa
, |