블로그 이미지
래머
오늘도 열심히 개발하는 개발자입니다.

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2017. 4. 18. 12:21 C/C++

https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd797987(v=vs.85).aspx



윈도우 api를 통해 조이스틱 사용하는 법

Getting the Driver Capabilities(드라이버 능력확인)

The following example uses joyGetNumDevs and joyGetPos to determine whether the joystick services are available and if a joystick is attached to one of the ports.


다음은 조이스틱 서비스를 이용할 수 있고 포트에 연결되었는지 결정하기위해 joyGetNumDevs와  joyGetPos를 사용하는 예이다.


JOYINFO joyinfo; 
UINT wNumDevs, wDeviceID; 
BOOL bDev1Attached, bDev2Attached; 
 

//조이스틱 디바이스 개수가 0이면 if((wNumDevs = joyGetNumDevs()) == 0) return ERR_NODRIVER;


//조이스틱1이 연결되었는가? bDev1Attached = joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED;


//조이스틱2가 연결되었는가? bDev2Attached = wNumDevs == 2 && joyGetPos(JOYSTICKID2,&joyinfo) != JOYERR_UNPLUGGED;


//조이스틱1, 2중하나라도 연결되었다면 if(bDev1Attached || bDev2Attached) // decide which joystick to use 어떤조이스틱을 사용할지결정 wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2; else return ERR_NODEVICE;




Capturing Joystick Input(조이스틱 입력 획득)


Most of the code controlling the joystick is in the main window function. In the following portion of the message handler, the application calls joySetCapture to capture input from the joystick JOYSTICKID1.

조이스틱을 제어하기위한 코드의 대부분은 메인 윈도우 함수안에 있다. 다음의 메시지 핸들러의 일부에서 어플리케이션은 JOYSTICKID1조이스틱에서 입력을 획득하기 위해 joySetCapture를 호출한다.

case WM_CREATE: 
    //리턴값이 0이 아닌경우 요청이 실패한것이다.
    if(joySetCapture(hWnd, JOYSTICKID1, NULL, FALSE)) 
    { 

//비프음을 출력하고 메시지 박스로 안내 후 프로그램 종료 MessageBeep(MB_ICONEXCLAMATION); MessageBox(hWnd, "Couldn't capture the joystick.", NULL, MB_OK | MB_ICONEXCLAMATION); PostMessage(hWnd,WM_CLOSE,0,0L); } break;

Processing Joystick Messages(조이스틱메시지처리)

The following example illustrates how an application could respond to joystick movements and changes in the button states. When the joystick changes position, the application moves the cursor and, if either button is pressed, draws a bullet hole on the desktop. When a joystick button is pressed, the application draws a hole on the desktop and plays a sound continuously until a button is released. The messages to watch are MM_JOY1MOVE,MM_JOY1BUTTONDOWN, and MM_JOY1BUTTONUP.

다음의 예는 어플리케이션이 어떻게 조이스틱의 이동과 버튼의 상태변화에 응답할 수 있는지 예를 보여준다. 조이스틱의 위치가 변할때 어플리케이션에서 커서를 이동시키고, 버튼이 눌릴경우, 데스크탑에 총알구멍을 그린다. 조이스틱 버튼이 눌리는경우, 어플리케이션은 구멍을 그리고 동시에 버튼 눌림이 해제될때까지 사운드를 플레이 한다. 해당 메시지 들은 MM_JOY1MOVEMM_JOY1BUTTONDOWN, MM_JOY1BUTTONUP 으로 관찰된다.


case MM_JOY1MOVE : // changed position 위치이동됨 if((UINT) wParam & (JOY_BUTTON1 | JOY_BUTTON2)) //wParam에는 버튼 눌림상태가 포함되있다. DrawFire(hWnd); DrawSight(lParam); // calculates new cursor position break; case MM_JOY1BUTTONDOWN : // button is down 조이스틱1 버튼눌림 if((UINT) wParam & JOY_BUTTON1) //1번 버튼이 눌린경우 { PlaySound(lpButton1, SND_LOOP | SND_ASYNC | SND_MEMORY); DrawFire(hWnd); } else if((UINT) wParam & JOY_BUTTON2) //2번 버튼이 눌린경우 { PlaySound(lpButton2, SND_ASYNC | SND_MEMORY | SND_LOOP); DrawFire(hWnd); } break; case MM_JOY1BUTTONUP : // button is up , 조이스틱1 버튼 눌림해제 sndPlaySound(NULL, 0); // stops the sound break;


posted by 래머
2016. 10. 27. 11:42 C/C++

이클립스 사이트에서 c++용 이클립스 다운로드 : Eclipse IDE for C/C++ Developers


http://www.eclipse.org/downloads/eclipse-packages/


현재 최신 버전은 이클립스 네온


이클립스 실행 후  메뉴중


Help -> Install New Software를 선택해서 몇가지 소프트웨어 설치가 필요하다.


해당 메뉴를 선택하면 Install이라는 창이 나온다.

창에서 Work with부분의 콤보박스를 선택해보면 다음과 같은 항목이 있다.


CDT - http://download.eclipse.org/tools/cdt/releases/9.0


위주소는 이클립스 버전에 따라서 다를 수 있음


해당 주소를 선택하면 아래쪽에 체크가 가능한 몇개의 항목이 나온다


CDT Main Features 및 CDT Optional Features를 선택하고 설치를 해준다.

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


이제 MinGW를 설치해준다.


https://sourceforge.net/projects/mingw/files/


위주소로 가서 설치 파일을 다운로드 후 설치를 진행한다.


설치 프로그램을 실행하면 몇가지 초기설치 단계가 진행되고 이후에 몇개의 설치 가능한 패키지를 선택하는 창이 출력된다.


여기에서 최소 C++컴파일러와 툴킷은 선택하고 설치하도록 한다.


설치 버튼이 따로 있는건 아니고, 메뉴항목중 Installation->Apply Changes를 눌러서 패키지 설치/제거가 가능하다.


시스템 환경변수의 Path에  MinGW의 bin폴더를 추가해준다.


윈도우 컴맨드를 실행해서 gcc를 입력했을때 컴파일러가 실행되면 성공


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

이제 이클립스에서 컴파일러를 설정해줘야 한다.


이클립스의 메뉴에서


Window -> preferences -> C/C++ -> Build -> Settings -> Discovery항목을 선택한다.

출력되는 창에서 CDT GCC Built-in Compiler Setting[Shcred]를 눌러 COMMAND항목에

-std=c+11 옵션을 뒤쪽에 추가해준다.

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


이제 이클립스에서 C++프로젝트를 만들고 프로젝트에 C++컴파일러관련 정보를 설정해야한다,.


프로젝트의 프로퍼티창을 오픈한다.


C/C++ Build항목의 Setting를 선택하고 창의 오른쪽에 출력되는 정보중

GCC C++ Compiler-> Dialect 항목을 선택하고 그오른쪽에 다시

Language standard 콤복박스에서 ISO C++11(-std=C++0X)를 선택해준다.





posted by 래머
2014. 5. 8. 10:26 C/C++

저번 시간에 간략하게 나마 포인터의 개념과 메모리의 구조에 대해서 알아봤다.

포인터는 어떠한 변수가 메모리 상의 어느 위치에 저장되 있는지 주소값을 갖고 있는 변수라고 했다.

그런데 포인터 중에는 특이한 포인터가 있으니 바로 이중포인터다.

일반적으로 이중 포인터는 다음과 같이 선언을 한다.


int형의 이중 포인터를 예로 들면


int **pointer;


포인터가 주소를 저장하는 변수라면 이중포인터의 역할은 무엇인가?

이중포인터도 일반 포이터와 마찬가지로 단지 주소를 저장하는 변수일뿐이다.


그렇다면 무엇의 주소를 저장하는가가 이중포인터의 관건일 것이다.


포인터는 메모리상의 주소를 저장하고 있다.

컴퓨터에서 메모리의 주소는 결국 양의 값을 가지는 정수 값일뿐이다.

어떠한 변수값을 예로 들어보자.


int t = 120;


이라는 변수가 선언이 된다면 해당 변수는 메모리 상의 어떠한 위치에 

저장이 되고 그위치에는 120이라는 값이 저장이 될것이다.


그리고 t의 포인터 를 선언해보자


int *pt = &t;

라고 선언을 하게 되면 포인터 pt는 t의 값이 저장된 주소 값을 갖게 될것이다.

만일 t가 메모리 주소 1000이라는 위치에 저장이 되었다 가정한다면

포인터 변수 pt는 1000이라는 값을 가지게 된다.



변수는 값을 메모리에 저장하고 있고 포인터 역시 변수일뿐이다.

그렇다면 pt가 가지고 있는 1000이라는 주소값또한 메모리상의 어떠한 위치에

놓여질 것이다.


이제 pt의 이중포인터를 선언해보자.


int **ppt = &pt;


pt또한 변수이기 때문에 &키워드를 사용해서 그것의 주소값을 기록할 수 있다.


ppt는 pt의 메모리 상 주소값을 가지게 된다.

즉 1000이라는 값이 메모리의 어느위치에 저장이 되있는지 ppt에 저장이 되는 것이다.


이렇게 이중포인터는 포인터의 메모리상 주소를 기록하기 위한 용도를 가지는 변수이다.



posted by 래머
2014. 5. 6. 09:17 C/C++

컴퓨터 프로그래밍에서 기본 자료형은 다양한 형태의 숫자들을 저장하기 위한 공간을 제공한다.

이러한 숫자 공간들은 컴퓨터의 메모리에 정해진 크기만큼의 공간을 차지하여 저장이 된다.


c또는 c++에서는 몇가지의 숫자를 저장하기 위한 기본 자료형들을 제공하는데,

컴파일러의 종류에 따라서 같은 자료형일지라도 그것이 메모리 상에서 차지하는 공간의 크기가 다르다.


VC++기준으로 기본형의 종류와 크기에 대해서 나열해 보면 다음과 같다.


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

char형

char형은 단일 바이트로 표현되는 가장 작은 데이터 단위로 정수를 표현하는 데이터 형이다.

이것은 부호있는(char) 형태와 부호 없는(unsigned char) 형태가 있는데

각기 표현가능한 수의 범위는 다음과 같다.


char                    -128 ~ 127

unsigned char      0 ~ 255

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

short 형

2바이트 메모리 공간을 차지하는 short 형은 역시나 

부호 있는 short 형과 없는 unsigned short 형으로 나뉘며 역시 정수를 표현한다.


short                    -32768 ~ 32767

unsigned short      0 ~ 65536

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

int형

가장 흔히 사용되는 기본 적인 정수 형 수를 표현하기 위해 제공되는 자료형으로

4바이트의 메모리 공간을 차지한다.

int형은 부호가 있는지 또는 없는지에 따라서 표현가능한 범위가 다르다.


부호 있는 int형의 경우


-2147483648 ~ 2147483647 까지의 정수를 표현할 수 있다.


부호 없는 int 즉 unsigned int형의 경우에는

0 ~ 4294967295 까지 표현이 가능하다.

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

long형

int 형과 마찬가지로 정수를 표현한다.

원래 16비트 시절 컴파일러에서는 int형의 경우 2바이트를 long형의 경우 4바이트 메모리 공간을 차지했지만

32비트 세대로 넘어 오면서 두개의 자료형은 같은 크기를 가지게 됐다.

그래서 32비트 컴파일러에서 실질적으로 int와 long형의 차이는 없다.

표현가능한 범위도 같다.

long형도 부호 있는 long과 없는 unsigned long형이 있다.

32비트 운영체제의 사용이 상당한 기간동안 지속되었기 때문에(현재까지도 쓰이고 있다.), int와 long을 별다른 의미없이 사용하는 프로그래머들이 상당히 많았다. 실질적으로 차이가 없음에 불가하고, 그냥 느낌상 좀 큰수를 표현하기 위해서는 long형을 사용해야만 할것같은 강박감... 아무튼 이것은 64비트 시대가 도래함에 따라 문제가 되게된다.

기존의 32비트 코드를 수정없이 그대로 64비트의 시스템에 이식하자니 주의 깊게 사용하지 않은 저런 자료형은 굉장한 골치거리가 된다.

특히나 프로그램의 덩치가 클수록 이런 문제는 더욱 심각하다.

물론 일일이 수정할 수는 있겠지만, 그과정에서 발생할 수 있는 의도치 않은 수많은 버그들은 생각만 해도 머리가 

지끈거린다.

아무튼 그런영향 때문인지 VC++컴파일러는(GCC 같은 것들은 다르다) 64비트로 넘어와서도 int와 long형의 자료크기가 여전히 4바이트로 유지되고 있다.

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

__bigint 형

다른 표준컴파일러들에는 없는 특이한 형태의 자료형이 있으니 그것은 __bigint형이다. 8바이트의 메모리 공간을 차지하고

있으며, 원래되로라면, 이녀석이 long형의 위치에 있어야 했지만 아무튼 vc++에서는 아예 64비트 정수를 표현하기 위한

자료형을 따로 만들었다.

표현가능한 범위는 음,,,, 계산하기 귀찮아서,,, 아무튼 엄청나다.

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

float형

이때까지 나열한 기본 자료형들이 정수를 표현하는 역할을 했다면, 이녀석은 그와 다르게 실수를 표현하기위한 자료이다.

int형과 마찬가지로 4바이트 메모리 공간을 차지하는 float형은 정수부와 소수부를 표현하기 위한 특이한 구조를(부동소숫점) 통해서

실수 표현을 시뮬레이션 하고 있다.

실질적으로 float형은 실수표현을 흉내만 낼뿐 꾀나 큰 오차를 가진 자료형이다.

특히나 정밀한 실수 수치를 다뤄야 하는경우에는 오차 때문에사용하기 힘들다.

하지만 대부분의 경우에는 float형으로 실수 계산을 하기에 별 무리가 없다.

표현가능한 범위는 기억이 안난다.

아무튼 빠르게 실수 계산에 이용할 수 있는 가장 많이 쓰이는 자료형이다.

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

double형

float형과 비슷하게 실수 표현에 사용되는 이자료형은 8바이트 길이를 가지며 float형보다 훨씬 높은 정밀도와

표현 범위를 가진다.

하지만 정밀도가 높아진 만큼 퍼포먼스문제가 있어, 32비트 시절에는 그렇게 자주 애용되는 자료형은 아니었던듯하다(지금은 다르겠지만).

꾀나 정밀한 연산을 해야할때 이용될수 있는 자료형이다. 하지만, 정말 엄청나게 정밀한 실수 연산에는 사용할 수 없다.

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

bool형

c언어에는 없는 새로운 자료형으로 c++에서 논리적인 값을 표현하기 위해 사용된다.

true또는 false 중하나의 상태를 가질 수 있다.

vc++에서는 1바이트 공간을 차지한다.

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

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

지금까지 기본자료형들에 대해서 알아봤다. 이제 포인터의 개념과 메모리의 구조에 대해서 알아보겠다.

먼저 메모리의 구조를 이해하게 되면 포인터에 대해서 쉽게 이해할 수 있게 된다.


메모리

오늘날의 컴퓨터들은 약간씩 구조에 차이는 있겠지만,

대부분은 연산을 위한 cpu, 연산의 명령또는 데이터를 저장하고 있는 메모리, 그리고 그것들의 소통로를 제공해주는 메인보드

그리고 그러한 연산의 결과들을 우리가 보거나 듣거나 만질 수 있게 표현해주는 각종 입출력 장치들로 구성되어 있다.


나머지 장치들을 제외하고 메모리 하드웨어에 대해서 간략히 알아보자.

실질적으로 메모리 하드웨어의 내부 구조가 어떻게 구성되는지는 메모리를 제조하는 제조사들마다 다를 테니 실질적인 물리적

구조를 논하는 것은 의미가 없을듯하다.

하지만 대부분의 메모리들은 논리적으로 다음과 같은 구조를 가지고 있다(반드시 그렇진 않지만 쉽게 생각하기 위해서 그렇다고 가정하자).


각 메모리들은 데이터를 저장하기 위한 셀들을 가지고 있다. 각셀들은 1바이트의 데이터를 저장한다.

이런셀의 개수가 몇개있는지에 따라서 메모리의 용량이 결정이 된다.

만일 10바이트 용량의 메모리가 있다고 가정해보자.

10바이트의 메모리에는

10개의 바이트를 저장하기위한 10개의 셀들이 나열되어 있을것이다.



위에 그림처럼 우리는 각셀들에 이름을 붙이기로 한다.

가장앞에 있는것은 0번 다음것은 1번 씩으로 이름을 순차적으로 하나씩 붙이기로한다.


이제 앞에서 5번째에 있는 셀에 1바이트를 저장한다고 해보자.

위에서 우리는 5번째 셀에 이름을 4로 붙였기 때문에 4번셀에 1바이트를 저장한다고 말할 수 있다.

이렇듯 우리는 어떤 기준점에 대해서 상대적인 위치를 부여 하는 즉 주소를 지정할 수 가 있는것이다.

어느동내의 어느위치에 몇번째 있는 집에 주소를 붙이듯이말이다.


포인터

C나 C++에서 포인터는 바로 위에서 설명한 주소를 가지고 있는 역할을 하는 변수 이다.

위의 예에서 앞에서 5번째에 있는 셀의 주소 즉 4라는 값을 포인터는 가지고 있게 되는것이다.

우리는 4번이라는 이름을 통해서 그게 어느 위치에 있는 셀인지를 식별할 수 있게된다.


컴퓨터의 모든 자료들은 메모리에 놓이게 되고 그것의 자료형 크기만큼 메모리 공간의 바이트를 차지하게 된다.

만일 short 형태의 어떤 자료가 메모리에 저장되어 있다고 가정해보자


short temp = 2;


이렇게 내가 선언한 변수가 위의 10바이트 메모리의 어떤 위치에 저장된다고 가정해보자.

short 형은 2바이트의 공간을 차지하기 때문에 10개의 셀중에서 2개의 셀을 차지하게 될것이다.

만일 temp변수가 앞에서 두번째 셀부터 저장이 된다면 temp변수는 1번과 2번셀을 차지하게 된다.


그리고 하나의 포인터를 선언해보자.

short* pt = &temp;

이렇게 pt라는 포인터에 temp변수의 주소를 할당하게 되면

포인터 pt는 temp변수가 위의 메모리 공간에서 저장된 첫번째 셀의 이름 값을 가지게 되는것이다.

이번의 예에서는 pt는 1이라는 셀의 이름을 가지게 될것이다.

즉포인터는 주소의 값을 저장하고 있는 변수이다.


포인터가 주소의 값을 저장하고 있는 변수라면 앞에 붙은 기본자료형은 대체 어떤 역할을 하는것일까?


byte*  int*, float* 등 다양한 형태의 기본자료형 또는 확장자료형(구조체나 클래스들)으로 포인터를

선언할 수 있다.

하지만 어떤 자료형의 포인터를 선언하더라도 포인터가 무언가의 주소를 가지고 있는 변수라는 

사실은 변하지 않는다.

포인터 선언에 사용된 자료형은 단지 포인터가 어떠한 자료형이 저장된 주소를 가지고 있는가를

나타낼뿐이다.

위의 예에서 short *pt로 선언된 pt포인터는 short형자료가 저장되어 있는 메모리의 주소를 가진다는 의미다.



'C/C++' 카테고리의 다른 글

이클립스 c++ 환경구축  (0) 2016.10.27
포인터의 포인터, 이중 포인터에 대해서 알아보자.  (0) 2014.05.08
왜 소멸자는 virtual 로 만드나?(펌)  (0) 2014.05.02
delete 와 delete[] 의 차이점  (0) 2014.05.02
hlsl 함수  (0) 2014.05.02
posted by 래머
2014. 5. 2. 01:23 C/C++

보통 클래스를 만들면 소멸자를 virtual(가상)함수로 만들게 됩니다.
남들이 다 하닌까.. 따라 하는 사람도 많습니다.
하지만 정확한 이유를 아는 것이 중요합니다.

클래스에서 virtual 함수를 가상함수라고 합니다.
가상함수를 두는 이유는 자신을 상속한 자손 클래스에서 해당 기능을 별도로 구현할때 주로 씁니다.

class TSoccerBall
{
;
virutal void PaintBall();
;
};

위는 축구공을 다루는 클래스라고 가정하고 축구 공을 그리는 부분은 PaintBall() 함수에서 한다고 치십다.
그런데 이 클래스가 그리는 축구공은 2002년형 피파 공인구 입니다. 이를 2006년형 공인구 모양으로 바꾸어
그리고 싶습니다.
물론 TSoccerBall 클래스의 여타 기능은 손댈 필요가 없는 상황입니다.
바로 그런 경우를 대비해서 원 설계자가 필요하면 다시 그리라고 함수를 virtual 로 선언해 놓은 것입니다.

class TSoccerBall2006 : public TSoccerBall
{
;
virutal void PaintBall();
;
virtual ~TSoccerBall2006();
};

이렇게 상속을 받아서 PaintBall() 함수를 새로 만들어 2006형 공으로 그려주면 됩니다.
원래 TSoccerBall  클래스에서는 어떤 상황에서 볼의 모양을 그리도록 내부에서 호출을 하는데
TSoccerBall2006 클래스가 상속받은 후에는 새롭게 만들어진 PaintBall() 함수를 호출하게 바뀝니다.
(물론, 굳이 virtual 로 안해도 이렇게 상속받아 새 공모양을 그리면 되지만, virtual을 쓰게 되면
조상 클래스의 포인트를 가지고도 후손 클래스의 virtual 함수를 부르게 되므로
TSoccerBall 클래스의 객체에서 PaintBall() 을 불러도 2006형 공 모양이 그려지게 됩니다)

이렇게 되면 원래 2002년형 볼을 그리도록 되어 있던 부분은 호출되지 않겠지요.
물론 TSoccerBall::PaintBall(); 로 억지로 호출할 수는 있지만, 새로운 공 모양만을 사용한다면
한번도 호출할 필요가 없어지고 그 코드는 사실 쓸모가 없게 됩니다.
이렇게 되면 이는 필요없는 코드 즉 오버헤드(overhead)가 되는 것입니다.

TSoccerBall2006 클래스는 TSoccerBall의 모든 기능을 이어 받으면서도 공모양만 바꾸었으므로
가상 함수는 정말로 유용한 것이라 아니할 수 없습니다.
그리고 2010년에 또 다른 공모양을 그리라고 새로 만든 PaintBall() 함수도 virtual 로 선언했습니다.

이런 식으로 가상함수를 만드는 것은 오버라이드(override)한다고 합니다.
인자만 다르고 함수 이름이 같은 것을 오버로딩(overloading)이라고 하는 것과 발음은 비슷해도
뜻이 완전히 틀리니 주의 하시기를...

가상함수라는 것은 위에서 잠시 봤듯이 오버헤드를 감수할만큼의 좋은 특징이 있기 때문에
너무나 흔하게 주변에서 접하는 소스에서 virtual 을 볼수 있습니다.

여기까지는 대부분 아시는 사실일 테고요.

다음은 왜 소멸자는 항상 virtual 로 만들까? 하는 문제에 대해 생각해 보겠습니다.
물론 소멸자를 virtual 로 만들지 않는다고 누가 뭐라고하는 사람 없습니다.
어디까지나 플머 마음입니다.

하지만 virtual로 하지 않으면 안되는 이유가 있습니다.
위의 예에서

TSoccerBall2006  *p2006 = new TSoccerBall2006;
TSoccerBall *p = p2006;
와 비슷한 형태로 p 가 자손 객체의 포인트를 가지고 있고
나중에
delete p;
로 객체를 소멸할때
소멸자가 virtual 이 아니면 결코 TSoccerBall2006  의 소멸자는 불려지지 않습니다.
바로 이런 문제 때문에 상속관계에 있고 리소스를 해제해야하는
소멸자는  거의 virtual 로 선언되는 것입니다.

여기서 반드시 알아야 하는 것은 생성과 소멸의 순서입니다.
생성의 과정은 반드시 조상 클래스의 생성자가 먼저 실행되고
소멸의 과정은 virtual 인 경우 자손 클래스의 소멸자가 먼저 실행됩니다.
물론 virtual 이 아니어도 자손의 소멸자가 먼저 실행되지만,
위처럼 조상 클래스 포인트가 자손 객체를 가르키는 경우의 객체 소멸시에도
virtual 인 경우는 소멸자가 안전하게 먼저 불려지게 됩니다.
이 때문에 소멸자는 거의 virtual 을 붙이게 되는 것입니다.

여러분들도 소멸자는 적절하게 상황을 판단해 virtual 을 붙이는게 좋습니다.

또 하나 기억할 것은,
일반 가상함수는 최종적으로 override된 자손의 함수만이 실행되지만
소멸자의 경우는 virtual 일지라도 모든 클래스의 소멸자가 불려진다는 사실을 알아야 합니다.
그러므로, 따로 조상 소멸자의 호출은 필요 없습니다.


너무 간단히 설명해서 초입자 분들은 조금 알아 듣기 힘들지도 모르겠는데
공부하다 보면 무슨 뜻이지 알게 될 것입니다.

그럼..





posted by 래머
2014. 5. 2. 01:22 C/C++


작성자 : 김태선  (jsdkts)

흔히 배열로 할당받은 메모리는 delete[] 로 해제하고, 단일 객체로 할당받은 메모리는 delete 로 해제한다고 알고 있습니다.
이것이 정답이며, 이외의 꽁수를 부리는 것은 문제를 발생시킵니다.
습관적으로 늘 이렇게 짝을 맞춰서 사용했지만, 그래도 정확하게 문제를 아는 것이 중요하다고 생각됩니다.

우선 delete 와 delete[]의 차이를 한마디로 말하라면
delete 는 단일 객체에 대한 소멸자의 호출과 메모리 환원을 하며
delete[] 는 배열객체에 대한 각각의 소멸자의 호출과 메모리 환원을 한다는 것입니다.

그러므로
ClassA  *pp = new ClassA;
delete pp;
는 아무런 문제가 없으나
ClassA  *pp = new ClassA[10];
delete pp;
과 같이 하는 경우는 pp[0]에 대한 소멸자와 메모리 해제만 이루어 집니다.
반드시
ClassA  *pp = new ClassA[10];
delete[] pp;
와 같이 해야 합니다. 그렇지 않으면 메모리 누수가 발생합니다.

혹자는 그냥 delete 만 써도 아무런 문제가 없다라고 말합니다.
이는 프로그램 종료시 할당받았던 전체 메모리가 OS에 의해 해제되기 때문입니다.
즉 메모리에 민감한 프로그램이 아닌 경우이겠지만, 정밀한 프로그램에서는 반드시 짝을 맞춰야 합니다.

실제 눈으로 확인을 해 봅시다.

다음은 테스트코드입니다. C++빌더의 테스트코드이긴 하지만 이해에 어려움은 없을 것입니다.

void    add(void *ptr)
{
    ULONG  p = (ULONG)ptr;
    Form1->Memo1->Lines->Add(String().sprintf("%08X", p));
}

void __fastcall TForm1::Button2Click(TObject *Sender)
{
    class A
    {
    private:
        void *p;
    public:
        A(void *ptr = 0)
       {
            p = ptr;
            add(this);  // 메모리 할당 받은 직후 자신의 메모리 베이스 어드레스를 표시하게 합니다.
        }
        ~A()
        {
            int k = 0;      // for trace
        }
    };

    A  *p = new A[2];

    delete[] p;        //***** 이 라인이 문제입니다.

    A  *p1 = new A;
    delete p1;
}


위 코드는 아무런 문제가 없습니다.

제 컴퓨터에 실행해서 힙 메모리 할당 번지를 보면

00D16D00   // p[0] 꺼
00D16D04   // p[1] 꺼
00D16D0C  // p1 꺼
이렇게 주소가 나옵니다.

여러번 실행해도 같은 주소만 계속 나옵니다. 메모리 할당과 해제가 완전하게 이루어진다는 뜻이죠.

그러면  delete[] p; 문장을 delete p; 로 바꾸고 실행해 보겠습니다.
처음 3개는 위와 같습니다.
00D16D00
00D16D04
00D16D0C
두번째로 함수가 다시 실행할때는 해제되지 못한 메모리로 인해 그 뒷번지에서 할당 받습니다.
00D16D10
00D16D14
00D16D1C
세번째도 마찬가지입니다. 이런식으로 메모리 잠식이 일어납니다.
00D16D20
00D16D24
00D16D2C

프로그램을 종료하고 다시 실행해 봅니다.
역시 위와 똑 같은 메모리 번지가 출력됩니다. 해제되지 못한 메모리가 OS에 되돌려졌다는 것을 알수 있습니다.

그럼..



posted by 래머
2014. 5. 2. 01:18 C/C++



hlsl 함수 비공개 3d관련 자료

2009/07/16 01:30 수정 삭제

작성자: 그라센(choi98772)

복사 http://blog.naver.com/choi98772/memo/130064811122

DirectX HLSL함수

놀 것 2008/01/29 03:11
absvalue abs(value a)절대치 (성분마다).
acosacos(x)x 의 각 성분의 역코사인을 돌려준다. 각 성분은,[-1, 1] 의 범위로 한다.
allall(x)x 의 모든 성분이 0 이외의 값인지 아닌지를 테스트한다.
anyany(x)x 의 몇개의 성분이 0 이외의 값인지 아닌지를 테스트한다.
asinasin(x)x 의 각 성분의 역정현을 돌려준다. 각 성분은,[-pi/2, pi/2] 의 범위로 한다.
atanatan(x)x 의 각 성분의 역탄젠트를 돌려준다. 반환값은,[-pi/2, pi/2] 의 범위이다.
atan2atan2(y, x)y/x 의 역탄젠트를 돌려준다. y 와 x 의 부호를 사용해 [-pi, pi] 의 범위에 있는 반환값의 상한을 판단한다. atan2 는, x 가 0 으로 동일하고, y 가 0 으로 동일하지 않은 경우에서도, 원점 이외의 각 점에 대해서 충분히 정의되고 있다.
ceilceil(x)x 이상의 최소의 정수를 돌려준다.
clampclamp(x, min, max)x 를 [min, max] 의 범위에 제한한다.
clipclip(x)x 의 몇개의 성분이 0 보다 작은 경우, 현재의 픽셀을 파기한다. x 의 각 성분이 면으로부터의 거리를 나타내는 경우, 이 함수를 사용해, 클립면을 시뮬레이션 한다.
coscos(x)x 의 코사인을 돌려준다.
coshcosh(x)x 의 쌍곡코사인을 돌려준다.
crosscross(a, b)2 개의 3D 벡터 a 와 b 의 외적을 돌려준다.
D3DCOLORtoUBYTE4D3DCOLORtoUBYTE4(x)4D 벡터 x 의 성분을 교체 및 스케일링 해, 일부 하드웨어에 있는 UBYTE4 지원의 부족을 보정한다.
ddxddx(x)스크린 공간의 x 좌표에 대해, x 의 편미분을 돌려준다.
ddyddy(x)스크린 공간의 y 좌표에 대해, x 의 편미분을 돌려준다.
degreesdegrees(x)x 를 라디안 단위로부터 도수로 변환한다.
determinantdeterminant(m)서방 행렬 m 의 행렬식을 돌려준다.
distancedistance(a, b)2 개의 점 a 와 b 간의 거리를 돌려준다.
dotdot(a, b)2 개의 벡터 a 와 b 의 내적을 돌려준다.
expexp(x)e 를 바닥으로 하는 지수 ex 를 돌려준다.
exp2value exp2(value a)2 를 바닥으로 하는 지수 (성분마다).
faceforwardfaceforward(n, i, ng)-n * sign(dot(i, ng))를 돌려준다.
floorfloor(x)x 이하의 최대의 정수를 돌려준다.
fmodfmod(a, b)a = i * b + f 가 되는 것 같은, a / b 의 부동 소수점수(실수)의 잉여 f 를 돌려준다. 여기서, i 는 정수, f 는 x 와 부호가 같아, 그 절대치는 b 의 절대치보다 작다.
fracfrac(x)f 가 0 보다 크고, 1 보다 작은 값이 되는 것 같은, x 의 소수부 f 를 돌려준다.
frcvalue frc(value a)소수부 (성분마다).
frexpfrexp(x, out exp)x 의 가수와 지수를 돌려준다. frexp 는 가수를 돌려주어, 지수는 출력 인수 exp 에 저장 된다. x 가 0 의 경우, 함수는 가수와 지수의 양쪽 모두에 0 을 돌려준다.
fwidthfwidth(x)abs(ddx(x)) +abs(ddy(x))를 돌려준다.
isfiniteisfinite(x)x 가 유한의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
isinfisinf(x)x 가 +INF 나 -INF 의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
isnanisnan(x)x 가 NAN 나 QNAN 의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
ldexpldexp(x, exp)x * 2exp 를 돌려준다.
lenfloat len(value a)벡터의 길이.
lengthlength(v)벡터 v 의 길이를 돌려준다.
lerplerp(a, b, s)a + s(b - a)를 돌려준다. 이 함수는, s 가 0 의 경우는 a 를 돌려주어, 1 의 경우는 b 를 돌려주도록, a 와 b 의 사이를 선형 보간 한다.
litlit(ndotl, ndoth, m)조명의 벡터 (앰비언트, 디퓨즈, 스펙큐러, 1)를 돌려준다. 앰비언트 = 1; 디퓨즈 = (ndotl < 0) ? 0 : ndotl; 스펙큐러 = (ndotl < 0) || (ndoth < 0) ? 0 : (ndoth * m);
loglog(x)x 의, 바닥이 e 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
log10log10(x)x 의, 바닥이 10 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
log2log2(x)x 의, 바닥이 2 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
maxmax(a, b)a 와 b 의 큰 (분)편을 선택한다.
minmin(a, b)a 와 b 가 작은 (분)편을 선택한다.
modfmodf(x, out ip)값 x 를, 각각이 x 와 같은 부호를 가진 소수부와 정수부로 나눈다. x 의 부호 첨부 소수부가 반환된다. 정수부는 출력 인수 ip 에 저장 된다.
mulmul(a, b)a 와 b 의 사이의 행렬 곱셈을 실행한다. a 가 벡터의 경우, 행 벡터로서 처리한다. b 가 벡터의 경우, 열로서 처리한다. 내부 넓이의 a 열과 b 행은 동일해야 한다. a 행 x b 열의 넓이를 얻을 수 있다.
noisenoise(x)처리되지 않다.
normalizenormalize(v)정규화된 벡터 v / length(v)를 돌려준다. v 의 길이가 0 의 경우, 결과는 무한이 된다.
powpow(x, y)xy 를 돌려준다.
radiansradians(x)x 를 도수로부터 라디안 단위로 변환한다.
reflectreflect(i, n)입사 방향 i, 표면 법선 n 로 했을 경우의, v = i - 2 * dot(i, n) * n 에 의해 구할 수 있는, 반사 벡터 v 를 돌려준다.
refractrefract(i, n, eta)입사 방향 i, 표면 법선 n, 굴절 eta 의 상대 인덱스가 주어졌을 경우의, 굴절 벡터 v 를 돌려준다. i 와 n 의 사이의 입사각이 지정된 eta 보다 너무 크면 (0,0,0)를 돌려준다.
roundround(x)x 를 가장 가까운 정수에 말다.
rsqrtrsqrt(x)1 / sqrt(x)를 돌려준다.
saturatesaturate(x)x 를 [0, 1] 의 범위에 제한한다.
signsign(x)x 의 부호를 요구한다. x 가 0 보다 작은 경우는 -1, 0 으로 동일한 경우는 0, 0 보다 큰 경우는 1 을 돌려준다.
sinsin(x)x 의 정현을 돌려준다.
sincossincos(x, out s, out c)x 의 정현과 코사인을 돌려준다. sin(x)는 출력 인수 s 에 저장 되어 cos(x)는 출력 인수 c 에 저장 된다.
sinhsinh(x)x 의 쌍곡정현을 돌려준다.
smoothstepsmoothstep(min, max, x)x < min 의 경우는 0 을 돌려준다. x > max 의 경우는 1 을 돌려준다. x 가 [min, max] 의 범위내이면, 0 으로 1 의 사이의 매끄러운 에르미트 보간을 돌려준다.
sqrtvalue sqrt(value a)제곱근 (성분마다).
stepstep(a, x)(x >= a) ? 1 : 0 을 돌려준다.
tantan(x) x 의 탄젠트를 돌려준다.
tanhtanh(x)x 의 쌍곡탄젠트를 돌려준다.
tex1Dtex1D(s, t)1D 의 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 스칼라-.
tex1Dtex1D(s, t, ddx, ddy)미분을 지정한, 1D 의 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t, ddx, ddy 는 스칼라-.
tex1Dprojtex1Dproj(s, t)1D 의 투영 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex1Dbiastex1Dbias(s, t)1D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
tex2Dtex2D(s, t)2D 의 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 2D 텍스처 좌표.
tex2Dtex2D(s, t, ddx, ddy)미분을 지정한, 2D 의 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t, ddx, ddy 는 2D 벡터.
tex2Dprojtex2Dproj(s, t)2D 의 투영 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex2Dbiastex2Dbias(s, t)2D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
tex3Dtex3D(s, t)3D 의 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 3D 텍스처 좌표.
tex3Dtex3D(s, t, ddx, ddy)미분을 지정한, 3D 의 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t, ddx, ddy 는 3D 벡터.
tex3Dprojtex3Dproj(s, t)3D 의 투영 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex3Dbiastex3Dbias(s, t)3D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
texCUBEtexCUBE(s, t)3D 의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 3D 텍스처 좌표.
texCUBEtexCUBE(s, t, ddx, ddy)미분을 지정한, 3D 의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t, ddx, ddy 는 3D 벡터.
texCUBEprojtexCUBEproj(s, t)3D 투영의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
texCUBEbiastexCUBEbias(s, t)3D 의 바이어스 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
transposetranspose(m)행렬 m 의 전치행렬을 돌려준다. 입력의 넓이가 m 행 x m 열의 경우, 결과는 넓이 m 열 x m 행이 된다.





posted by 래머
2014. 5. 2. 01:17 C/C++



[본문스크랩] [번역] Light Space Perspective Shadow Maps [LisPSM] 비공개 3d관련 자료

2009/12/13 16:19 수정 삭제

작성자: 그라센(choi98772)

복사 http://blog.naver.com/choi98772/memo/130075701859

출처 꿈★은 이루어진다 | 주바리
원문 http://blog.naver.com/xtar/70038803883

Light Space Perspective Shadow Maps

Michael Wimmer, Daniel Scherzer and Werner Purgathofer


원문: http://www.cg.tuwien.ac.at/research/vr/lispsm
번역: http://starlike.cafe24.com/moniwiki/wiki.php/LightSpacePerspectiveShadowMaps
개요: LisPSM 기본 이론

1 소개
2 이전의 작업들
3 Light space perspective shadow maps
3.1 동기
3.2 개요
3.3 Focussing the shadow map
3.4 The perspective frustum in light space
3.5 Choosing the free parameter n
3.6 Applying the perspective frustum
4 Analysis and optimal parameter estimation
4.1 What is perspective aliasing
4.2 Logarithmic shadow mapping
4.3 Analysis of light space perspective shadow maps
4.4 General case
4.5 Discussion
5 결과
6 결론과 이후의 작업들
7 참고문헌



 




'C/C++' 카테고리의 다른 글

delete 와 delete[] 의 차이점  (0) 2014.05.02
hlsl 함수  (0) 2014.05.02
액티브x배포관련  (0) 2014.05.02
ActiveX 권한 상승에 대한 일반적인 이야기와 솔루션들 [펌]  (0) 2014.05.02
[본문스크랩] [MSSQL] 함수  (0) 2014.05.02
posted by 래머
2014. 5. 2. 01:16 C/C++

이미 여러 블로그 아티클로도 잘 다뤄진 내용이지만 몇 가지 부수적인 내용을 덧붙여서 ActiveX 패키징 및 배포에 관한 내용을 총 정리해보았습니다. 실무에서는 아직도 ActiveX 컨트롤에 관한 유지보수를 틈틈이 수행할 필요가 있다보니 이런 문서를 작성하게 되는 것 같습니다. :-)

1. INF 파일에 대한 이해

INF 파일은 ActiveX 패키지 배포 시 내장되는 설치 정보 파일로 보통 배포되는 CAB 파일의 이름과 동일하게 맞추어서 배포합니다. 예를 들어 MyControl.cab을 배포한다면 MyControl.inf 파일을 내장하고 있어야 하는 것입니다.

그리고 INF 파일의 문법적인 구조에 대한 상세한 설명이 없어서 상당히 답답한 때가 많습니다만 간략하게 두 가지 사례를 짚어서 설명해보도록 하겠습니다.

a. Hooking 없이 설치하는 경우

Hooking이란 설치 도중에 별도의 외부 프로세스를 이용하여 설치 자체를 자동화하거나 설치 전/후 과정을 제어하기 위한 일종의 옵션 기능입니다. ActiveX에 수많은 보안 결함이 존재하지만 디자인적으로 보았을 때에도 충분히 문제가 드러납니다. 대부분 간단한 유형의 ActiveX 컨트롤은 Hooking 기능 없이 배포가 되는 편입니다. 다음의 샘플 INF 코드를 살펴보기로 합니다.

접기

[version]
signature="$CHICAGO$
AdvancedINF=2.0

[Add.Code]
SecureMail.ocx=SecureMail.ocx
libeay32.dll=libeay32.dll
clientcert.pem=clientcert.pem
clientkey.pem=clientkey.pem

[clientkey.pem]
file-win32-x86=thiscab
Destdir=11

[clientcert.pem]
file-win32-x86=thiscab
Destdir=11

[libeay32.dll]
file-win32-x86=thiscab
Destdir=11

[SecureMail.ocx]
file-win32-x86=thiscab
clsid={6BBDF9C9-AF4E-45CE-B543-159C9E19577B}
RegisterServer=yes
FileVersion=2,0,0,2
Destdir=11

접기

흔히 볼 수 있는 형태의 INF 파일입니다. 제일 상단에 있는 것이 INF 파일의 형식에 관한 기본 정보이고 Windows 95 이후로부터는 대체로 Advanced INF 2.0 규격을 따르는 것 같네요. (시그니처에 Windows 95의 코드 네임인 Chicago가 적혀있는 것으로 보았을 때도 그러합니다.)

하단의 [Add.Code] 섹션에서는 이 CAB 파일을 통해서 배포될 파일들을 기술합니다. 편의 상 파일 이름과 엔트리 이름을 일치시켜두는 것도 좋습니다. 그리고 각각의 파일명으로 기술된 섹션이 해당 파일을 어떤 식으로 복사되고 관리되어야 하는지를 서술하는 부분으로 이후에 설치 제거를 할 때 근거 정보가 됩니다.

각 섹션 별로 file-win32-x86 이라는 엔트리 명이 보이는데 파일의 플랫폼과 대상 아키텍처를 지정하는 부분입니다. 옛날 (NT 시절)에는 Alpha나 MIPS 계열 프로세서를 위하여, 요즈음에는 x64 (AMD64) 프로세서를 위하여 이 부분이 달리 할당될 수도 있을 것입니다. 그리고 thiscab 이라는 값은 이 CAB 파일 내에 파일이 있음을 뜻하는 부분입니다.

DestDir 엔트리는 이 파일이 어느 위치로 복사되어야 하는지를 나타내는 부분으로 많은 옵션이 있을것 처럼 보이지만 실제로 알려진 것은 세 종류인듯 합니다. 값을 10으로 지정하면 %windir% 경로 (흔히 C:\Windows)에, 11로 지정하면 %windir%\system32 (Windows 9x 계열 운영체제에서는 %windir%\system)에, 비워두면 OCCACHE 디렉터리 (%windir%\Downloaded Program Files)에 복사됩니다.

그리고 이제 OCX 파일 섹션의 내용을 살펴보기로 합니다. OCX 파일의 내용에서 꼭 확인해야 할 것은 RegisterServer=yes 항목과 Version 항목, 그리고 clsid 항목입니다. Version은 OCX 파일을 빌드할 때 추가되는 Win32 리소스의 버전 테이블 정보와 일치할 필요가 있으며, clsid 항목은 실제로 브라우저에서 인스턴스화되어 실행되어야 하는 COCLASS의 CLSID와 일치해야 합니다.

b. Hooking의 경우

더보기

ActiveX를 이용하여 대리 설치 프로그램을 기동시키기 위하여 위와 같이 작성할 수도 있지만, 컴포넌트별로 Hook을 다르게 지정할 수도 있으니 참고하시기 바랍니다. Hook을 실행하기 위하여 %EXTRACT_DIR% 라는 지역 환경 변수를 지정한 것을 볼 수 있고 프로그램 매개 변수까지 지정이 가능한 것을 보실 수 있습니다.

Hook에서 지정한 프로그램의 경우 주의 사항이 한 가지 있다면, 프로그램의 실행 종료 코드가 0으로 끝날 필요가 있다는 것입니다. 그렇지 않으면 설치가 중간에 실패한 것으로 인지되어 브라우저에는 아무것도 표시되지 않는다고 합니다. 이를 미리 확인해보려면 패키징 하기 전에 Hook을 통해서 구동하려는 프로그램을 똑같이 명령 프롬프트에서 아래와 같이 실행하면 알 수 있습니다.

START /WAIT "실행할 프로그램 및 매개 변수들"
ECHO %ERRORLEVEL%

%ERRORLEVEL% 환경 변수의 값이 0이 아닌 것으로 나타나면 Hook 프로그램으로는 사용할 수 없을 것입니다.

2. PVK 파일과 SPC 인증서 파일을 PFX 파일로 변환하기

SIGNCODE 도구에서 SIGNTOOL 도구로 새롭게 바뀐 이후부터, ActiveX CAB 파일에 서명을 하거나 EXE 파일 위에 서명을 하는 방법이 조금 바뀌었습니다. 바로 PFX 파일을 생성하는 작업이 추가 된 것인데, 기존에 가지고 있는 인증서 파일 형식이 PVK 파일과 SPC 파일 두 가지로 구성된 경우 아래와 같이 변환해야 합니다.

PVK2PFX -PVK "PVK 파일의 경로" -SPC "SPC 파일의 경로" -PFX "PFX 파일을 만들 경로" -PI "기존 인증서의 Password" -F

PVK2PFX 유틸리티는 .NET Framework SDK v2.0 혹은 Windows SDK v6.0 이상을 설치하면 같이 따라오는 유틸리티입니다. 여기서 -PVK 스위치 다음에 PVK 파일의 경로를, -SPC 스위치 다음에 SPC 파일의 경로를 서술해주어야 하고, 그 다음 -PFX 스위치 다음에 새로 만들 PFX 파일의 경로를 서술해야 합니다. 추가적으로 인증서로부터 암호를 해독하고 PFX 파일에 암호를 새로 설정하기 위하여 -PI 스위치 뒤에 인증서 암호를 기술합니다. 기본 동작이 기존 파일이 있을 경우 실패로 처리되는데 편의상 -F 스위치를 지정하여 PFX 파일을 덮어쓰도록 실행합니다.

3. CAB 파일에 서명을 할 때에는 반드시 Spanning Space를 확보할 것

CAB 파일에 서명을 추가하기 위해서는 별도의 Spanning Space를 확보해야 하는데 이를 위해서는 CABARC 유틸리티로 압축해야 합니다. (다른 압축 유틸리티에서는 지원하지 않을 수도 있습니다.)

CABARC -S 6144 N "생성할 CAB 파일의 경로" "파일1" "파일2" ... "파일n"

-S 스위치가 Spanning Space 확보를 지시하는 스위치로 이 뒤에 Magic Constant 값 6144를 지정합니다. 이것이 인증서 크기에 관한 Magic Constant로 문서화된 수치이므로 그대로 사용하면 됩니다. 새로운 파일을 만들어야 하므로 N 스위치를 지정하여 모드를 변경하고, 그 다음에는 CAB 파일이 생성될 경로, 그리고 그 다음부터는 CAB 파일에 포함할 파일들을 하나씩 서술하면 됩니다. 와일드 카드도 지원하므로 편리합니다.

4. 실제로 서명하고 확인하기

SIGNCODE 프로그램보다 기능이 다양하고 정교해진 SIGNTOOL을 이용해서 서명을 하려면 다음의 스위치 설정을 활용합니다.

SIGNTOOL SIGN /F "PFX 파일 경로" /P "PFX 파일 암호" /T "타임스탬프 URL" "대상 파일 1" "대상 파일 2" ... "대상 파일 n"

SIGNTOOL 프로그램을 서명 모드로 실행하기 위하여 SIGN 스위치를 지정하고, /F 스위치에는 2단계에서 만든 PFX 파일, 그리고 /P 스위치에는 2단계에서 지정한 인증서 암호를 입력합니다. /T 스위치 뒤에는 실제로 접속 가능한 타임스탬프 URL을 지정해야 하는데 이는 인증서 발행 업체가 제공하는 고유 URL을 이용하면 됩니다. 마지막으로 대상 파일들을 지정하면 한꺼번에 동일한 인증서로 서명할 수 있습니다.

CAB 파일을 제외한 보통의 EXE, DLL, OCX 등에 직접 서명하는 것은 별도로 빌드할 설정해주어야 할 특별 옵션같은것들은 존재하지 않습니다.

좀 더 자세한 내용을 보시려면 아래 Article들도 참고하시면 좋겠습니다.

http://ditongs.egloos.com/1511212
http://littletrue.egloos.com/3968904




posted by 래머
2014. 5. 2. 01:15 C/C++



ActiveX 권한 상승에 대한 일반적인 이야기와 솔루션들 [펌] 비공개 액티브x

2009/12/27 19:08 수정 삭제

작성자: 그라센(choi98772)

복사 http://blog.naver.com/choi98772/memo/130076764234

 ActiveX 권한 상승에 대한 일반적인 이야기와 솔루션들  | ActiveX/COM2009-10-20 오전 11:02:44
남정현 (rkttu2002)   번호: 8380 추천:4  / 읽음:898
BLOCKQUOTE { MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px } P { MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px } v\:* { BEHAVIOR: url(#default#VML) } o\:* { BEHAVIOR: url(#default#VML) } .shape { BEHAVIOR: url(#default#VML) } x\:* { POSITION: relative; VISIBILITY: hidden } TD { WORD-BREAK: break-all } BODY { WORD-WRAP: break-word } UL { MARGIN-TOP: 5pt; MARGIN-BOTTOM: 5pt; MARGIN-LEFT: 30pt } OL { MARGIN-TOP: 5pt; MARGIN-BOTTOM: 5pt; MARGIN-LEFT: 30pt }

인사말

 

안녕하세요. 데브피아 C# 포럼에서 활동 중인 남정현 인사드립니다. 예전에 Visual C++ 포럼에서 자주 곁눈질로 코드를 배우고 연습했던 추억이 새록새록 떠오르네요. 지금은 C#으로 주로 프로그래밍을 하고 있지만 Visual C++을 통하여 프로그래밍을 하거나, 이슈를 고민하는 일도 아직 많이 있다보니 블로그에 Visual C++ 관련 포스트를 가끔 올립니다.

 

이제 어느정도 Windows Vista나 Windows 7에 관한 UAC 문제가 해결되셨을 것으로 알고 있습니다만 Visual C++ 포럼에 인사도 올릴겸 블로그 포스트 내용을 발췌하여 올립니다. 혹시 수정해야 할 부분이나 잘못된 부분이 있으면 많은 지적과 첨삭 부탁드립니다.

 

감사합니다. :-)

 

데브피아 C# 포럼 시삽 남정현 올림

 


 

최근 고객사의 요청으로 ActiveX 컨트롤 하나를 유지보수하고 있습니다. ActiveX 컨트롤에서 탈피하려는 추세가 있지만, 별 다른 대안이 없어서 ActiveX 컨트롤을 유지보수해야 하는 경우도 아직 우리나라에서는 상당히 많은것 같습니다.

 

Internet Explorer 7.0부터는 보호 모드라는 개념이 새로 소개되었습니다. 보호 모드란, 일종의 Sand-box 개념으로 기존과 같이 현재 로그온한 사용자의 권한을 그대로 물려받아 무분별하게 실행되는 것을 방어하는 안전 장치입니다. Windows XP와는 달리 Windows Vista부터는 일반 사용자를 단순히 관리자로 분류하지 않고, UAC를 통하여 작업에 대해 허가/거절 여부를 정할 수 있게 하였습니다.

 

우리가 권한 상승이라고 이야기하는 기능은 사실 권한 상승을 사용자에게 요청하는 것입니다. 이러한 권한 상승 요청을 구현하기 위하여 이제까지 참고할 수 있는 보편적인 리소스는 EXE 파일과 함께 매니페스트 파일을 배포하는 것이 대표적인 것이었습니다. 그리고 ActiveX 컨트롤의 경우 Elevation Moniker를 통하여 권한 할당을 받는 것이 대표적입니다.

 

ActiveX 컨트롤에서 어떻게 권한 상승을 구현할 수 있는지에 대하여 잘 정리한 권용휘 MVP님의 아티클을 참고하시면 어떻게 권한 상승이 이루어지고 관리될 수 있는지에 대한 컨셉을 확인하실 수 있습니다. (http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=7669) 저는 권용휘 MVP님의 아티클 위에 몇 가지 내용을 더 첨언해보고자 합니다.

 

1. Visual C++ 6.0 (SP6)에서의 권한 상승 구현

 

놀랍게도, 그리고 안타깝게도, Visual C++ 6.0 개발 도구를 업그레이드하지 못하는 이슈는 도처에 널려있습니다. 기존에 개발되어있던 소프트웨어나 라이브러리가 특정 문자 세트에 완벽하게 맞추어져있지만, 업그레이드할 수 없을만한 이슈 (개발 업체의 부도 - 또는 - 계약 해지 / 담당자의 연락 두절과 같은)로 인하여, 올해부터는 더 이상 일체의 기술 지원을 받을 수 없는 (서비스 팩 다운로드도 MS 공식 홈페이지에서는 더이상 받으실 수 없습니다.) 그런 개발 플랫폼위에서 고군분투해야 하는 상황은 생각보다 자주 있습니다.

 

사실 권한 상승을 제대로 프로그래밍하려면 Windows Vista나 Windows 7 SDK가 필요합니다. 하지만 이들 SDK의 코드를 가져다 사용하려면 개발 도구를 Visual C++ 2005나 2008로 업그레이드할 필요가 있습니다. 하지만 개발 도구나 Windows SDK를 설치하지 않고 간단히 적용할 수 있다고 소개된 방법은 다행히 Visual C++ 6.0에서 온전하게 동작합니다.

 

typedef struct tagBIND_OPTS3 : tagBIND_OPTS2 {
 HWND hwnd;
} BIND_OPTS3, *LPBIND_OPTS3;


위의 코드를 자주 사용하는 헤더에 선언해두면 권한 상승을 위하여 호출하는 CoGetObject에 정확히 바인딩할 수 있습니다. 그리고, 아래의 두 함수를 이용하여, 권한 상승을 지원하는 운영 체제 (Windows Vista 이상의 운영 체제)를 식별하고 실제로 객체를 생성할 수 있습니다.

 

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, OUT void ** ppv)
{
 BIND_OPTS3 bo;
 OLECHAR wszCLSID[50];
 OLECHAR wszMonikerName[300];
 const int CHARS_IN_GUID = 39;

 StringFromGUID2(rclsid, wszCLSID, CHARS_IN_GUID);
 swprintf(wszMonikerName, L"Elevation:Administrator!new:%s", wszCLSID);
 wprintf(L"%s\r\n", wszCLSID);
 wprintf(L"%s\r\n", wszMonikerName);

 memset(&bo, 0, sizeof(bo));

 bo.cbStruct = sizeof(bo);
 bo.hwnd = hwnd;
 bo.dwClassContext = CLSCTX_LOCAL_SERVER;

 return CoGetObject((LPCWSTR)wszMonikerName, &bo, riid, ppv);
}

 

BOOL IsUACRequiredOperatingSystem(void)
{
 BOOL bIsUACRequired = FALSE;
 OSVERSIONINFO sInfo;
 sInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 if (GetVersionEx(&sInfo))
 {
  if( sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
   sInfo.dwMajorVersion >= 6 &&
   sInfo.dwMinorVersion >= 0) { // Windows VISTA or Higher
   bIsUACRequired = TRUE;
  }
 }

 return bIsUACRequired;
}

 

Visual C++ 6.0 기반의 프로젝트들은 거의 대부분 ANSI나 Multibyte Character Set 기반의 문자 세트를 기준으로 프로그램이 구성되어있습니다. 확장성을 위하여 Transition이 가능한 데이터 타입 (LPTSTR, LPCTSTR, TCHAR 등)은 거의 고려하지 않았을 확률이 높습니다. 하지만 우리가 호출해야 할 API들은 유니코드 문자열을 필요로 하기 때문에 직접 유니코드를 사용하도록 기존 코드를 수정하였습니다. 그리고 더불어서, StringCchPrintf 함수를 대신하여 sprintf 계열의 함수를 직접 이용하였습니다.

 

2. 실제 ActiveX 컨트롤에 권한 상승을 적용하는 또 다른 방법

 

권용휘 MVP님의 아티클에서 설명하는 방법은, 특정 메서드나 프로퍼티의 호출 과정에서 필요로하는 권한 상승을, 권한 상승을 외부에서 요구하고 결과를 받기 위한 프록시 멤버와, 실제 처리하는 멤버로 이원화하여 구현하는 방식입니다. 대부분의 경우 이 방법으로 해결할 수 있다고 생각합니다. 하지만 간혹, ActiveX 컨트롤의 동작이나 상태, 속성을 정의하는 작업 전체가 시스템에 종속적으로 구성된 경우도 있을 수 있습니다. 이 경우에는, 격리모드에서 실행되는 ActiveX 컨트롤과는 별도로 새로운 인스턴스를 노출시켜야 할 필요가 있습니다.

 

이에 대한 솔루션은 인터넷 검색중에 발견한 어떤 블로그의 아티클에서 찾을 수 있었습니다. (http://hbesthee.tistory.com/620) 이 아티클은 델파이 기반의 솔루션을 제시하고 있었으며 저는 이것을 Visual C++ 기반의 코드로 옮겼습니다.

STDMETHODIMP CtrusEBANK::trusElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 if (IsUACRequiredOperatingSystem())
 {
  ItrusEBANK *pElevatedObj = NULL;
  HRESULT hr = CoCreateInstanceAsAdmin(NULL,
   CLSID_trusEBANK, IID_ItrusEBANK,
   (void **)&pElevatedObj);

  if (SUCCEEDED(hr))
  {
   ret->vt = VT_DISPATCH;
   ret->pdispVal = pElevatedObj;
   ret->pdispVal->AddRef();
   return S_OK;
  }
  else
  {
   ret->vt = VT_NULL;
   return hr;
  }
 }
 else
 {
  ret->vt = VT_BOOL;
  ret->boolVal = VARIANT_FALSE;
 }

 return S_OK;
}

 

STDMETHODIMP CtrusEBANK::trusNeedElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 ret->vt = VT_BOOL;

 if (::IsUACRequiredOperatingSystem())
  ret->boolVal = VARIANT_TRUE;
 else
  ret->boolVal = VARIANT_FALSE;

 return S_OK;
}

 

위의 코드에서는 trusNeedElevate를 이용하여 권한 상승이 지원되는 운영 체제인지 파악하는 부분과, trusElevate를 이용하여 권한 상승이 적용될 개체를 반환하는 방법 두 가지를 보여주고 있습니다. 그리고 아래의 코드는 자바스크립트에서의 실제 사용 예시입니다.

 

var obj = new ActiveXObject("EBANK.trusEBANK");

if (obj.trusNeedElevate())
{
 var result = obj.trusElevate();
 if (result) {
  obj = result;
 }
}

// 여기서부터 obj 객체 사용

 

이와 같은 방법을 통하여, 권한 상승이 온전하게 ActiveX 컨트롤에 적용될 수 있도록 하여, 원활한 동작 환경을 구현할 수 있을 것입니다.

 

3. Internet Explorer Host Window를 Elevation하는 방법 [업데이트]

 

그리고 최근에 저는 여러가지 방법을 적용해보던 중에 가장 현실적인 타협안 하나를 발견하였습니다. 바로, Internet Explorer Host Window 자체를 Runtime 도중에 Elevation 처리하는 방법으로, 기존의 응용프로그램 기반 매니페스트와 유사하게 작동합니다. 그리고 이 방법은, Host Window를 Elevation하는 데에 사용할 수 있는것 뿐만 아니라, 대리 실행해야 하는 응용프로그램의 Manifest 보유 여부에 관계없이 Elevation에도 사용될 수 있습니다.

 

참고로, 이 방법은 http://www.softblog.com/2008-02/vista-tools/ 에서 소개한 VistaTools.cxx 파일의 코드를 일부 발췌한 것임을 밝혀둡니다.

 

typedef struct _TOKEN_ELEVATION {
    DWORD TokenIsElevated;
} TOKEN_ELEVATION, *PTOKEN_ELEVATION;

 

위의 구조체는 Elevation 상태를 점검하기 위하여 필요한 구조체로 이미 Elevation 처리가 되어있는 호스트 위에서 실행되는 경우 Elevation을 다시 실행하지 않도록 하기 위하여 필요합니다. 이 구조체는 Visual C++ 6.0 기반에서 프로그램을 업데이트할 때 수동으로 지정해야 합니다.

 

#ifndef CSIDL_PROGRAM_FILES
  #define CSIDL_PROGRAM_FILES 0x0026
#endif // CSIDL_PROGRAM_FILES

 

위의 Special Folder Constant는 %PROGRAMFILES% 폴더의 경로를 가져오기 위하여 필요합니다. 마찬가지로 Visual C++ 6.0 기반에서 프로그램을 업데이트할 때 수동으로 지정해야 합니다.

 

#include <shellapi.h>
#include <shlobj.h>

 

마지막으로 위의 두 헤더 파일을 참조하도록 선언하면 일단 준비는 끝납니다.

 

BOOL IsVistaOrHigher(void)
{
 OSVERSIONINFO versionInfo;
 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 if (GetVersionEx(&versionInfo) &&
  versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
  versionInfo.dwMajorVersion >= 6)
  return TRUE;
 else
  return FALSE;
}

 

HRESULT IsElevated(BOOL *pElevated)
{
 HRESULT hResult = E_FAIL;
 HANDLE hToken = NULL;

 if (!IsVistaOrHigher())
  return hResult;

 if (!OpenProcessToken(
  GetCurrentProcess(),
  TOKEN_QUERY,
  &hToken))
  return hResult;

 TOKEN_ELEVATION te = { 0 };
 DWORD dwReturnLength = 0;
 const int TokenElevation = 20;

 if (GetTokenInformation(
  hToken,
  (TOKEN_INFORMATION_CLASS)TokenElevation,
  &te,
  sizeof(te),
  &dwReturnLength))
 {
  hResult = te.TokenIsElevated ? S_OK : S_FALSE;

  if (pElevated)
   *pElevated = (te.TokenIsElevated != 0);
 }

 CloseHandle(hToken);
 return hResult;
}

 

BOOL ShellExecWithVerb(HWND hWnd, LPCTSTR lpVerb, LPCTSTR lpPath, LPCTSTR lpParameters, LPCTSTR lpDirectory)
{
 SHELLEXECUTEINFO executeInfo;
 memset(&executeInfo, 0, sizeof(executeInfo));

 executeInfo.cbSize = sizeof(SHELLEXECUTEINFO);
 executeInfo.fMask = 0;
 executeInfo.hwnd = hWnd;
 executeInfo.lpVerb = lpVerb;
 executeInfo.lpFile = lpPath;
 executeInfo.lpParameters = lpParameters;
 executeInfo.lpDirectory = lpDirectory;
 executeInfo.nShow = SW_NORMAL;

 return ShellExecuteEx(&executeInfo);
}

 

BOOL ShellExecWithElevation(HWND hWnd, LPCTSTR lpPath, LPCTSTR lpParameters, LPCTSTR lpDirectory)
{
 return ShellExecWithVerb(hWnd, _T("runas"), lpPath, lpParameters, lpDirectory);
}

 

BOOL OpenUrlWithElevation(HWND hWnd, LPCTSTR lpUrl)
{
 _TCHAR lpBuffer[MAX_PATH + 1];

 if (!SHGetSpecialFolderPath(hWnd, lpBuffer, CSIDL_PROGRAM_FILES, 0))
  return FALSE;

 _tcscat(lpBuffer, _T("\\Internet Explorer\\iexplore.exe"));
 return ShellExecWithElevation(hWnd, lpBuffer, lpUrl, _T(""));
}

 

위의 코드의 내용들 중에서 강조표시된 것이 실제 Elevation을 위하여 필요한 코드입니다. 그리고 실제 사용법은 아래와 같습니다.

 

STDMETHODIMP CMyAXControl::RunElevatedWeb(VARIANT *url)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())
 USES_CONVERSION;

 CComBSTR targetUrl;
 url->vt = VT_BSTR;
 targetUrl = CComBSTR(url->bstrVal);

 _TCHAR buffer[MAX_PATH + 1];
 _tcscpy(buffer, (_TCHAR*)OLE2T(targetUrl.m_str));
 BOOL bResult = OpenUrlWithElevation(NULL, buffer);

 if (bResult)
  return S_OK;
 else
  return E_FAIL;
}

 

그리고 위의 메서드가 실제로 실행되어야 할 때와, 그렇지 않을 때를 구분하기 위하여 아래와 같이 상태 확인을 위한 메서드를 배치하는 것도 도움이 됩니다. :-)

 

STDMETHODIMP CMyAXControl::NeedElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 ret->vt = VT_I4;
 ret->intVal = 0;

 if (IsVistaOrHigher())
 {
  BOOL bResult = FALSE;

  if (SUCCEEDED(IsElevated(&bResult)))
  {
   if (bResult == TRUE)
    ret->intVal = 4; // 이미 Elevation이 완료됨
   else
    ret->intVal = 3; // Elevation이 필요함
  }
  else
   ret->intVal = 2; // 상태 정보를 조회할 수 없음
 }
 else
  ret->intVal = 1; // UAC가 지원되지 않는 운영체제로 판단함

 return S_OK;
}

 

위의 메서드를 통하여, 반환값이 1 - 또는 - 4로 반환되는 경우에 한정하여 실제로 필요한 코드를 실행하고, 그렇지 않은 경우 Elevation을 수행하도록 유도하는 코드를 웹에서 작성할 수 있을 것입니다.

 

덧) 지적하거나 수정이 필요한 부분이 있으시면 댓글로 남겨주시면 바로 반영하도록 하겠습니다. 감사합니다. :-)

 

출처: http://www.rkttu.com/





posted by 래머
prev 1 2 next