반응형

GetForegroundWindow 현재 제일 앞의 창의 핸들을 얻어온다





GetForegroundWindow 와 GetActiveWindow의 차이점은 무엇인가요?? 




MSDN을 보면 GetActiveWindow는
This function retrieves the window handle to the active window associated with the thread that calls the function.
라고 나오고


GetForegroundWindow는 
This function returns the handle to the foreground window — the window with which the user is currently working. 
라고 나오는데요

 

문자 그대로 해석하자면

전자는 활성화된 윈도우-함수를 호출한 쓰레드와 연동된 녀석-의 핸들을 받는다 이고

후자는 가장 앞의 윈도우-즉, 현재 수행중인 윈도우-의 핸들을 받는다 입니다.

 

즉, 전자에서 NULL로 나오는 이유는 함수를 호출한 쓰레드가 그 윈도우와 관계가 없기 때문이죠.

전자의 활용은 현재 내 프로그램이 실행중이고 윈도우가 많은데

그중에 어떤 윈도우가 활성화 상태인지 알고 싶을때 쓰고

후자는 어떤 프로그램이든 관계 없이 최상위 윈도우를 알고 싶을 때 쓰는게 아닐까요?

 

굳이 나누자면

전자가 NULL이 나온다면 내 프로그램 자체가 활성화 된 윈도우가 없다...라고 판단이 가능하겠죠

반응형
반응형

원형

HWND SetParent(HWND hWndChild, HWND hWndNewParent);

MFC 원형

CWnd* CWnd::SetParent( CWnd* pWndNewParent );

인수

▶hWndChild : 차일드 윈도우의 핸들

▶hWndNewParent : 새로운 부모 윈도우의 핸들. NULL이면 데스크탑 윈도우가 새로운 부모 윈도우가 된다. 메시지 전용 윈도우인 HWND_MESSAGE를 지정하면 차일드도 메시지 전용 윈도우가 된다.

리턴

이전의 부모 윈도우 핸들이 리턴되며 실패시 NULL을 리턴한다.

설명

모든 윈도우는 부모 자식 관계를 가지는데 이 함수는 부모 윈도우를 변경한다. 부모 윈도우가 변경될 경우 차일드 윈도우는 새로운 부모의 작업 영역에 다시 그려지는데 단 차일드 윈도우가 숨겨져 있을 때는 다시 그릴 필요가 없다. 새 부모 윈도우는 반드시 같은 프로그램에 속해 있어야 한다.

부모 자식 관계가 변경되더라도 WS_CHILD, WS_POPUP 스타일은 변경되지 않으므로 필요할 경우 이 스타일들을 직접 변경해 주어야 한다.

참고함수

GetParent : 현재 설정되어 있는 부모 윈도우의 핸들을 구한다.


사용자 삽입 이미지
사용자 삽입 이미지

반응형
반응형

http://www.winapi.co.kr/reference/Function/CreateWindowEx.htm

CreateWindowEx

원형

HWND CreateWindowEx(DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, LPVOID lpParam);

MFC 원형

BOOL CWnd::CreateEx( DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL );

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam = NULL);

인수

▶dwExStyle : 생성될 윈도우의 확장 스타일을 지정한다.

스타일설명
WS_EX_DLGMODALFRAME이중 경계선을 가진 윈도우를 만든다.
WS_EX_NOPARENTNOTIFY생성되거나 파괴될 때 부모 윈도우에게 WM_PARENTNOTIFY 메시지를 보내지 않도록 한다.
WS_EX_TOPMOST모든 윈도우보다 수직적으로 위에 있는 윈도우를 만든다. 비활성화된 상태에서도 다른 윈도우에 가려지지 않는다. 이 속성을 실행중에 변경할 때는 SetWindowLong 함수를 사용할 수 없으며 SetWindowPos 함수를 사용해야 한다.
WS_EX_ACCEPTFILES드래그되는 파일을 받을 수 있다. 이 속성을 지정하지 않고 윈도우를 생성했을 경우 DragAcceptFiles 함수로 실행중에 이 속성을 변경할 수 있다.
WS_EX_TRANSPARENT형제 윈도우가 다 그려지기 전에 그려지지 않아 투명하게 보이는 윈도우를 만든다.
WS_EX_MDICHILDMDI 차일드 윈도우를 만든다.
WS_EX_TOOLWINDOW플로팅 툴바 형식의 윈도우를 만든다. 타이틀 바의 높이가 보통 윈도우보다 낮으며 작은 폰트를 사용한다. 툴 윈도우는 타스크 바에 나타나지 않으며 Alt+Tab키로 전환할 수도 없다. 시스템 메뉴 아이콘은 없으나 Alt+Space키로 시스템 메뉴는 호출할 수 있다.
WS_EX_WINDOWEDGE양각 모양의 경계선을 가진 윈도우를 만든다.
WS_EX_CLIENTEDGE작업영역이 쑥 들어간 음각 모양으로 만든다.
WS_EX_CONTEXTHELP타이틀 바에 ?표 버튼을 출력한다. 이 버튼은 도움말 제공에 사용된다.
WS_EX_LAYERED2000에서 추가된 속성이며 레이어드 윈도우를 생성한다.
WS_EX_RIGHT한국어에는 적용되지 않는 스타일
WS_EX_LEFT한국어에는 적용되지 않는 스타일
WS_EX_RTLREADING한국어에는 적용되지 않는 스타일
WS_EX_LTRREADING한국어에는 적용되지 않는 스타일
WS_EX_LEFTSCROLLBAR한국어에는 적용되지 않는 스타일
WS_EX_RIGHTSCROLLBAR한국어에는 적용되지 않는 스타일
WS_EX_CONTROLPARENTTab키로 차일드 사이를 전환할 수 있도록 한다.
WS_EX_STATICEDGE사용자의 입력을 받아들이지 않는다는 의미의 삼차원 장식을 한다.
WS_EX_APPWINDOW윈도우가 보일 때 강제로 타스크 바 위에 있도록 한다.
WS_EX_OVERLAPPEDWINDOW(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)복합 속성
WS_EX_PALETTEWINDOW(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST) 복합 속성

▶나머지 인수 : CreateWindow와 동일하다.

리턴

윈도우 생성에 성공했으면 생성된 윈도우의 핸들을 리턴하며 에러 발생시 NULL을 리턴한다.

설명

윈도우를 생성하는 기능은 CreateWindow 함수와 동일하되 확장 스타일을 지정하는 dwExStyle 멤버가 있다는 점만 다르다. CreateWindow 함수는 dwExStyle이 0인 매크로 함수로 정의되어 있다.

예제 1 

다음 예제는 항상 위에 있으며 툴 윈도우 모양의 타이틀 바를 가진다. 툴 윈도우는 타이틀 바의 높이가 낮으며 타스크 바에 나타나지 않고 Alt+Tab으로 작업 전환을 할 수 없다.

hWnd=CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
	lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
	CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
	NULL,(HMENU)NULL,hInstance,NULL);

참고함수

CreateWindow

플렛폼

95이상

참조

 

반응형
반응형

http://kazeonme.springnote.com/pages/3607111

 

CTestApp, CMainFrame, CTestDoc, CTestView

 App 포인터 얻기 
-          CTestApp *pApp = (CtestApp *) AfxGetApp();


◎ CTestApp에서
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestDoc* pDoc = (CTestDoc*)((CMainFrame*)AfxGetMainWnd())->GetActiveDocument();
CTestView* pView = (CTestView*)((CMainFrame*)AfxGetMainWnd())->GetActiveView();

◎ CMainFrame에서
CTestApp* pApp = (CTestApp*)AfxGetApp();
CTestDoc* pDoc = (CTestDoc*)GetActiveDocument();
CTestView* pView = (CTestView*)GetActiveView();

◎ CTestDoc에서
CTestApp* pApp = (CTestApp*)AfxGetApp();
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CTestView* pView = (CTestView*)((CMainFrame*)AfxGetMainWnd())->GetActiveView();

◎ CTestView에서
CTestApp* pApp = (CTestApp*)AfxGetApp();
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
                  or (CMainFrame*)GetParent();
CTestDoc* pDoc = (CTestDoc*)((CMainFrame*)AfxGetMainWnd())->GetActiveDocument();
              or (CTestDoc*)GetDocument();

 

◎ 기타 클래스에서 View얻기

CTestView* pView = (CTestView*) ((CFrameWnd*)AfxGetApp()->GetMainWnd())->GetActiveView();


반응형
반응형

http://jaram.tistory.com/55
39. Window 창크기,위치정보 저장하기

  MainFrame 의 WM_DESTROY 에

    WINDOWPLACEMENT w;

    this->GetWindowPlacement(&w); //윈도우의 정보를 저장한다.

    CString strRect;

    strRect.Format("%04d,%04d,%04d,%04d", //04d 는 4자리 확보하고 남은건 0으로 채워라

         w.rcNormalPosition.left,w.rcNormalPosition.top,

         w.rcNormalPosition.right,w.rcNormalPosition.bottom); //윈도우의 위치,크기 확보..

        

        BOOL bMax,bMin; //윈도우의 상태를 저장하기위한 변수

        //w.falg 는 이전상태의 정보를 가지고 잇다!!

        if(w.showCmd==SW_SHOWMINIMIZED)           //최소화 상태

        {

                bMin=TRUE;

                if(w.flags==0) //falg 값이 0 이면 이전 상태가 보통상태이다!!

                        bMax=FALSE;

                else   //이전상태가 대화 상태

                        bMax=TRUE;

        }

        else                            

        {

                if(w.showCmd==SW_SHOWMAXIMIZED) //최대화상태

                {

                        bMax=TRUE;

                        bMin=FALSE;

                }

                else //보통 상태

                {

                        bMax=FALSE;

                        bMin=FALSE;

                }

        }

        AfxGetApp()->WriteProfileString("WinStatus","Rect",strRect);

        AfxGetApp()->WriteProfileInt("WinStatus","Max",bMax);

        AfxGetApp()->WriteProfileInt("WinStatus","Min",bMin);



//읽어올차례..

ActivateFrame 함수로 가서

        WINDOWPLACEMENT w;  //윈도우의 상태를 저장하는 구조체..

        BOOL bMax,bMin;               //최대,최소상태를 저장할 변수

        CString strRect; //창크기를 받아올 변수

        strRect=AfxGetApp()->GetProfileString("WinStatus","Rect","0000,0000,0500,0700");

        bMin=AfxGetApp()->GetProfileInt("WinStatus","Min",FALSE);

        bMax=AfxGetApp()->GetProfileInt("WinStatus","Max",FALSE);

        int a=atoi(strRect.Left(4)); //문자열을 int 로 바꿔준다.

        int b=atoi(strRect.Mid(5,4));     //atoi 아스키 값을 int형으로 바꿔준다..

        int c=atoi(strRect.Mid(10,4));

        int d=atoi(strRect.Mid(15,4));

        w.rcNormalPosition=CRect(a,b,c,d);

        if(bMin)

        {

                w.showCmd=SW_SHOWMINIMIZED;

                if(bMax)

                {

                        w.flags=WPF_RESTORETOMAXIMIZED  ;

                }

                else

                {

                        w.flags=0;

                }

        }

        else

        {

                if(bMax)

                {

                        w.showCmd=SW_SHOWMAXIMIZED;

                }

                else

                {

                        w.showCmd=SW_SHOWNORMAL;

                }

        }

        this->SetWindowPlacement(&w); //설정된 값으로 윈도우를 그리게 한다..

        

        //CFrameWnd::ActivateFrame(nCmdShow); //이건 반드시 주석처리한다..


반응형
반응형

ShowWindow

원형BOOL ShowWindow(HWND hWnd, int nCmdShow);
인수

▶hWnd : 대상 윈도우 핸들

▶nCmdShow : 지정하고자 하는 보이기 상태이며 다음 값중 하나를 지정한다.

설명
SW_FORCEMINIMIZE

2000 이후에만 쓸 수 있는 플레그이다. 윈도우를 소유한 스레드가 블록된 상태에서도 윈도우를 최소화시킨다.

SW_HIDE

윈도우를 숨긴다.

SW_MAXIMIZE

윈도우를 최대화시킨다.

SW_MINIMIZE

윈도우를 최소화시키며 다음 Z순서를 가지는 윈도우가 활성화된다.

SW_RESTORE

최대, 최소화된 윈도우를 이전 위치로 복구한다.

SW_SHOW

윈도우를 활성화하며 보인다.

SW_SHOWDEFAULT

STARTUPINFO 구조체가 지정하는 보이기 상태로 만든다.

SW_SHOWMAXIMIZED

윈도우를 최대화된 상태로 활성화한다.

SW_SHOWMINIMIZED

윈도우를 최소화한 상태로 활성화한다.

SW_SHOWMINNOACTIVE

윈도우를 최소화 상태로 보이며 활성화 상태는 변경되지 않는다.

SW_SHOWNA

윈도우를 헌재 상태로 보이며 활성화 상태는 변경되지 않는다.

SW_SHOWNOACTIVATE

최근 크기와 위치에 윈도우를 보이며 활성화 상태는 변경되지 않는다.

SW_SHOWNORMAL

윈도우를 보이며 활성화한다. 만약 윈도우가 최소화되어 있거나 최대화되어 있다면 윈도우를 원래 크기대로 복구한다. 윈도우를 처음 화면에 보일 때는 이 플래그를 사용해야 한다.

 

리턴윈도우가 이전에 보이는 상태였으면 0이 아닌 값을 리턴하며 보이지 않는 상태였으면 0을 리턴한다.
설명

윈도우의 보이기 상태를 지정한다. 보이기 상태(show state)란 보이기/숨기기는 물론이고 최대화/최소화/복구 상태 등 윈도우의 현재 상태를 통칭하는 용어이며 nCmdShow 인수가 지정하는 여러 가지 보이기 상태 중 하나를 선택할 수 있다.

단, 이 함수가 처음 호출될 때는 반드시 WinMain의 인수로 전달된 nCmdShow를 그대로 넘겨 주는 것이 좋다. 쉘은 프로그램을 실행할 때 사용자가 지정해 놓은 등록 정보를 WinMain으로 전달해 주는데 이 설정 상태대로 보여야 하기 때문이다. 만약 이 프로그램이 STARTUPINFO 구조체의 정보대로 생성되었다면 첫번째 ShowWindow 호출에서 nCmdShow 인수 지정은 무시되며 이 구조체가 지정하는 보이기 상태가 적용된다. 두번째 호출부터는 원하는 보이기 상태로 변경할 수 있다.

참고함수CreateWindow, ShowOwnedPopups
플렛폼95이상
본문참조 

written by http://www.winapi.co.kr

반응형
반응형




http://microdev.tistory.com/14

작업표시줄에 현재 윈도우의 캡션(?)을 안나오게 하는 방법....

윈도우가 초기화 될때 아래의 코드를 쓰면 장땡!!

ex) CDialog::OnInitDialog()

1
2
3
4
DWORD dwStyle = GetWindowLong( HWND, GWL_EXSTYLE );
dwStyle &= ~WS_EX_APPWINDOW;
dwStyle |= WS_EX_TOOLWINDOW;
SetWindowLong( HWND, GWL_EXSTYLE, dwStyle );    

반응형
반응형

http://www.winapi.co.kr


SetWindowPos

원형

BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);

인수

▶ hWnd : 이동 대상 윈도우의 핸들

▶ hWndInsertAfter : 윈도우의 Z순서를 지정한다. 이 인수가 지정하는 윈도우 앞에 hWnd가 배치되는데 단 hWnd가 액티브 상태일 경우 이 인수가 지정하는 윈도우 핸들은 무시된다. 또는 다음과 같이 Z순서를 지정하는 값들이 올 수 있다.

설명
HWND_BOTTOM

hWnd윈도우를 Z순서의 제일 바닥으로 보낸다. 만약 이 윈도우가 항상 위(Top Most)속성을 가지고 있었다면 이 속성은 해제되고 모든 윈도우의 제일 아래쪽에 배치된다.

HWND_NOTOPMOST

항상 위 속성을 가지지 않은 윈도우 중 제일 위쪽에 배치된다. 즉, 항상 위 속성을 가진 윈도우 바로 아래에 배치된다. 이 옵션은 항상 위 속성을 해제할 때 사용된다. 만약 항상 위 옵션을 가지고 있지 않다면 이 옵션은 아무런 효과도 가지지 않는다.

HWND_TOPMOST

항상 위 속성을 설정한다. 항상 위 속성을 가지지 않는 윈도우들보다 위쪽에 배치된다.

HWND_TOP

Z순서상의 제일 위쪽에 배치한다.

▶X,Y : 윈도우의 새로운 좌상단 좌표를 지정한다. 픽셀 단위이다.

▶cx, cy : 윈도우의 새로운 폭과 높이를 지정한다. 픽셀 단위이다.

▶uFlags : 위치와 크기 변경에 대한 여러 가지 옵션들이며 플래그들을 조합하여 지정할 수 있다.

플래그설명
SWP_ASYNCWINDOWPOS

이 함수를 부른 스레드와 윈도우를 소유한 스레드가 다른 입력 큐를 사용할 경우 시스템은 윈도우를 소유한 스레드에게 요구를 포스팅하기만 한다. 이는 호출 스레드가 다른 스레드가 요구를 처리하는 동안 블럭되는 것을 방지한다.

SWP_DEFERERASE

WM_SYNCPAINT 메시지 발생을 금지한다.

SWP_DRAWFRAME

윈도우 주변에 프레임을 그린다.

SWP_FRAMECHANGED

SetWindowLong으로 경계선 스타일을 변경했을 경우 새 스타일을 적용한다. 이 플래그가 지정되면 크기가 변경되지 않아도 WM_NCCALCSIZE 메시지가 전달된다.

SWP_HIDEWINDOW

윈도우를 숨긴다. 이 경우 이동과 크기 변경은 무시된다.

SWP_NOACTIVATE

크기 변경 후 윈도우를 활성화시키지 않는다.

SWP_NOCOPYBITS

이 플래그가 지정되지 않으면 작업영역의 내용이 저장되었다가 크기나 위치변경 후 다시 작업영역으로 복사된다. 이 플래그가 지정되면 이런 저장을 하지 않는다.

SWP_NOMOVE

위치는 이동하지 않고 크기만 변경한다. X,Y인수가 무시된다.

SWP_NOOWNERZORDER

소유자의 Z순서를 변경하지 않는다.

SWP_NOREDRAW

크기, 위치를 바꾼 후 그리기를 하지 않는다. 해당 윈도우는 물론이고 이 윈도우에 의해 다시 드러나는 윈도우도 다시 그리기를 하지 않는다. 이 플래그를 주었을 경우 프로그램은 필요한 부분을 즉시 무효화시켜 다시 그리도록 해 주어야 한다.

SWP_NOREPOSITION

=SWP_NOOWNERZORDER

SWP_NOSENDCHANGING

윈도우에게 WM_WINDOWPOSCHANGING 메시지를 보내지 않는다.

SWP_NOSIZE

크기는 변경하지 않고 위치만 이동한다. cx, cy 인수가 무시된다.

SWP_NOZORDER

현재의 Z순서를 그대로 유지한다. hWndInsertAfter 인수를 무시한다.

SWP_SHOWWINDOW

윈도우를 보인다. 이 경우 이동과 크기 변경은 무시된다.

 

리턴

성공하면 0이 아닌 값을 리턴하며 에러 발생시 0을 리턴한다.

설명

이 함수는 윈도우의 위치, 크기, Z순서를 동시에 또는 일부만 변경할 때 사용된다. 예를 들어 크기는 그대로 두고 위치만 변경하고자 한다거나 위치와 크기는 그대로 두고 Z순서만 변경하고자 할 때 사용한다. MoveWindow 함수는 크기와 위치를 항상 같이 변경하지만 이 함수는 SWP_NOSIZE, SWP_NOMOVE 플래그로 위치와 크기를 개별적으로 변경할 수 있다.

또한 이 함수는 Z순서를 변경하기 위한 목적으로, 특히 항상 위(Top Most) 속성을 토글하기 위한 용도로도 많이 사용되는데 두번째 인수에 HWND_(NO)TOPMOST를 줌으로써 이 속성을 토글할 수 있다. 이 함수로 항상 위 속성을 설정하면 이 윈도우에 소유된 윈도우도 항상 위 속성을 같이 가지게 된다. 그러나 이 윈도우를 소유한 윈도우는 영향을 받지 않는다. 반대로 이 함수로 항상 위 속성을 해제하면 이 윈도우에 소유된 윈도우와 이 윈도우를 소유한 윈도우의 항상 위 속성이 모두 해제된다.

일반적으로 항상 위 속성을 가지지 않은 윈도우가 항상 위 속성을 가진 윈도우를 소유할 수는 있지만 반대는 불가능하다. 왜냐하면 소유된 윈도우는 소유한 윈도우보다 Z순서의 위쪽에 있어야 하므로 소유한 윈도우만 항상 위 옵션을 가질 수는 없기 때문이다. 이렇게 되면 항상 위 옵션을 가지는 윈도우의 차일드로 열려 있는 대화상자가 밑으로 숨어 버리는 현상이 발생할 수 있다. SetWindowPos 함수는 이 모든 처리를 다 해 주므로 항상 위 스타일을 토글 할 때는 SetWindowLong으로 SWL_EXSTYLE을 조작하지 말고 반드시 이 함수를 사용해야 한다.

예제 1 

다음 예제는 SetWindowPos 함수로 윈도우의 위치만 변경하는 방법과 항상 위 스타일을 토글하는 방법을 보여준다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	char Mes[]="왼쪽=위치 변경, 오른쪽=항상 위 옵션 변경";
	static BOOL bTopMost=FALSE;

	switch(iMessage) {
	case WM_LBUTTONDOWN:
		SetWindowPos(hWnd, HWND_NOTOPMOST,
			rand()%640, rand()%480, 0, 0, SWP_NOSIZE);
		return 0;
	case WM_RBUTTONDOWN:
		if (bTopMost) {
			SetWindowPos(hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
		} else {
			SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
		}
		bTopMost=~bTopMost;
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd, &ps);
		TextOut(hdc,10,10,Mes,lstrlen(Mes));
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

왼쪽 버튼을 누르면 난수로 정한 새 위치로 윈도우를 옮기되 SWP_NOSIZE 플래그를 주어 크기는 변경하지 않도록 하였다. 오른쪽 버튼을 누르면 항상 위 옵션을 토글시킨다.

참고함수

MoveWindow : 윈도우의 위치와 크기를 동시에 변경한다.

플렛폼

95이상

본문참조

 

반응형
반응형

//return CView::PreCreateWindow(cs);


 CView::PreCreateWindow(cs);

 if(!CWnd::PreCreateWindow(cs))
  return FALSE;

 HINSTANCE hInst=AfxGetInstanceHandle();
 WNDCLASS wc;

 ::GetClassInfo(hInst, cs.lpszClass, &wc);
 wc.lpszClassName=cs.lpszClass=WINDOW_CLASSNAME;

 return AfxRegisterClass(&wc);

반응형
반응형

http://blog.naver.com/vane77/30005554405


첨부파일 (1)

SwapChains을 이용한 다중 분할 창

 


 
 
 

swapchain을 사용할 개수 만큼 HWND인 윈도우 핸들을 선언 한다.
HWND g_hwnd = NULL;
HWND g_hwnd2 = NULL;
 
LPDIRECT3DSWAPCHAIN9 chain1 = NULL;
LPDIRECT3DSWAPCHAIN9 chain2 = NULL;
 

  
WinMain()
각각의 핸들은 CreateWindowEX 팝업창을 생성하고 등록 절차를 마친다.
g_hwnd  = CreateWindowEx( NULL,WINDOW_CLASSNAME, NULL,
                             WINDOW_STYLE2,   0,  10, 640, 480, NULL, NULL, hInstance, NULL);       
g_hwnd2 = CreateWindowEx( NULL,WINDOW_CLASSNAME, NULL,
                             WINDOW_STYLE2, 639,  10, 640, 480, NULL, NULL, hInstance, NULL);
 
ShowWindow(g_hwnd, SW_SHOW);  
UpdateWindow(g_hwnd);         
ShowWindow(g_hwnd2, SW_SHOW); 
UpdateWindow(g_hwnd2);

  
Init DirectX
DirectX객체 생성후 Device 생성 시점에 대표(?)가 되는 핸들을 이용하여 생성한다.
GetSwapChain으로 지정된 스왑체인을 CreateAdditionalSwapChain()으로 다른창 내용에
들어갈 chain2에 copy 한다.
 
Direct3D_Object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,   
                              VertexProcessing,  &Present_Parameters, &D3D_Device)))
 
  // We create the first chain by simple calling GetSwapChain to get the default.
  // Then we call CreateAdditionalSwapChain to create the second window's chain.
  D3D_Device->GetSwapChain(0, &chain1);
  D3D_Device->CreateAdditionalSwapChain(&Present_Parameters, &chain2);
 
Render()를 호출 하기전에 각각의 분할창에 대해 스홥 체인을 바꿔가면서 렌더링을 걸어준다.  
  
  Render1()
    chain1->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
    D3D_Device->SetRenderTarget(0, pBackBuffer);
 
             BegineScene()
                      :
             EndScene()
 
    chain1->Present(NULL, NULL, g_hwnd, NULL, 0);
 
  Render2()
    chain2->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
    D3D_Device->SetRenderTarget(0, pBackBuffer);
 
             BegineScene()
                      :
             EndScene()
 
    chain2->Present(NULL, NULL, g_hwnd2, NULL, 0);
 
다중 분할창을 구현 하는 방법에 대해서 간단히 적어 보았습니다.
(참고로 제가 변경한 소스내용은 지저분합니다.)
아래 사이트 주소를 남겨 놓았으니 원본 소스를 참고하십시오.
DirectX Tutorial이 잘 되어 있습니다.
 
 
References
 
 



http://www.cyworld.com/mustx/417




 

맵툴 만들다가... 다이얼로그에 미니맵을 띄우면 좋을 것 같아서;;

 

처음에는 다이얼로그를 폼뷰로 만들어 보고, 그냥 폼뷰를 붙일 수 있을까도 생각해 봤는데...

;; 그럴 필요가 없더군요.

 

일단 스왑체인 포인터를 준비해 두고..

 

LPDIRECT3DSWAPCHAIN m_pSwapMain;

LPDIRECT3DSWAPCHAIN m_pSwapMini;

 

그런데 한가지, 같은 창 내의 것이라면 구지 스왑체인 (분할 윈도우...)을 쓰지 않아도 되는 듯 합니다. 정확히 기억 나지 않지만... 아니라면 테클 환영;;;

 

메인용 디바이스 생성 파라매터는 CDirect3D라는 클래스로 일단 초기화...

이제 스왑 채인을 생성?? 해야 겠습니다.

 

 D3DDISPLAYMODE d3ddm;

//일단 기본 정보를 CDirect3D클래스에서 뺴 돌립니다.
 if(FAILED(m_pDirect3d->m_pD3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm))) 
 {
        return false;
 }

//출력 파라메터에서 중요한 것이, 윈도우지정 부분입니다.

//첫번째 스왑 체인은 메인윈도우 디바이스에서 단순히 스왑체인을 얻어 오는 것이지만

//2번째는 생성? 개념으로 봐야 겠네요.. 그래서 출력할 다이얼로그의 윈도우 핸들 값을 넘겨 줍

//니다.
 D3DPRESENT_PARAMETERS d3dpp; 
 ZeroMemory(&d3dpp, sizeof(d3dpp));
 d3dpp.Windowed = true;
 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
 d3dpp.BackBufferFormat = d3ddm.Format;
 d3dpp.EnableAutoDepthStencil = true;
 d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
 d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
 d3dpp.hDeviceWindow = m_pDlgMini->GetSafeHwnd();

 

//그 다음으로 디바이스에서 매인 스왑체인을 얻어오고, 거기에 추가 스왑체인?? 을

//생성합니다. 이때 넘겨줄 첫번째 인자로 위에서 지정한 present parameter가 됩니다.

 m_pDirect3d->GetDevice()->GetSwapChain(0, &m_pSwap1);
 m_pDirect3d->GetDevice()->CreateAdditionalSwapChain(&d3dpp, &m_pSwap2);

 

이제 Render부분 입니다.

여기서 한가지, 메인이라고 해서 그냥 Device를 이용해서 그리기를 하려고 하면;; 안 그려 지더군요... 그래서 보통 스왑체인 포인터를 2개 선언한것도 메인용과, 그에 따른 애디션 스왑체인을 생성하기 위한 것 같습니다.

 

//첫번째로 최종 렌더된 이미지가 뿌려질 버퍼를 얻어 와야 합니다.

 LPDIRECT3DSURFACE9 pBackBuffer = NULL;       

//스왑 체인에서 해당 백 버퍼를 얻어 옵니다.
  m_pSwap1->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );

//그리고 디바이스에서 최종적으로 뿌리게 될 타겟 버퍼를 아까 스왑 체인에서 얻어온 표면으로

//지정 합니다.
  m_pD3ddev->SetRenderTarget( 0, pBackBuffer );


  m_pD3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xAAAAAAff, 1.0f, 0L);
  m_pD3ddev->BeginScene();

 // 드로우~~~!!

 m_pD3ddev->EndScene();
  

//그리기가 끝나면 최종적으로 해당 표면에 뿌리는데, 이때 일반 디바이스의 Present함수의

//인자와 틀린것이 최종적으로 뿌리게 될 윈도우를 지정 하여야 한 다는 것입니다.
  m_pSwap1->Present( NULL, NULL, m_mainWnd, NULL, 0 );

 

이제 미니맵으로 쓰이게 될 부분인데, 똑 같습니다;;;

m_pSwapMain을 m_pSwapMini로 바꿔주고, Present에서 타겟 윈도우(여기서는 다이얼로그)핸들을 지정해 주면 되는 것입니다.


 m_mainWnd = AfxGetMainWnd()->GetSafeHwnd();
 m_miniWnd = m_pDlgMini->GetSafeHwnd();

 

출처 블로그 > 별 헤는 밤 비는 내리고
원본 http://blog.naver.com/preknife/26829222

 

 




반응형
반응형

http://www.viper.pe.kr/cgi-bin/moin.cgi/MFC_%EB%A5%BC_%EC%9D%B4%EC%9A%A9%ED%95%9C_Direct3D_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D#head-67b3c40633d83bbb112a1d972088a12b35a7fd35

 

1. 소개 [Bottom] [Top]

Direct3D 는 게임에서 주로 많이 사용되고 있으며 MFC 는 응용 프로그램 개발에 많이 사용되고 있다. 이렇게 사용 목적이 전혀 다른 두 가지를 게임 개발에 이용하는 것은 부적합하다. 하지만 게임 자체를 개발하는 것에는 부적합할지 모르나 개발에 필요한 보조 프로그램을 개발하는데는 가장 적합하지 않을까?

아직도 많은 사람들은 MFC 는 덩치가 크고 느리다고 한다. 그러나 그것은 펜티엄 100MHz 를 사용하던 과거의 이야기일뿐, 지금과 같이 거의 모든 CPU 가 GHz 를 넘어가는 경우엔 전혀 다르다. 메모리나 저장매체 또한 GByte 단위를 넘어가고 있는 현시점에서 전혀 영향을 줄 수 없는 것들이다. 그리고 WINAPI 보다 편리한 인터페이스와 프레임워크를 제공하고 있으므로 프로그래밍 속도 또한 빠르다.

MFC 를 이용한 Direct3D 프로그래밍은 게임 프로그램 보다는 보조 프로그램을 개발하는 것이 목적이다. 그리고 Direct3D 프로그래밍을 처음 시작할 때도 유용하다. 우선 Direct3D 를 구동하기 위한 프레임워크 개발이 필요 없으며 여러가지 데이터를 입력하거나 테스트할 경우에는 손쉽게 인터페이스를 만들고 수정할 수 있다. MFC 를 이용한 Direct3D 프로그래밍를 시작하게 된 이유도 편리한 인터페이스 프로그래밍 때문이다.

참고로 Microsoft Visual Studio 2005 를 기준으로 정리한다.

2. 참고도서 [Bottom] [Top]

  • IT EXPERT : 3D 게임 프로그래밍
    • 김용준 저, 한빛 미디어
    • ISBN: 8979142536

    • Direct3D 의 Framework 에 대하여 간략히 소개하고 있음 ( p.142 ~ 158 )

3. 참고링크 [Bottom] [Top]

4. MFC 프로젝트 만들기 [Bottom] [Top]

MFC 프레임워크는 다양한 형태를 제공한다. 기본적으로는 다중 문서 (MDI) 와 문서/뷰 (Doc/View) 구조가 선택되어 있지만, Direct3D 프로그래밍에서는 단일 문서 (SDI) 구조를 사용할 것이다. 그리고 문서/뷰 (Doc/View) 구조는 불필요하기 때문에 단일 뷰 구조만 사용한다. 문서/뷰 구조를 사용하고 싶다면 참고링크 에 소개된 글들을 참고한다.

4.1. 새 프로젝트 만들기 [Bottom] [Top]

  1. Visual Studio 의 새 프로젝트 마법사를 열고, 아래 그림과 같이 MFC 응용 프로그램 을 선택한 후 프로젝트 이름을 입력하고 확인을 클릭한다.

    • 예) 프로젝트 이름: MFC4Direct3D

  2. MFC 응용 프로그램 마법사 가 열리면 다음과 같이 선택한다.

    • 단일 문서 를 선택한다.

    • 문서/뷰 아키텍처 지원 은 체크하지 않는다(단일 뷰 구조).

    • 참고> 유니코드를 사용하지 않는 경우, 유니코드 라이브러리 사용 을 체크하지 않는다.

  3. MFC 응용 프로그램 마법사 의 나머지 과정은 기본값을 사용한다.

  4. 새 프로젝트 만들기 끝나면 다음과 같이 구조가 될 것이다.

4.2. 라이브러리 링크 시키기 [Bottom] [Top]

새로운 프로젝트가 만들어졌다면, Direct3D 를 사용할 수 있도록 Direct3D 라이브러리를 추가한다. 라이브러리 추가는 프로젝트 속성에서 추가하거나 컴파일 지시어를 사용한다.

  • D3D9.lib

  • D3DX9.lib
  • Winmm.lib

4.2.1. 방법1: 프로젝트 속성에서 라이브러리 추가 [Bottom] [Top]

4.2.2. 방법2: 컴파일 지시어를 사용하여 라이브러리 추가 [Bottom] [Top]

MFC 응용 프로그램 프로젝트는 미리 컴파일된 헤더 (Precompiled Header) 를 사용하므로 StdAfx.h 헤더 파일에 다음과 같이 추가하면 된다.

  • function isnumbered(obj) { return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber'; } function nformat(num,chrs,add) { var nlen = Math.max(0,chrs-(''+num).length), res = ''; while (nlen>0) { res += ' '; nlen-- } return res+num+add; } function addnumber(did, nstart, nstep) { var c = document.getElementById(did), l = c.firstChild, n = 1; if (!isnumbered(c)) if (typeof nstart == 'undefined') nstart = 1; if (typeof nstep == 'undefined') nstep = 1; n = nstart; while (l != null) { if (l.tagName == 'SPAN') { var s = document.createElement('SPAN'); s.className = 'LineNumber' s.appendChild(document.createTextNode(nformat(n,4,' '))); n += nstep; if (l.childNodes.length) l.insertBefore(s, l.firstChild) else l.appendChild(s) } l = l.nextSibling; } return false; } function remnumber(did) { var c = document.getElementById(did), l = c.firstChild; if (isnumbered(c)) while (l != null) { if (l.tagName == 'SPAN' && l.firstChild.className == 'LineNumber') l.removeChild(l.firstChild); l = l.nextSibling; } return false; } function togglenumber(did, nstart, nstep) { var c = document.getElementById(did); if (isnumbered(c)) { remnumber(did); } else { addnumber(did,nstart,nstep); } return false; } document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
    #pragma comment( lib, "D3D9" )
    #pragma comment( lib, "D3DX9" )
    #pragma comment( lib, "Winmm" )
    

5. 프로그램 구조 이해하기 [Bottom] [Top]

기본적인 프로젝트 설정이 끝났다면 구현에 앞서 다음의 그림을 보면서 앞으로 만들게 프로그램에 대하여 대략적인 구조를 파악해 보자.

여러개의 추가적인 클래스와 더 많은 멤버 함수들이 있지만 여기서는 프로그램 초기화, 렌더링, 마무리 작업의 주축을 이루는 부분에 대해서만 정리하였다.

아래에서 설명하겠지만 CChildView 클래스는 C3DBase 클래스를 상속 받은 클래스로 UpdateFrame() 과 Render() 함수는 C3DBase 클래스에 정의된 가상 함수 (Virtual Function) 를 재정의 (Overriding) 한 것이다.

그럼, 프로그램 초기화, 렌더링, 마무리 작업에 대하여 시퀸스 다이어그램을 보면서 처리 과정을 이해해 보자.

5.1. 초기화 과정 [Bottom] [Top]

일반적인 MFC 응용 프로그램 초기화 과정에 3D 장치 초기화/설정 및 3D 객체 생성 과정을 추가한 것으로 CChildView::InitStartup() 함수에서 필요한 초기화 과정을 추가하면 된다.

예를 들어 카메라 및 조명을 초기화 하거나, 격자 (Grid) 객체를 생성한다.

5.2. 렌더링 과정 [Bottom] [Top]

  • 기본 렌더링 과정
    • 루프를 통한 렌더링 과정 수행

  • 화면 갱신에 의한 렌더링 과정
    • 창의 전환이나 크기 조절, 이동 시 발생하는 화면 갱신에 대하여 렌더링 과정 수행

5.3. 마무리 과정 [Bottom] [Top]

프로그램 종료 시 3D 장치 해제와 3D 객체 제거 과정을 수행한다. CChildView::FinalCleanup() 함수에서 필요한 마무리 과정을 추가하면 된다.

예를 들어 CChildView::InitStartup() 함수에서 생성된 객체를 제거한다.

6. Direct3D 에 맞게 MFC 프로그래밍하기 [Bottom] [Top]

먼저, 앞서 그림에서 CMFC4Direct3DAppCMainFrameCChildView 그리고 C3DBase 로 4개의 주요 클래스를 볼 수 있다. 이 중에서 CMFC4Direct3DApp 클래스는 프로젝트에 따라 다른 이름으로 생성될 수도 있으며, 각 클래스는 개조하는 과정에서 자세히 설명할 것이다.

6.1. C3DBase 클래스 만들기 [Bottom] [Top]

C3DBase 클래스는 CChildView 클래스가 상속하게 될 Base 클래스로 주요 기능은 다음과 같다.

  • Direct3D 인터페이스와 장치를 얻어서 Direct3D 를 사용할 수 있도록 초기화 한다.
  • Direct3D 인터페이스와 장치를 해제하여 Direct3D 를 종료한다.
  • 3D 렌더링 과정을 수행한다.

6.1.1. 멤버 함수 [Bottom] [Top]

  • document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
    HRESULT CreateDirect3D( HWND hWnd, UINT nPresentationInterval );
    void    ReleaseDirect3D();
    void    ResetDirect3D( int nWidth, int nHeight );
    void    RenderDirect3D();
    
    virtual void    UpdateFrame() = 0;
    virtual void    Render() = 0;
    
    void    Pause( BOOL bPause=TRUE );
    
  • CreateDirect3D() 함수
    • Direct3D 장치를 생성한다.

  • ReleaseDirect3D() 함수
    • Direct3D 장치를 해제한다.

  • ResetDirect3D() 함수
    • 화면 (View) 의 크기가 바뀔때 Direct3D 장치를 재설정한다.

  • RenderDirect3D() 함수
    • 렌더링 과정을 수행한다. 내부에서 UpdateFrame() 과 Render() 함수를 호출한다.

  • UpdateFrame() 함수 (가상 함수)

    • 상속 받은 클래스에서 재정의 해야하며 프레임 갱신 처리를 한다.

  • Render() 함수 (가상 함수)

    • 상속 받은 클래스에서 재정의 해야하며 렌더링 처리를 한다.

  • Pause() 함수
    • 다른 창으로 포커스가 이동 시 CPU 점유율을 떨어뜨리기 위해서 사용한다.

6.2. CChildView 클래스 개조하기 [Bottom] [Top]

CChildView 클래스는 C3DBase 클래스를 상속받은 클래스로 실제 3D 렌더링을 수행한다. 또한 Direct3D 의 초기화 및 해제가 실제로 처리되는 곳이다. 즉, 3D 렌더링의 핵심 클래스가 된다.

  • PreCreateWindow() 함수 수정

    • 아래의 코드 중 AfxRegisterWndClass() 함수의 3번째 매개변수 값을 NULL 로 설정한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
       2 {
       3         if (!CWnd::PreCreateWindow(cs))
       4                 return FALSE;
       5 
       6         cs.dwExStyle |= WS_EX_CLIENTEDGE;
       7         cs.style &= ~WS_BORDER;
       8         cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
       9                 ::LoadCursor(NULL, IDC_ARROW), NULL, NULL);
      10 
      11         return TRUE;
      12 }
    
    • 참고> AfxRegisterWndClass() 함수의 3번째 매개변수는 Background Brush 를 설정하는 곳으로 NULL 로 설정하게 되면 프레임마다 화면이 지워지지 않는다. 즉, 화면이 깜박거리는 현상 (Flicker 현상) 을 방지한다.

  • OnPaint() 함수 수정

    • 위에서 설명했듯이 창 변화에 따른 화면 갱신 과정으로 렌더링 과정을 수행한다.
    • WM_PAINT 메세지 핸들러.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CChildView::OnPaint()
       2 {
       3         CPaintDC dc(this);      // 삭제하면 안됨.
       4 
       5         if( IS_NOT_NULL( g_pD3DDevice ) )
       6         {
       7                 // 렌더링 수행
       8                 RenderDirect3D();
       9         }
      10 }
    
    • 참고> CPaintDC dc(this); 코드는 삭제하지 말것. 삭제하게 되면 화면 갱신 시 창의 프레임 갱신이 느려지게 된다.

  • InitStartup() 함수 추가

    • 초기화 과정에 필요한 작업을 여기에 추가한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 HRESULT CChildView::InitStartup()
       2 {
       3         //----------------------------------------------------------------------
       4         // TODO: 초기화 처리를 수행한다.
       5         //      * 배경색 설정
       6         //      * 카메라 설정
       7         //      * 조명 설정
       8         //      * 렌더링 객체 생성 등
       9         //
      10 
      11         // 배경색 설정
      12         SetBackColor( 0x40, 0x40, 0x40 );
      13 
      14         // 카메라 설정
      15         m_pCamera = new CCamera();
      16         InitCamera();
      17 
      18         // 조명 설정
      19         InitLight();
      20         LightEnable( FALSE );           // 조명 비활성화
      21 
      22         // 격자 생성
      23         m_grid.Create( 20, 4, 20 );
      24 
      25         return S_OK;
      26 }
    
  • FinalCleanup() 추가

    • 초기화 과정이나 실행 과정에서 생성된 3D 객체를 여기서 제거한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CChildView::FinalCleanup()
       2 {
       3         //----------------------------------------------------------------------
       4         // TODO: 마무리 처리를 수행한다.
       5         //      * 초기화 또는 수행 중 생성된 3D 객체 해제 등
       6 
       7         // 격자 제거
       8         m_grid.Release();
       9 
      10         // 카메라 제거
      11         SAFE_DELETE( m_pCamera );
      12 
      13         // Direct 3D 해제
      14         ReleaseDirect3D();
      15 }
    
  • UpdateFrame() 함수 재정의 (C3DBase 가상 함수)

    • 실제 프레임 갱신 작업을 여기에 추가한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CChildView::UpdateFrame()
       2 {
       3         if( IS_NOT_NULL( m_pCamera ) )
       4         {
       5                 // 카메라 설정
       6                 m_pCamera->SetCamera();
       7         }
       8 
       9         //--------------------------------------------------------------------------
      10         // TODO: 프레임 갱신 처리를 수행한다.
      11         //
      12 }
    
  • Render() 함수 재정의 (C3DBase 가상 함수)
    • 실제 렌더링 작업을 여기에 추가한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CChildView::Render()
       2 {
       3         // 격자 렌더링
       4         m_grid.Render();
       5 
       6         //--------------------------------------------------------------------------
       7         // TODO: 3D 오브젝트를 렌더링한다.
       8         //
       9 }
    
  • WM_SIZE 메세지 핸들러 추가
    • 창의 크기가 바뀔 때 Direct3D 장치를 재설정한다.
    • 이 과정이 생략되면 창의 크기가 바뀔 시 3D 화면이 찌그러지거나 도트가 뭉게지는 현상이 발생한다.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CChildView::OnSize(UINT nType, int cx, int cy)
       2 {
       3         __super::OnSize(nType, cx, cy);
       4 
       5         if( IS_NOT_NULL( g_pD3DDevice ) )
       6         {
       7                 // Direct3D 재설정
       8                 ResetDirect3D( cx, cy );
       9 
      10                 // 조명 설정
      11                 InitLight();
      12                 LightEnable( FALSE );           // 조명 비활성화
      13         }
      14 }
    

6.3. CMainFrame 클래스 개조하기 [Bottom] [Top]

CMainFrame 클래스는 MFC 프레임워크에서 창의 기본 골격을 이루는 클래스로 창을 열거나 닫기, 메뉴, 툴바, 상태바 등을 관리하며 주로 사용자와의 인터페이스 처리를 담당하게 된다. 따라서 3D 렌더링에는 별로 중요하지 않다.

  • InitDirect3D() 함수 추가
    • 프로그램 시작 시 3D 초기화 과정을 수행한다.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 HRESULT CMainFrame::InitDirect3D()
       2 {
       3         // View 영역 크기 설정
       4         SetSize( 800, 600 );
       5 
       6         // Direct 3D 초기화
       7         m_wndView.CreateDirect3D( m_wndView.m_hWnd, D3DPRESENT_INTERVAL_IMMEDIATE );
       8 
       9         // 사용자 초기화 수행
      10         m_wndView.InitStartup();
      11 
      12         return S_OK;
      13 }
    
  • SetSize() 함수 추가

    • 프로그램 시작 시 3D 화면의 크기를 설정하는 곳으로 3D 화면에 맞게 창의 프레임 크기를 재계산하고 창 크기를 변경한다.
    • 매개변수는 실제 3D 화면의 크기를 설정한다.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CMainFrame::SetSize( int nWidth, int nHeight )
       2 {
       3         RECT client, frame;
       4 
       5         GetWindowRect( &frame );
       6         m_wndView.GetClientRect( &client );
       7 
       8         // View 영역 크기에 맞게 창크기 변경
       9         frame.right  = nWidth  + frame.right  - client.right;
      10         frame.bottom = nHeight + frame.bottom - client.bottom;
      11 
      12         MoveWindow( &frame );
      13 }
    
  • DestroyWindow() 함수 재정의 (CWnd 가상 함수)

    • 프로그램 종료 과정의 시작점으로 마무리 과정을 수행한다.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 BOOL CMainFrame::DestroyWindow()
       2 {
       3         // 마무리 작업
       4         m_wndView.FinalCleanup();
       5 
       6         return CFrameWnd::DestroyWindow();
       7 }
    
  • WM_ACTIVATE 메세지 핸들러 추가
    • 창의 포커스 이동 유무에 따라 3D 렌더링 속도를 조절한다. 즉, CPU 점유율을 조절한다.
    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
       2 {
       3         CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
       4 
       5         m_wndView.Pause( nState == WA_INACTIVE );
       6 }
    

6.4. CMFC4Direct3DApp 클래스 개조하기 [Bottom] [Top]

CMFC4Direct3DApp 클래스는 가장 먼저 실행되므로 실행 순서상으로 본다면 최상위에 위치한다고 볼 수 있다. 주요 기능은 다른 객체들을 생성하고 실행시키는 역활을 한다. 그리고 가장 중요한 기능은 CChildView 클래스에게 3D 렌더링하도록 RenderDirect3D() 함수를 호출한다.

  • InitInstance() 함수 수정

    • 프로그램 초기화 과정으로 MFC 응용 프로그램 마법사 에서 생성된 코드에 3D 초기화 과정만 추가한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 BOOL CMFC4Direct3DApp::InitInstance()
       2 {
       3         ...     // 생략
       4 
       5         // 창 하나만 초기화되었으므로 이를 표시하고 업데이트합니다.
       6         pFrame->ShowWindow(SW_SHOW);
       7         pFrame->UpdateWindow();
       8         // 접미사가 있을 경우에만 DragAcceptFiles를 호출합니다.
       9         // SDI 응용 프로그램에서는 ProcessShellCommand 후에 이러한 호출이 발생해야 합니다.
      10 
      11         // 초기화 수행
      12         pFrame->InitDirect3D();
      13 
      14         return TRUE;
      15 }
    
  • OnIdle() 함수 재정의 (CWinApp 가상 함수)

    • 반복 루프로 CChildView::RenderDirect3D() 함수를 호출한다.

    document.write('줄 번호 보이기/숨기기<\/a>'); 줄 번호 보이기/숨기기
       1 BOOL CMFC4Direct3DApp::OnIdle(LONG lCount)
       2 {
       3         CWinApp::OnIdle(lCount);
       4 
       5         // 렌더링 수행
       6         static_cast< CMainFrame * >( m_pMainWnd )->m_wndView.RenderDirect3D();
       7 
       8         return TRUE;
       9 }
    

7. 다운로드 (준비중) [Bottom] [Top]

  • 다운로드: /!\ 코드는 준비중 - <!> 준비되면 업로드 예정


MFC

 

반응형
반응형

http://www.cyworld.com/01099811833/3457820

 

마우스 이벤트를 강제로 발생하는 API함수

VOID mouse_event( 
DWORD dwFlags, // 동작 지정 Flag
DWORD dx, // x좌표

DWORD dy, // y좌표

DWORD dwData , // 휠정보

PTR dwExtraInfo // 추가 정보
);

dwFlags 정보

상수설명
MOUSEEVENTF_ABSOLUTE8000x, y mouse의 위치값을 포함하여 event를 발생함
MOUSEEVENTF_MOVE1x, y에 지정한 위치로 이동
MOUSEEVENTF_LEFTDOWN2왼쪽 button down
MOUSEEVENTF_LEFTUP4왼쪽 button up
MOUSEEVENTF_RIGHTDOWN8오른쪽 button down
MOUSEEVENTF_RIGHTUP10오른쪽 button up
MOUSEEVENTF_MIDDLEDOWN20가운데 button down
MOUSEEVENTF_MIDDLEUP40가운데 button up
MOUSEEVENTF_WHEEL800mouse wheel 동작


표의 값은 VB.NET에선 &H로, C#에선 0x로 처리

dx, dy는 이동시킬 좌표를 지정, 단 OUSEEVENTF_MOVE와 MOUSEEVENTF_ABSOLUTE가 지정되어야 실제로 마우스 커서가 이동되는 효과를 볼 수 있음

Mouse의 좌표 지정시 실제 Monitor화면의 해상도를 기준점으로 잡으려면 65535로 가로세로 해상도를 나누고 Mouse Cursor를 위치시킬 위치를 곱하면 됩니다. 예를 들어 화면 해상도가 1024*768일 경우 (65535 / 1024) * y, (65535 / 768) * y 와 같이 처리하시면 원하는 곳으로 이동하게 됩니다.

| (or연산)을 통해서 여러가지 동작을 한번에 지정할 수 있다

ex) 마우스 오른쪽 버튼을 눌렀다 떼는 동작

mouse_event(0x8 | 0x10, 0, 0, 0, 0);

반응형
반응형

디렉토리 생성함수

CreateDirectory(경로,NULL);

NULL 인경우 해당 폴더가 존재하면 생성하지 않는다










출처 : http://blog.daum.net/studiocoma/6521408



Win32 API가 제공하는 CreateDirectory 함수의 응용.

API CreateDirectory의 경우 c:\1\2\3을 만들때 c:\1\2가 이미 존재하지 않으면 실패한다. -_-

이를 보완하고(상위폴더부터 주구장창 만들어 낸다;) 경로에 파일명까지 포함되어 있어도 폴더만 만들도록 해봤다.
- 말은 거창(?)한데 그냥 문자열 파싱해서 CreateDirectory를 반복 호출한다;; 

BOOL _CreateDirectory( LPCTSTR lpszPath )
{
    TCHAR szPathBuffer[MAX_PATH];

    size_t len = _tcslen( lpszPath );

    for ( size_t i = 0 ; i < len ; i++ )
    {
        szPathBuffer[i] = *( lpszPath + i );
        if ( szPathBuffer[i] == _T('\\') || szPathBuffer[i] == _T('/') )
        {
            szPathBuffer[i + 1] = NULL;
            if ( ! PathFileExists( szPathBuffer ) )
            {
                if ( ! ::CreateDirectory( szPathBuffer, NULL ) )
                {
                    if ( GetLastError() != ERROR_ALREADY_EXISTS )
                        return FALSE;
                }
            }
        }
    }
    return TRUE;
}


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


CFileFind fileFinder;
 if(!fileFinder.FindFile(".\\simple\\MyMenu.mdb"))
 {
  CreateDirectory(".\\simple", NULL);
  CopyFile(".\\MyMenu.mdb", ".\\simple\\MyMenu.mdb", TRUE);
 }



부모디렉토리없어도 디렉토리 생성하기, 디렉토리에 파일있어도 하위디렉토리까지 모든파일 삭제하기


출처 :  http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=7293&ref=7293


API 에서 지원되는 함수들은 어느정도 제약이 있기 떄문에......

 

좀 짜증났거든요.....  

 

물론 만드는게 어렵진 않지만 자주 사용해야 하므로 아예 보관하시고 카피해서 사용하시면 편하실거 같아서요...

 

 

 

#include <windows.h>

#include <stdio.h>

#include <string>

 

using namespace std;

 

/*

 

  기존 디렉토리가 있을경우 안만들어지고 없으면 만든다. 부모디렉토리가 없어도 생성가능

 

*/

void CreateDir(char* Path)

{

    char DirName[256];  //생성할 디렉초리 이름

    char* p = Path;     //인자로 받은 디렉토리

    char* q = DirName;  

 

    while(*p)

    {

        if (('\\' == *p) || ('/' == *p))   //루트디렉토리 혹은 Sub디렉토리

        {

            if (':' != *(p-1))

            {

                CreateDirectory(DirName, NULL);

            }

        }

        *q++ = *p++;

        *q = '\0';

    }

    CreateDirectory(DirName, NULL);  

}

 

 

/*

 

 하위디렉토리를 제외한 해당 디렉토리 모든 파일들을 제거

 

 */

void DeleteAllFiles(char* folderPath)

{

    char fileFound[256];

    WIN32_FIND_DATA info;

    HANDLE hp;

 

    sprintf(fileFound, "%s\\*.*", folderPath);

    hp = FindFirstFile(fileFound, &info); //디렉토리에 파일이 있는지 첫번째 파일만.

    do

    {

        sprintf(fileFound,"%s\\%s", folderPath, info.cFileName);

        DeleteFile(fileFound);

 

    }while(FindNextFile(hp, &info));  //다른 파일이 있을때 까지

 

    FindClose(hp);

}

 

 

/*

 

 해당 하는 디렉토리에 파일이 존재해도  디렉토리가 비어있지 않아도 지울수 있다 .

 

*/

 

void EmptyDirectory(char* folderPath)

{

    char fileFound[256];

    WIN32_FIND_DATA info;

    HANDLE hp;

 

    sprintf(fileFound, "%s\\*.*", folderPath);

    hp = FindFirstFile(fileFound, &info);   //디렉토리에 파일이 있는지 첫번째 파일만.

    do

    {

        if (!((strcmp(info.cFileName, ".")==0)||(strcmp(info.cFileName, "..")==0)))

        {

            if((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==FILE_ATTRIBUTE_DIRECTORY)  //Sub디렉토리가 존재하는경우

            {

                string subFolder = folderPath;

                subFolder.append("\\");

                subFolder.append(info.cFileName);

                EmptyDirectory((char*)subFolder.c_str()); /// {return (_Ptr == 0 ? _Nullstr() : _Ptr); }

                RemoveDirectory(subFolder.c_str());

            }

            else

            {

                sprintf(fileFound,"%s\\%s", folderPath, info.cFileName);

                BOOL retVal = DeleteFile(fileFound);

            }

        }

 

    }while(FindNextFile(hp, &info));

 

    FindClose(hp);

}




CreateDirectory 에서 폴더 접근 권한 바꾸기


출처 : http://ktr0.egloos.com/757379


Windows API 를 보면 CreateDirectory 라는 함수가 있다.

CreateDirectory("c:\aaa",NULL);

위와 같은 방법으로 첫번째 파라메터에 폴더 이름을 써 주고
두번째 파라메터에 NULL 을 써 주는 경우가 많다.

두 번째 파라메터는 사실 보안에 대한 파라메터이다.

PSECURITY_ATTRIBUTE 형의 변수가 들어가는데, NULL 이면 부모 폴더의
값을 그대로 가져온다.

 SECURITY_ATTRIBUTES sa;
 SECURITY_DESCRIPTOR sd;
 PSID pEveryoneSID = NULL;
 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
 EXPLICIT_ACCESS ea[2];
 PACL pacl = NULL;
 if(!AllocateAndInitializeSid(&SIDAuthWorld,1, SECURITY_WORLD_RID,0,0,0,0,0,0,0,&pEveryoneSID))
 {
  AfxMessageBox("Fail to get Everyone SID!!!",0,0);
  return;
 }
 ZeroMemory(&ea, 2*sizeof(EXPLICIT_ACCESS));
 ea[0].grfAccessPermissions = GENERIC_ALL;
 ea[0].grfAccessMode = SET_ACCESS;
 ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
 ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
 ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;

 SetEntriesInAcl(1,ea, NULL, &pacl);
 InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
 SetSecurityDescriptorDacl(&sd, TRUE,pacl , FALSE);
 //SetSecurityDescriptorSacl(&sd, TRUE,pacl , FALSE);


 sa.nLength = sizeof (SECURITY_ATTRIBUTES);
 sa.lpSecurityDescriptor = &sd;
 sa.bInheritHandle = TRUE;
 
 ::CreateDirectory(sysDirBuffer_tmp,&sa);
 if(pEveryoneSID){
  FreeSid(pEveryoneSID);
 }
 if(pacl){
  LocalFree(pacl);
 }

위의 예가 바로 CreateDirectory 함수에서 SECURITY_ATTRIBUTE 값을 가지고
폴더에 모든 ACCESS 권한을 주는 예제이다.

 

 

http://www.tipssoft.com/bulletin/board.php?bo_table=QnA&wr_id=380&page=392

GetCurrentDirectory 함수 : 현재의 작업 디렉토리를 얻어온다. 
SetCurrentDirectory 함수 : 작업디렉토리의 경로를 셋팅한다. 

일반적으로 작업디렉토리는 파일이 실행된 경로가 지정됩니다. 
아래는 작업디렉토리의 변경이 일어나는 대표적인 예인 CFileDialog 대화상자를 이용한 경우입니다 

참고하시면 될 듯 싶습니다. 


    char path[MAX_PATH] = {0}; 
    // 현재의 작업디렉토리를 저장한다. 
    GetCurrentDirectory(MAX_PATH, path); 

    // CFileDialog 를 Open 모드로 생성한다. 
    CFileDialog dlg(TRUE); 
    if(dlg.DoModal()==IDOK){ 
                // 사용자가 폴더를 옮겨가며 특정 파일을 지정 
                // 이경우 작업폴더가 변경됨. 
                CString str = dlg.GetFileName(); 
                //얻어온 파일과 관련된 코드 추가.............. 
                
                // 이전에 저장했던 작업디렉토리 경로를 되돌려 셋팅 
                SetCurrentDirectory(path); 
    }


 





http://msdn.microsoft.com/ko-kr/library/system.io.directory.createdirectory(v=vs.80).aspx



Directory.CreateDirectory 메서드

.NET Framework 2.0
3명 중 1명이 도움이 되는 것으로 평가 이 항목 평가

지정된 경로에 모든 디렉터리를 만듭니다.

이름설명
Directory.CreateDirectory (String)지정된 path로 모든 디렉터리와 하위 디렉터리를 만듭니다.

.NET Compact Framework에서 지원됩니다.

Directory.CreateDirectory (String, DirectorySecurity)지정된 경로에 모든 디렉터리를 만들고 지정된 Windows 보안을 적용합니다.

방법: 파일에 텍스트 쓰기 

반응형
반응형

http://infoki.net/613


SHFILEOPSTRUCT

SHFileOperation 함수가 행하는 파일 조작에 관한 정보가 격납됩니다.

typedef struct _SHFILEOPSTRUCT {
    HWND  hwnd;                 // 윈도우 핸들
    UINT  wFunc;                // 실행하는 조작
    LPCTSTR  pFrom;             // 대상 파일명
    LPCTSTR  pTo;               // 목적 파일명
    FILEOP_FLAGS  fFlags;       // 플래그
    BOOL fAnyOperationsAborted; // 결과
    LPVOID  hNameMappings;      // 파일명 매핑 오브젝트
    LPCTSTR lpszProgressTitle;  // 다이얼로그의 타이틀
} SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT;

멤버

hwnd

파일 조작의 상황을 표시하는 다이알로그 박스의 오너 윈도우의 핸들을 지정합니다.

wFunc

실행하는 파일 조작을 나타내는 값을 지정합니다.이하의 값중 한쪽이 격납됩니다.

의미
0x0001 (FO_MOVE)

pFrom 멤버로 지정된 파일을 pTo 멤버로 지정된 위치로 이동합니다.

0x0002 (FO_COPY)

pFrom 멤버로 지정된 파일을 pTo 멤버로 지정된 위치에 카피합니다.

0x0003 (FO_DELETE)

pFrom 멤버로 지정된 파일을 삭제합니다.

0x0004 (FO_RENAME)

pFrom 멤버로 지정된 파일의 이름을 pTo 멤버로 지정된 파일명으로 변경합니다.이 플래그를 사용하고, 한 번의 함수 호출로 복수의 파일의 이름을 변경할 수 없습니다.복수의 파일의 이름을 변경하려면 FO_MOVE 플래그를 사용합니다.

pFrom

조작의 대상이 된다1개이상의 파일명이 격납된 버퍼의 주소를 지정합니다.이러한 파일명은 풀 패스 지정되어 있지 않으면 안됩니다.파일명에 “*” 등의, 표준MS-DOS와일드 카드를 지정할 수 있습니다.

각각의 파일명은1개의 눌 문자로 단락지어지지 않으면 안됩니다.또, 마지막 파일명의 뒤에는2개의 눌 몬지를 들어갈 수 있지 않으면 안됩니다.

pTo

카피·이동처 디렉토리명 또는 변경 후의 파일명이 격납된 버퍼의 주소를 지정합니다.이 멤버를 사용하지 않는 경우에는 0 (NULL) (을)를 지정하지 않으면 안됩니다.

카피 또는 이동의 조작에 대하고, pFrom 멤버와 같은 방법으로 복수의 파일을 지정할 수 있습니다.이 경우, 각각의 파일명은1개의 눌 문자로 단락지어, 마지막 파일명의 뒤에는2개의 눌 몬지를 들어갈 수 있지 않으면 안됩니다.복수의 파일명을 지정하려면 , fFlags멤버에 FOF_MULTIDESTFILES 플래그를 지정합니다.

카피 또는 이동의 조작에 대하고, 존재하지 않는 디렉토리를 지정할 수 있습니다.이 때, 시스템은 디렉토리의 작성을 시도합니다만, 통상은, 다이알로그 박스를 표시해 새로운 디렉토리를 작성할지를 유저에게 확인합니다.파일 조작중에 다이알로그 박스를 표시하지 않게 하려면 , fFlags 멤버에 FOF_NOCONFIRMMKDIR 플래그를 지정합니다.

파일명에 와일드 카드 문자를 사용할 수 없습니다.또, 파일명은 풀 패스 지정되어 있지 않으면 안됩니다.상대 패스를 사용하면, 예기치 않은 결과를 일으킬 가능성이 있습니다.

fFlags

파일 조작을 제어하는 옵션 플래그를 지정합니다. 0 또는 이하의 값을 조합해 지정합니다.

의미
0x0001 (FOF_MULTIDESTFILES)

pTo 멤버가 복수의 파일명을 지정해 있는 것을 나타냅니다.

0x0002 (FOF_CONFIRMMOUSE)

현재는 사용되지 않습니다.

0x0004 (FOF_SILENT)

경과를 나타내는 다이알로그 박스를 표시하지 않습니다.

0x0008 (FOF_RENAMEONCOLLISION)

이동, 카피, 이름의 변경의 조작에 대하고, 지정한 파일명이 벌써 존재하고 있었을 경우에는, 조작 대상의 파일에 새로운 이름을 붙입니다.

0x0010 (FOF_NOCONFIRMATION)

표시되는 다이알로그 박스로 「네」또는 「모두」를 선택하도록(듯이) 합니다.

0x0020 (FOF_WANTMAPPINGHANDLE)

FOF_RENAMEONCOLLISION 하지만 지정되어 있고, 같은 파일명이 존재했기 때문에 새로운 파일명이 붙여졌을 경우에, 낡은 이름과 새로운 이름을 포함한 매핑 오브젝트의 핸들을hNameMappings 멤버에 격납합니다.

0x0040 (FOF_ALLOWUNDO)

가능한 한 un-do 정보를 보관 유지하도록(듯이) 합니다. pFrom 그리고 지정된 파일명이 풀 패스로 지정되어 있을 필요가 있습니다.삭제시로 지정하면, 파일을 쓰레기통에 넣을 수 있습니다.

0x0080 (FOF_FILESONLY)

와일드 카드 파일명(*.*)(이)가 지정되었을 경우에게만 조작을 실행합니다.

0x0100 (FOF_SIMPLEPROGRESS)

경과를 나타내는 다이알로그 박스에 파일명을 표시하지 않습니다.

0x0200 (FOF_NOCONFIRMMKDIR)

새로운 디렉토리를 작성할 필요가 있는 경우에, 작성할지의 확인을 하지 않습니다.

0x0400 (FOF_NOERRORUI)

에러가 발생했을 경우에, 유저 인터페이스를 표시하지 않습니다.

0x0800 (FOF_NOCOPYSECURITYATTRIBS)

Version 4.71 이후: 파일의 시큐러티 속성이 카피되지 않게 합니다.

0x1000 (FOF_NORECURSION)

로컬 디렉토리에만 조작을 행합니다.서브 디렉토리에 대해서 재귀적으로 조작을 행하지 않습니다.

0x2000 (FOF_NO_CONNECTED_ELEMENTS)

Windows Me/2000 이후: 그룹으로서 접속되고 있는 파일을 이동하지 않습니다.지정된 파일만을 이동합니다.

0x4000 (FOF_WANTNUKEWARNING)

Windows Me/2000 이후: 쓰레기통에 넣는 것은 아닌 삭제 조작 시에 파일이 삭제될 때 경고를 합니다.

0x8000 (FOF_NORECURSEREPARSE)

Windows XP 이후: 리파스포인트를 컨테이너는 아니고 오브젝트로서 취급합니다.

fAnyOperationsAborted

지정한 파일 조작이 완료하기 전에 유저에 의해서 중지되었을 경우에는 1 (TRUE) 하지만 격납됩니다.그 이외의 경우에는 0 (FALSE) 하지만 격납됩니다.

hNameMappings

이동·카피·이름 변경된 파일의 낡은 파일명과 새로운 파일명을 포함한 파일명 매핑 오브젝트의 핸들이 격납됩니다.이 멤버는 fFlags멤버에 FOF_WANTMAPPINGHANDLE 플래그가 지정되었을 경우에게만 사용됩니다.이 핸들이 불필요하게 되면SHFreeNameMappings 함수로 해방하지 않으면 안됩니다.이 핸들에 관해서는, 아래의 해설을 참조해 주세요.

lpszProgressTitle

경과를 표시하는 다이알로그 박스의 타이틀 바에 사용하는 문자열의 주소를 지정합니다. fFlags 멤버로 FOF_SIMPLEPROGRESS 하지만 지정되어 있는 경우에게만 유효합니다.

해설

pFrom 멤버 및 pFrom 멤버가 풀 패스가 아닌 파일명의 경우는, 커런트 디렉토리에 있다고 보입니다.

파일 삭제시에 pFrom 멤버에 풀 패스가 아닌 파일명이 지정되었을 경우는, FOF_ALLOWUNDO 플래그를 지정했을 경우에서도 파일은 쓰레기통에 넣어지지 않습니다.

fFlags 멤버에 FOF_WANTMAPPINGHANDLE 플래그를 지정하면, hNameMappings 멤버에는 파일명 매핑 오브젝트의 핸들이 격납됩니다.이 핸들은, 멤버가 UINT 형태 및 SHNAMEMAPPING 구조체의 배열에의 포인터인 구조체의 포인터로서 취급됩니다.즉,

struct HANDLETOMAPPINGS {
    UINT           uNumberOfMappings;  // 배열의 요소수
    SHNAMEMAPPING *lpSHNameMapping;    // 배열에의 포인터
};

그렇다고 하는 구조체에의 포인터이다고 합니다(이 HANDLETOMAPPINGS 구조체는 명시적으로 정의되고는 있지 않습니다).이 때의UINT 형태의 값으로서 SHNAMEMAPPING 구조체의 배열의 요소수가 세트 됩니다.각각의 SHNAMEMAPPING 구조체에는, 변경되었다1개의 파일이 낡은 패스명 및 새로운 패스명이 격납되고 있습니다.

hNameMappings 멤버의 값으로 해서 취득한 핸들은 SHFreeNameMappings 함수를 사용해 해방하지 않으면 안됩니다.

hNameMappings 멤버에 돌려주어지는 파일명 매핑 오브젝트를 참조하는 경우, Windows 95/98/Me 그럼, SHFileOperation 함수는 ANSI 세트의 SHNAMEMAPPING 구조체를 돌려줍니다만,Windows NT/2000/XP 그럼, SHFileOperation 함수는 ANSI 판의 함수 및 Unicode 판의 함수 모두 Unicode 세트의 SHNAMEMAPPING 구조체를 돌려줍니다.따라서, 모든 버젼의 Windows 그리고 조작시키기 위해서는, Windows 의 버젼을 조사해 조건 나누어 하는 코드를 작성할 필요가 있습니다.

대응 정보

Shell32.dll Version 4.00 이후

Windows 95 이후 / Windows NT 4.0 이후

변수와의 대응

HSP 변수멤버
멤버명오프셋사이즈
shfo.0hwnd04
shfo.1wFunc44
shfo.2pFrom84
shfo.3pTo124
shfo.4fFlags164
shfo.5fAnyOperationsAborted204
shfo.6hNameMappings244
shfo.7lpszProgressTitle284








출처 : http://blog.naver.com/wjfeo42?Redirect=Log&logNo=30003505246

          http://blog.naver.com/ollbback?Redirect=Log&logNo=120040302994

 

 

파일 복사, 삭제, 이동  파일명 변경등을 지원하는 함수로써 윈도우 파일 익스플로어 에서 뜨는

파일 전송 이미지가 나오는 다이얼로그를 띄울수 있다는 점.......

 

int WINAPI SHFileOperation(LPSHFILEOPSTRUCT lpFileOp);

Parameter SHFILEOPSTRUCT

 





http://blog.naver.com/imanoos/35359421



SHFILEOPSTRUCT를 이용해서 폴더를 이동시킬 때 주의점



SHFILEOPSTRUCT shop= {0};
shop.hwnd = NULL;
shop.wFunc = FO_MOVE; //FO_COPY
shop.pFrom = strSource;
shop.pTo = strDest;

// shop.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_MULTIDESTFILES;
shop.fAnyOperationsAborted = false;
shop.hNameMappings = NULL;
shop.lpszProgressTitle = "데이타 이동";
SHFileOperation(&shop);


 

보통 위와 같이 초기화를 시키는데, 이 때 shop.pFrom 및 shop.pTo 부분에 들어가는 마지막 문자열은 항상 NULL로 끝나야 한다.

 

예)

 

TCHAR pszPath[1024];
ZeroMemory(&pszPath, sizeof(TCHAR)*1024);

strcpy(pszPath, strPath);

반응형
반응형

#define random(n)  (rand() % (n))   //난수 발생 매크로 0에서 n-1까지
#define randomR(a, b) (rand() % (b-a))+a  //난수 발생 매크로 a에서 b-1까지

 

 

 

HCRYPTPROV   hCryptProv;
 BYTE         pbData[16];
 CryptGenRandom(hCryptProv,  8,  pbData );  // 랜덤R 호출전 선언

 

 

#include <WinCrypt.h> //필요한 헤더

반응형
반응형

 

SendMessage와 PostMessage의 차이점

한구기2010-01-12 15:33:34주소복사
조회 386  스크랩 0

BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

 

두 함수의 인수는 완전히 동일하다.

여기서 Post라는 말은 순수 우리 말로는 "붙인다~"라고 번역되며

"Send"라는 말은 보낸다.라고 번역된다.

 

PostMessage 함수는 Msg인수로 지정된 메세지를 hWnd 윈도우의 메세지 큐에

집어넣어 윈도우 프로시져에서 이 메세지를 처리하도록 한다. 메세지를 큐에 넣기만하고

바로 리턴하므로 메세지를 붙인 수 즉시 다른 작업을 할 수 있지만

큐에 대기하고 있는 다른 메세지가 있으면 뒤에 붙인 메세지는 곧바로 처리되지 않는다.

큐에 붙여진 메세지는 GetMessage에 의해 읽혀지고

DispatchMessage에 의해 윈도우 프로시져로 보내져 처리될 것이다.

 

급하게 처리할 필요가 없거나 또는 지금 하고 있는 작업을 완전히 끝내야만 처리할 수 있는 메세지는

PostMessage 함수로 큐에 붙인다. 이 함수로 붙여진 메세지는 언제 처리될지 정확하게

예측하기 힘들고 그래서 붙여지는 메세지는 wParam과 lParam에는 지역 포인터를 사용하지 말아야 한다.

메세지를 붙일 시점에는 포인터가 존재했더라도 메세지가 처리될 시점에는 포인터가 무효해질 수 있기

때문이다.PostMessage는 메세지를 큐에 붙인 후 성공하면 TRUE를 리턴하며,

실패하면 FALSE를 리턴하는데 메세지 큐는 크기가 한정되어 있기 때문에 고속으로 전송되는 모든

메세지를 다 수용하지 못할 수도 있다.다행히 Win32 환경에서는 큐 크기가 대폭 늘어나면서 왠만해서는

큐가 부족한 상황이 잘 발생하지 않는다.

 

SendMessage는 메세지를 큐에 넣는 것이 아니라,

곧바로 윈도우 프로시져로 보내 즉각 처리하도록 하며,

메세지가 완전히 처리되기 전에는 리턴하지 않는다.

 

즉, 블록시켜서 SendMessage는 윈도우간 특히 부모 윈도우와 차일드 컨트롤간의 통신에 자주 사용된다.

예를 들어 리스트 박으세요~ 라는 LB_ADDSTRING 이라는 메세지를 보내면 이는 리스트 박스에게

문자열 항목을 추가하라는 명령이 되며, 항목이 완전히 추가되고 난 후에 SendMessage가 리턴된다.

 

윈도우간에 메세지를 교환할 때 어떤 함수를 사용할 것인가는,

신중하게 결정해야 하는데 대부분의 경우는 SendMessage로 보내는 것이 정석이며,

또 효율적이다. 또 WM_COPYDATA 같은 메세지는 그 특성상 반드시

SendMessage로만 보내야 하며, PostMessage로 붙여서는 안된다.

 

다시, 간략히 정리하자면

SendMessage는 당장 어떤 일을 하라는 명령이며, PostMessage는 한가해질 때 어떤 일을 하라는 신호다.

라고 생각하면 쉬울 것이다.

 

부가적으로,

PostMessage는 큐에 넣고, SendMessage는 WndProc의 case 하나를 호출하는 것과 같다.

 

출처:윈도우즈 API 정복(한빛미디어,김상형)

=====================================================================================================

SendMessage()의 응용 방법...

 

LRESULT SendMessage(

    HWND hWnd,

    UINT Msg,

    WPARAM wParam,

    LPARAM lParam

);

 

윈도우가 다른 두 객체에서 데이타 전달 또는 명령 실행에 유용하게 쓰일 수 있다.

보통 Msg에 동작의 구분 값이 들어가고

wParam과 lParam에 전달할 데이타를 넣는다.

 

그런데 데이타의 전달 뿐만 아니라,

호출 한 부분에서 데이타를 받을 때도 쓸 수 있다.

즉 A에서 B로 SendMessage를 보낼때 값을 wParam과 lParam에 넣어서 전달을 하고,

다시 B에서 A로 LRESULT에 값을 담아서 A로 다시 보낼 수 있는 것이다.

 

ClassB::sendData(){

           ...

           ::SendMessage(ClassA Windows, WM_COMMAND, (LPARAM)nInt1, (WPARAM)nInt2);

           ...

}

 

struct SData{

           int n1;

           int n2;

};

 

IMPLEMENT_MESSAGE_HANDLER(ClassA, OnCommand){

           int nValue1 = (int)wParam ;

           int nValue2 = (int)lParam

           ...

           SData data;

           ...

           pResult = (void * )&data;

           return (LRESULT)pResult ;

}

 

이때 주의해야 할 점은..

B에서 A로 보낼때는 값을 stack에 들어가서 전달 과정에서 사라지지 않게 하는 것이 중요하다.

즉 void * pResult 가 함수내에 선언되어있다면 함수가 리턴되면서 사라질 수 있기 때문에

멤버 변수로 선언하거나, static으로 선언해서 사라지지 않도록 해야 한다.

 

================================================================================================

 

WPARAM과 LPARAM의 역할  API 

2006/05/24 12:19

복사 http://blog.naver.com/samyoung79/110004541846


LPARAM과 WPARAM은 메세지에 대한 추가적인 정보를 가지고 있다.
예를 들어 키보드를 눌렀을 경우 메세지가 발생하는데
무슨 키를 눌렀는지 2개의 키를 동시에 눌렀는지등등..
마우스를 클릭햇을 경우도 메세지가 발생하는데
어느 위치에 눌렀는지 등등

LPARAM은 보통 위치정보,
WPARAM은 보통 어떤키를 눌렀는지 중복키를 눌렀는지 정보, resource의 발생정보

 

(표1) 기본적인 마우스 메시지

  메시지

  내용

 lParam

 wParam

 WM_LBUTTONDOWN

좌측 마우스 버튼을 눌렀을때

 마우스 위치

 키상태정보

 WM_LBUTTONUP

좌측 마우스 버튼을 띄었을때

 마우스 위치

 키상태정보

 WM_RBUTTONDOWN

우측 마우스 버튼을 눌렀을때

 마우스 위치

 키상태정보

 WM_RBUTTONUP

우측 마우스 버튼을 띄었을때

 마우스 위치

 키상태정보

 WM_MOUSEMOVE

마우스가 움직일때

 마우스 위치

 키상태정보

 

마우스가 클릭하였을 때 마우스에 대한 메시지가 발생되는데 클라이언트 영역과 비 클라이언트 영역에 분리되어 다르게 나타난다.

클라이언트 영역이란 윈도우 타이틀바와 외곽선을 제외한 즉 실제로 WM_PAINT메시지에 의해서 그래픽을 출력하는 영역을 말한다.

마우스가 눌려졌을 경우에는 어느 부분이 눌려졌다는 정보를 보내주어야 한다.

이 정보를 lParam에 기록한다.

lParam은 32비트 LONG형이다.

이 형에 마우스 정보를 입력하여 알려주게 된다.

우리는 보통 위치 좌표를 사용할 경우 2차 평면에서는 (X,Y)를 사용한다.

즉 정보값이 최소 2개라는 의미이다. 헌데 lParam은 한 개의 정보 저장 장소이다.

이 저장장소에 두 개의 값을 저장하기 위해서 lParam을 16비트씩 잘라서 상위 16비트에는 Y좌표를 하위 16비트에는 X좌표값을 넣어서 보내준다.

lParam안에 X,Y의 자표값이 나누어져 들어가 있기 때문에 우리는 이 값을 분리해야 한다.

다행히도 윈도우 lParam의 값에서 상위 16비트를 얻고자 한다면 HIWORD함수를 사용해야 한다.

 

int y = HIWORD(lParam)  int x = LOWORD(lParam)

 

좌측 마우스 버튼의 클릭 메시지 처럼 우측 마우스 버튼 클릭 메시지도 같은 방법으로 lParam에 위좌표를 넣어서 전송한다.

 

마우스 더블클릭메시지

더블 클릭이란 연속적으로 빠르게 마우스 버튼을 두번 클릭하는 것이다.

이 메시지를 이벤트를 받기 위해서는 WNDCLASSEX의 멤버변수 style안에 CS_DBCLKS 삽입시킴으로서 이벤트를 받을수 있다.

이 값을 대입하기전 CS_DBLKS라는 값을 대입하면 연속적으로 두번 눌렀을 경우 WM_LBUTTONDOWN이 연속적으로 들어오는 것과 동시에 WM_LBUTTONDBLCKS가 발생

이 더블 클릭 메시지는 사실 시스템에서 발생하는 메시지가 아니라 어플리케이션에서 조합하여 만든 메시지이다.

어느 일정한 시간 구간안에서 WM_LBUTTONDOWN이 발생하면 이것을 WM_BUTTONDBLCKS로 바꾸어서 메시지 큐에 삽입하기 때문이다.

이때 사용하는 함수가 GetMessageTime인데 이 함수는 현재 메시지 큐에서 가장 최근에 얻은 메시지의 시간을 얻는 함수이다.

이 함수를 통해서 어느 시간 사이에 WM_LBUTTONDOWN 메시지가 연속적으로 발생하였을때 이 메시지와 함께 WM_BLUTTONDDBLCLK로 발생하게 되는것이다.

이런 이유로 더블클릭 메시지는 WM_LBUTTONDOWN이나 WM_RBUTTONDOWN과 같이 lParam과 wParam에 저장되는 정보의 값은 같다.

어플리케이션을 만들 경우 좌측 마우스 버튼을 눌렀을때와 더블 클릭을 했을 경우 이 두개를 분리하여 처리할 경우가 있다.

그러나 메시지는 더블 클릭했을 경우 두개의 메시지가 동시적으로 발생하기 때문에 이것을 막기는 어렵다.

 

 

 

비클라이언트 영역

 

  메시지

  내용

 lParam

 wParam

 WM_NCLBUTTONDOWN

좌측 마우스 버튼을 눌렀을때

 마우스 위치

 Hit-test정보

 WM_NCLBUTTONUP

좌측 마우스 버튼을 띄었을때

 마우스 위치

 Hit-test정보

 WM_NCRBUTTONDOWN

우측 마우스 버튼을 눌렀을때

 마우스 위치

 Hit-test정보

 WM_NCRBUTTONUP

우측 마우스 버튼을 띄었을때

 마우스 위치

 Hit-test정보

 WM_NCMOUSEMOVE

마우스가 움직일때

 마우스 위치

 Hit-test정보

 

 

[출처] WPARAM과 LPARAM의 역할|작성자 동키

반응형
반응형

http://blog.naver.com/ljyhs?Redirect=Log&logNo=30014607134







HRESULT는 32비트 signed 형 정수이다.

 

COM의 규정에 의한 것은 아니지만, COM 인터페이스에 소속된 대부분의 함수들은 HRESULT

 

형태의 반환값을 사용한다. 그러나, 이렇게 대부분의 COM 인터페이스에 소속된 함수들이 반

 

환형으로 HRESULT를 사용함으로써 클라이언트에게 일관성 있는 함수의 상태 정보를 전달 할

 

수 있게 된다. 이 반환 값을 사용하기 위해서는 SUCCEEDED나 FAILED 매크로 를 사용해야

 

한다.

 

* HRESULT 형의 구조

 

 0~15 비트 : RETURN CODE 
 

 16~28 비트 : FACILITY 

 29~30 비트 : R 

 31 비트 : SEVERITY ( SEVERITY(31비트)는 함수의 수행의 성공과 실패를 알려주는 코드다.)

 

 

* HRESULT의 상태 정보 요약.

 

  S_OK : 때때로 Boolean TRUE 값(0X0)으로 S_FALSE와 함께 사용되며 함수가 성공하였음을 의미한다. 

 NOERROR : S_OK와 동일한 의미이다. 

 S_FALSE : S_OK와 반대로 Boolean FALSE 값(0X1)으로 함수가 실폐하였음을 의미한다. 

 E_UNEXPRCTED : 치명적인 실패를 의미한다. 

 E_NOTIMPL : 멤버 함수에 구현 코드가 포함되어 있지 않다. 

 E_OUTOFMEMORY : 필요한 메모리를 할당할 수 없음 

 E_INVALIDARG : 하나 혹은 그 이상의 인자가 타당하지 않음 

 E_NOINTERFACE : 요청한 인터페이스를 지원하지 않음 

 E_POINTER : 타당하지 않은 포인터 

 E_HANDLE : 타당하지 않은 처리 

 E_ABORT : 작동 중지 

 E_FAIL : 특정하지 않은 실패 

 E_ACCESSDENIED : 일반적 접근이 금지된 에러

 

 

 

부분출처 : www.pnteam.net/419

 

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

 

LRESULT 은 long 형 타입으로서, WIn32 환경에서 메시지 처리를 마친 후 O/S에게 어떤 신호

 

를 주기 위해 사용되는 값이다.

 

각각의 메시지 case에 대한 처리 값이 때로는 단순히 -1, 0, 1등으로 어떨때는 비트 플래그로

 

O/S가 알 수 있도록 설정한다.

 

대부분의 경우 0을 리턴하면 내가 모든 메시지를 처리할테니 O/S는 더이상 메시지에 대한 처

 

리를 하지 말라는 것으로 인식, 간혹 -1의 경우 O/S가 진행하던 작업을 취소시키는 의미를 가

 

질 때도 있다.

반응형
반응형

http://blog.naver.com/gunner98?Redirect=Log&logNo=110018669558







첨부파일 (2)

우리가 현재 실행되고 있는 프로그램의 현재 작업 디렉토리를 알아 오기 위해서

위해서 "GetCurrentDirectory()"를 주로 이용합니다.

그럼 아주 쉽게 프로그램의 작업 디렉토리를 알아 옵니다.

 

여기에서 프로그램이 .dll 혹은 .ocx를 안고 있다면 좀 생각해 보아야 할 상황이

생길 수가 있습니다.

 

먼저, 프로그램에서 "GetCurrentDirectory()"함수를 이용해서 해당 경로에서 검색을

한다고 할 때, 검색할 폴더의 경로를 GetCurrentDirectory()함수를 통해서 얻어진

경로에 해당 파일을 찾고자 할 때 문제가 발생할 수가 있습니다.

 

그 문제는 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 이용해서 현재 Process의

Current Directory를 바꾸어 버리면, 프로그램에서 GetCurrentDirectory()함수를 통해

얻어지 경로는 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 이용해서 설정한 폴더로

바꾸어 버립니다. 그래서 프로그램에서는 엉뚱한 경로에 가서 해당 파일을 찾다가  못

찾았다고 할 것입니다.

 

위의 문제를 해결하기 위해서는 프로그램에서 상대경로를 사용하고 있기 때문에 위와

같은 문제를 발생할 수가 있습니다. 그래서 절대경로를 이용하는 방법이 있을테고,

다른 방법은 .dll 혹은 .ocx에서 SetCurrentDirectory()함수를 사용하지 않는 것입니다.

 

이 문제를 한번 재현해 보고 싶어서 Sample Source를 짜 보았습니다.

정말 우리가 예상했던 결과대로 Current Directory가 바뀌어 버리네요..^^

 

[ 실행 화면 ]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 1. 프로그램의 작업 디렉토리 경로 알아오기 >

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 2. dll에서 Current Directory 변경하기 >

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

< 그림 3. dll에서 Current Directory 변경한 후에 다시 작업 디렉토리 알아오기 >

 

위의 결과를 보면 알 수 있듯이, 처음에 GetCurrentDirectory()함수를 이용해서 작업 디렉토리

경로를 알아 오니 "C:\test\Smaple"였습니다.

dll에서 SetCurrentDirectory()함수를 이용해서 현재 Current Direcotory를 "D:\TRACE\UCM"로 변경하였습니다.

그런 다음 다시 프로그램에서 GetCurrentDirectory()함수를 이용해서 작업 디렉토리 경로를

알아 오니 "D:\TRACE\UCM"로 바뀌어져 있는 것을 확인할 수가 있습니다.

반응형
반응형

#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console" )

 

아무대나 이 한줄을 적는다


_DEBUG   전처리를 해도 좋다..


p.s 전에 한번 올린적이 있음

반응형
반응형

 

.inl 파일을 헤더파일 하단에 붙일때 inline 은 선언부에 써도 되고 안써도 되지만 정의부에는 inline 키워드를 써야한다

 

 

이하 정리잘된 포스트!!!!!

 


 

 

http://dev.log.mumbi.net/390

 

[C++] inline keyword 와 inl file. ( *.inl )

Language/C++ | 2009/08/24 14:21 | Posted by 임준환( 멈비 )
inline 함수는 함수를 호출하는게 아니라 마치 #define macro 와 같이 치환되어 함수 호출에 대한 overhead 를 줄일 수 있어 좀 더 빠른 처리가 가능하다. 

하지만 inline 함수는 사용자가 지정한다고 해서 inline 함수가 되는 것이 아니라 사용자가 compiler 에게 inline 함수로 사용하겠다는 신청을 하면 compiler 가 inline 함수의 조건에 맞으면 inline 함수로 처리하고 아니면 일반 함수로 처리하게 된다. 

inline 함수의 조건이란 7 line 이하이어야 하고, 재귀 호출과 같은 반복되는 함수는 제외된다.


함수를 inline 함수로 신청하기 위해서는 
  1. 함수의 선언과 동시에 정의를 한다. ( inline keyword 를 붙이지 않아도 된다. )
  2. 함수의 선언 앞에 inline keyword 를 붙인다. 단, 함수의 정의는 선언부와 같은 file 안에 있어야 한다.

 
그러므로 inline 함수는 cpp file ( *.cpp ) 에 정의할 수 없는 것이다. 


위의 사항으로 비추어 보면 선언과 정의가 같은 file 안에 있어야 하므로 선언과 정의를 분리하기 어려워진다.

그래서 등장한 기법이 inl file ( *.inl ) 이다.

inl file 에는 inline 함수의 정의부를 적어 놓고 선언부가 있는 header file ( *.h ) 의 끝에서 #include 로 inl file 을 포함해 주면 된다.

단, 여기서 주의할 점은 선언과 동시에 정의를 했을 경우에는 inline keyword 를 붙이지 않아도 되지만, 그렇지 않으므로 꼭 inline keyword 를 붙여야 한다. 


inl file 이 또 하나 유용한 점이 있는데 바로 template 을 작성할 때이다.

template 또한 inline 함수와 마찬가지로 선언과 정의가 같은 file 에 있어야 한다.

그러므로 template 의 선언은 header file 에 작성하고, 정의는 inl file 에 하면 선언과 정의를 분리할 수 있게 된다.

반응형
반응형

출처 : http://blog.naver.com/kzh8055/140062582343

 

 


[ Effective C++ ] 암시적 타입 변환( Implicit Type Casting )  Effective !!! 2009/02/06 21:51

 

 
흠...

 

이번 글은 암시적인 타입 변환에 대해서다.

사실 EC++ 에 '암시적인 타입변환'이란 항목 자체가 있는건 아니지만 책 전반에 걸쳐

계속 언급돼는 중요한 내용이고 간혹 헷갈리는 부분도 있고해서 정리하고 넘어가야겄다.

 

암시적 타입 변환 VS 명시적 타입 변환

 

암시적( Implicit ) 과 반대돼는 개념은 명시적( Explicit ) 일것이다.

명시적은 말그대로 어떤 의도를 대놓고(?) 나타낸다는 의미이고

그렇다면 명시적 타입변환( Explicit Type Casting )이란

어떤 타입을 또 다른 타입으로 바꾸는데 있어 사용자가 직접 그 타입 변환을 수행하는

캐스팅 명령어를 코딩하는 경우라 볼수 있다.

 

비교적 타입 제한에 관대한 C 언어에서는 명시적 캐스팅 방법 달랑 하나로

거의 대부분의 경우 이방법을 사용해 모든 타입으로의 캐스팅이 가능하다.

- 심지어는 호환돼는 타입이 아닐경우라도( 실수형에서 문자형으로의 타입 변환등 ) 가능하다 ㅡ,.ㅡ

 

float radian= 3.14

int a = ( int )radian;              // C 스타일의 ( 명시적인 )캐스팅< float 에서 int 로 >

 

그럼 C++ 에서 제공하는 타입 변환자 는 어떠한가?

타입 검사에 있어선 한층 까탈스러워진 C++ 의 경우 각각 용도에 맞는 타입변환자가

4 개 씩이나 준비 돼있다. 그게 바로 static_cast, const_cast, dynamic_cast, reinterpret_cast 이다.

- 이것에 대한 자세한 설명은 책이나 웹 상에 널려있는 정보를 활용해라

 

하여간 여기서 중요한 부분은 명시적 타입 변환은 사용자( 프로그래머 )의

타입변환 의도가 직접 코드에 반영( 타입 변환자 등 으로 )돼 있다는 것이다.

- 쉽게 말해 캐스팅 연산을 적용했다는거다.

 

그러면 이와는 반대인 암시적인 타입 변환은 뭔가?

위의 맥락 상으로는 타입 변환자 등 타입을 캐스팅하기 위한 부분이 코드상에

( 명시적으로 ) 반영돼지 않았음에도 불구 하고 자동으로 타입 변환이 이뤄지는것을 말할것이다.

 

사용자가 작성한 코드에는 타입 변환 의도가 없었음에도 자동으로 타입변환이 발생한다면

과연 이것은 누구의 소행일것인가?

 

범인( ? )은 바로 컴파일러다. 컴파일러는 일련의 규칙에 따라( 컴파일러 제작자가 의도한 )

상황에 맞다고 생각하면 기특하게도 사용자가 구지 시키지도 않은 타입변환을

지가 스스로 수행하는 것이다.

 

흔히 발생하는 암시적인 타입 변환의 예를 들어 봅시다.

 

class Base{ ... };                          // Base 클래스

class Derived : public Base{ ... };   // Base 로 부터 상속 받은 Derived 클래스

 

...

/* Client Code */

 

Base* pBase = new Base;     // 오케바리! 동일한 타입의 객체를 동일한 타입의 포인터로 가르킴

pBase = new Derived;            // 이 또한 이상 무! 자식 객체는 부모 객체의 포인터로 가리킬수 있다

 

C++ 을 비롯한 객체 지향언어를 사용해본 인간이라면

 

[ 부모 타입 포인터 ] = [ 자식 타입 객체( 주소 ) ];

 

와 같은 형태의 코딩을 수없이 해왔을것이다.

사실 따지고 보면 엄연히 두 타입이 서로 같지 않은 셈이니

C++ 컴파일러가 왠지 타입 불일치 에러를 뱉어낼 듯 하나

희안하게 아무련 불평없이 깔끔히 컴파일 된다.

 

위 코드가 전혀 문제 없이 컴파일돼는 이유는 앞서 말한' 일련의 규칙 '에 따라 컴파일러가

알아서 캐스팅을 수행하기 때문이다.

 

그러니깐 Base( 기반 클래스 )타입 포인터로 Drived 객체( 파생 클래스 )를 가르키는 상황은

컴파일러 입장에선 자신이 갖고 있는 ' 일련의 규칙' 에 포함돼 있다는 소리다.

 

또 다른 예를 들어 봅시다.

어떤 상수 타입의 객체에 대한 참조( 또는 포인터 ) 는 초기화 될시

비 상수 객체를 가르킬수 있다.

 

class CObject{ ... };

 

CObject nonConstObject;                                // 비 상수 객체

 

const CObject* cpObject = &NonConstObject;    // 상수 객체를 가르키는 포인터로 비상수 객체를

                                                                    // 가르켰지만 이상 없이 통과!

 

요것 또한 컴파일러의 ' 일련의 규칙 '

- 즉, 상수 객체를 가르키는 포인터( 참조 )를 초기화 할시 비 상수 객체를 이용하는 것이 가능하다 

에 포함돼니 컴파일러가 발벗고 나서 자동적으로 캐스팅한 결과 이상없이 컴파일돼는 것이다.

 

 

암시적인 타입 변환에 사용자가 개입(?)

 

컴파일러에 내장된( 즉 하드 코딩된 ) 암시적 타입변환에 대한 규칙 자체는 바꿀수 없다.

그러나 프로그래머가 몇가지 방법을 이용해

컴파일러가 암시적인 타입변환을 할수 있게 끔 힌트를 줄수도 있는데

그 중 하나가 암시적 타입 변환 연산자라고 하는 것이다.

 

이 연산자의 형태는 operator 캐스팅할 타입() 인데

보다시피 반환 값 타입이 존재하지 않고 operator 키워드 옆에 달랑 타입( 캐스팅 ) 만

덩그러니 놓여져 있다.

 

바로 예제로 들어갑시다.

 

//---------------------------------------------

//   클래스 CTest 

//---------------------------------------------

class CTest

{

   public:
      operator float()
      {   return static_cast< float >( m_Var );   }
   private:
      static const int m_Var = 5;
};

 

 

/* Client Code */

 

float Result = 0.0f;
const float Radian = 3.14f;

 

Result = Radian * CTest();

 

실수형 타입인 Radian 을 CTest 객체( 임시 객체 ) 와 곱해

역시 실수형인 Result 변수에 그 결과를 대입하는 실질적인 의미가 전혀 없는 예제이다.

 

컴파일 에러가 발생하지 않는게 오히려 이상한 상황으로 보이겠지만

위 코드는 아무 문제 없이 컴파일이 성공한다.

그럼 과연 위의 코드가 컴파일돼면서 어떤 일들이 벌어졌는지 대충 살펴 봅시다.

 

실질적으론 연산이 이뤄지는 세번째 줄을 보면

첫번째로 컴파일러는 Radian 변수와 CTest 객체간의 곱셈을 시도하려 할것이다.

 

컴파일러는 Radian 의 타입이 float 실수형이 므로 당연히 두번째 피 연산자

즉, 곱셈 연산자( * ) 의 오른쪽 항은 당연히 float 가 될것이라고 예상하지만

안타 깝게도 오른쪽 항엔 왠 CTest 타입의 객체가 자리를 잡고 있다.

 

당황스런(?) 컴파일러는 이제부터 바쁘게 CTest 타입이 float ( Radian 의 타입 ) 타입으로

변환 될수 있는지 알아보러 다니기 시작한다.

 

맨 먼저 컴파일러에 내장된 암시적 타입 변환이 일어 날수 있는 규칙 을 살펴보지만

거기에 사용자 정의 타입인 CTest 에 관한 부분이 있을 턱이 없다.

 

그 다음으로 찾게 돼는 부분이 바로 ( 피연산자에 대한 )암시적 타입변환 연산자 이다.

다행히도 operator float() 란 놈이 있으니 컴파일러는 이제야 안도의 한숨을 쉬며

잽싸게 CTest 객체를 float 형으로 변환하고 무사히 위의 연산은 수행된다.

 

 

매개 변수가 하나인 생성자

 

컴파일러가 암시적인 타입변환을 할수 있게 끔 빌미( ? )를 제공하는 두번째 방법은

매개 변수 를 하나 받는 생성자를 정의 하는 것이다.

 

* 이제 부터 사용할 예는 EC++ 항목 24의 것을 거의 그대로 가져오는것이다.

 

//-------------------------------------

//   유리수를 추상화한 Rational 클래스

//-------------------------------------

class Rational

{

   ...

   //----------------------------------

   //   생성자

   //----------------------------------

   public:

      Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

   ...

   //----------------------------------

   //   연산자 재정의

   //----------------------------------

   public:

      void operator=( const Rational& rhs );                // 대입 연산자

      void operator*( const Rational& rhs );                // 곱셈 연산자

   ... 

};

 

유리수 체계를 추상화한 Rational 클래스는

생성자의 인자로 분자, 분모에 해당하는 정수를 받는다.

그런데 보다시피 분모인 denominator 는 디폴트 값 1을 취하므로

기본적으로 이 생성자는 하나의 인자를 받는것으로 볼수 있다.

Rational 클래스를 사용하는 예는 아마도 다음과 같을것이다.

 

Rational a( 3, 4 );    // 유리수 3 / 4

Rational b( 2 );        // 유리수 2 / 1, 즉 그냥 정수 2

 

일반적인 상식적으로 정수와 유리수와의 연산( 곱셈, 덧셈 등 )은 전혀 이상한 일이 아니다.

 

b * 3;

 

그러니깐 위와 같은 일,

유리수 b( 사용자 정의 )  와 정수 '3' ( 기본 형 )를 곱하는 것이 유효한 식이

아무래도 자연스럽다는 것이다.

 

상식적으로 보자면 둘의 타입은 전혀 호환 될것 같아 보이지 않아서

위의 식은 보기 좋기 실패할것 같지만 컴파일러에겐 유효한 식이다.

그럼 과연 뭐가 어떻게 돌아가길래 위 식이 성공하는지 살펴 봅시다.

 

일단 컴파일러 입장에서 위 식은 다음과 같이 해석된다.

 

b.operator*( 3 );    // b( Rational 클래스 )의 operator '*' () 이 호출

 

문제는 Rational 클래스의 operator '*' 는 정수를 취하지 않는다는것이다.

 

      void operator*( const Rational& rhs );                // 곱셈 연산자

 

보다시피 Rational 클래스의 정의된 operator '*' 연산자는 인자로 Rational 객체의 참조를 받고 있다.

유리수 객체( 참조 )가 들어갈 자리에 정수를 넣었는데도 컴파일 상 아무런 문제가 없다는것은

결국 컴파일러란 놈이 정수를 유리수 객체 타입으로 암시적 캐스팅을 수행했다는 것이다.

 

그럼 대체 어떻게 정수( int )가 유리수( Rational ) 객체로 탈바꿈 할수 있는것인가?

 

범인은 바로 요놈이다.

 

      Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

 

앞서 말했다시피 매개 변수가 하나인 생성자는 컴파일러가 암시적인 타입변환을 수행하는데

힌트를 제공한다고 했다.

 

이 생성자는 정수를 Rational 타입으로 바꾸는 역할도 수행한다.

 

즉, 매개변수가 하나인 생성자는 그 매개 변수 타입을 자신의( 생성자가 속한 ) 클래스 타입으로

변환 가능하다는 조건을 컴파일러에게 제공한다는 얘기다.

 

그러니까 결과적으로 위의 b * 3 이란 식은 컴파일러가 다음과 같이 해석한다는 거다.

 

       b.operator*( Rational( 3 ) );

 

그렇다고 ' 그렇다면 정수 타입은 무조건 Rational 객체로 바뀔수 있는 거군. 훗... 역시 난 천재'

라고 생각하면 좀 곤란한다.

 

위의 ' 매개 변수가 하나인 생성자를 통한 타입 변환 '이 성공하기 위해선 한 가지 조건이 필요하다.

- 뭐 앞에서 설명하는 과정에 이미 들어갔지만

 

그 조건은 바로 ' Rational 객체를 매개 변수로 갖고 있는 연산자가 호출될 경우' 이고

정수 타입의 인자가 Rational 타입인 매개 변수에 전달될때

비로서 정수 타입이 Rational 타입으로 캐스팅 된다는 것이다.

 

앞서 봤다시피 b * 3 은 우선 ' b.operator( 3 ) ' 으로 해석된다고 했는데

이때 operator '*' 는 const Rational& 타입을 인자로 취한다고 했다.

이상태에서 정수( 3 )은 const Rational& 타입의 임시 변수에 대입돼는데

- 인자 전달 매커니즘 상

이때야 비로소 ' Rational( int ) ' 생성자가 개입(?)해 정수 형을 Rational 타입으로

바꿀수 있다는 것이다.

 

가령, b * 3 을 뒤집어 3 * b 를 만들어 버리면 결과는 어떻게 될것인가?

 

어떻게든 될거라고 생각 했겠지만 얄짤없이 컴파일 실패가 발생한다.

 

위에서 정수(타입)은 Rational 타입으로 암시적 변환된다고 했지만( Rational( int ) 에 의해 )

전제 조건을 만족 시키지 못했다. 즉, 정수 3 이 Rational 객체로 변환 되기 위해선

그 자리에 Rational 객체 타입을 인자로 받는 operator '*' 가 호출되야 한다.

 

그러니까 이런 놈이 필요하다고 쩝...

 

const Rational operator*( const Rational& Arg1, const Rational& Arg2 );

 

그러면 컴파일러의 해석상 operator*( 3, b ) 이렇게 될것이고

 

3 이 전달될 자리의 매개 변수 타입이 const Rational& 이므로 적법한

( 암시적인 )타입 변환이 발생해 위식은 무사히 컴파일 된다.

- 여기서 b 야 원래 Rational 객체니 두말하면 잔소리고

 

정수 3이 들어갈 자리에 Rational 객체를 받는 '*' 연산자가 필요하다고 했는데

일단 이 '*' 연산자는 멤버 '*' 연산자던 , 전역 '*' 연산자던 상관 없지만 3 이 클래스가 아니므로

저 식이 유효하려면 이경우 무조건 전역 '*' 연산자 만 가능하다.

 

만일, 매개 변수가 하나인 생성자가 암시적 타입변환에 대한 빌미를 제공하는 것에 대해

불만이 있는 사람이라면 ' explicit ' 키워드를 생성자 앞에 붙이도록.

 

      explicit Rational( int numerator, int denominator = 1 );     // 분자, 분모( = 1 )를 취하는 생성자

 

이러면 앞서 가능했던 부분( int 를 Rational 로 )이 불가능해진다.

즉, b * 3 이란 식은 가차없이 컴파일 에러로 지목된다는 말이다.

 

물론 암시적 타입 변환이 좋을 마냥 좋을 경우만 있는 것은 아니므로 explicit 키워드로

막아야 될 때도 있을것이니 알아서 상황에 맞게 조율하도록 합시다.
 

반응형
반응형


그냥 아래처럼 다른것도 응용해서 사용하면 된다

 

std::ostream& operator<<( std::ostream& cout, D3DXVECTOR3 v ){

 return cout;
}

반응형
반응형

http://bgashin.tistory.com/127

 

 

 

 

UX - 유저 경험 기반의 인터페이스 작성.
유저들이 경험한 장점, 단점을 가져와서 UI를 만드는 작업이다.
이것에 관련된 연구가 한창 진행중이며, 책들도 나오고 있다. 게임에 이것을 사용하는 것은 매우 중요하다.

시계 바늘과 눈금이 어긋남
- 3.14대신,  표준 원주율을 써서 구한다.
#define _USE_MATH_DEFINES
#include <math.h>
M_PI를 3.14대신에 쓴다.
 



WM_KEYDOWN:
키보드가 눌러졌는지 상태 값만 본다. Ex)A를 눌렀으면 눌렀다는 값만ㄷ ㅡㄹ어옴
WM_CHAR:
문자를 받는다.
이 부류는 게임에서 잘 안쓴다. 쓰는 공간이 한정되어 있고, 뗏다 눌렀다를 체크가능하기 때문이다.
----------
하지만 게임에서는 얘네들 보다는 GetAsyncKeyState를 쓴다.
눌렀는지, 뗐는지, 누른중인지 체크한다. 아무곳에서나 체크 할 수 있다.
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0))
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000 ? 0 : 1))

Wparam은 어떤 값이 들어오있는지 lparam은 상태
마우스 x는 하위비트 lparam
마우스 y는 상위비트 lparam
버튼에는 wparam 값을 넣고 Button & MK_SHIFT 쉬프트 눌린상태로 버튼이 눌러졌느냐?

더블유엠페인트에서 겟디시쓰면 안되는 이유. 페인트는 반다 그렸다는 반환값을 돌려주지만 겟디씨는 반환값이 없어서 무한루프를 돈다.
 

반응형
반응형

#include <iostream>
using namespace std;

class A{
public :
 virtual void initialize(){
  cout<<"base initialize"<<endl;
 } 
 virtual float integrate(float dt){ return 0;}  
};

class B : public A{
public :
 void initialize(){
  
 }
 static float integrate(float dt){
  cout<<"derived initialize"<<endl;
 }
};


int main(){

 B derived;

 A* pBase= &derived;

 pBase->initialize();                             //A 클래스의 initialize() 가 호출된다

 return 0;
}

 

B 의  static float integrate(float dt)  ==>  float integrate(float dt) 로 고치면 당연히 B 의 initialize() 가 호출된다

 


반응형
반응형

잘 정리된 포스트

const_cast 는 포스트 하지 않았는데 간단하기 때문이다

상수성 const 를 제거한다


아래는 포스트 내용

 

 

http://prostars.tistory.com/55 

 

 

C++의 4가지 캐스트 연산자에 대한 이야기 중 두 번째다.
이번은 그중에서 dynamic_cast 에 대해서 이야기한다.

dynamic_cast 는 상속 관계 안에서 포인터나 참조자의 타입을 기본 클래스에서 파생 클래스로의 다운 캐스팅과 다중 상속에서 기본 클래스 간의 안전한 타입 캐스팅에 사용된다.
안전한 타입 캐스팅이란 런타임에 타입 검사를 한다는 것이며 아래에 조금 더 자세하게 나온다.
const_cast와 같이 다른 용도로는 사용하지 못하며 용도가 명확하다.
참고로 dynamic_cast 를 사용하려면 기본적으로 다형성은 이해를 하고 있어야 하며 RTTI도 이해하고 있다면 이 글을 볼 필요가 없을 것이다.


객체가 위치한 메모리의 시작부분을 찾는 데도 사용된다는데 사용해 본 적이 없다.
객체를 void* 로 dynamic_cast 하면 시작 주소가 나온다.
[예] void* p = dynamic_cast<void*>( ObjectPointer );

 

- dynamic_cast 사용
dynamic_cast 를 사용하기 전에 제약 사항을 확인하자.
나름 제약 사항이 많다.

•상속 관계 안에서만 사용할 수 있다.

•하나 이상의 가상함수를 가지고 있어야 한다.
•컴파일러의 RTTI 설정이 켜져 있어야 한다.

[예제 1]
class Base
{
public :
    virtual void Put( void ) { cout << "Base" << endl; }
};

class Derived : public Base
{
public :
    void Put( void ) { cout << "Derived" << endl; }
 };

int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Base;
    Base* pDerived1 = new Derived;
    Derived* pDerived2 = new Derived;
    Derived* pDerived = NULL;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pDerived = pBase;

    // 컴파일 성공 : 런타임에 타입 변환에 실패하며 널을 리턴한다.
    pDerived = dynamic_cast<Derived*>( pBase );        
    if ( pDerived == NULL )
        cout << "Runtime Error" << endl;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pDerived = pDerived1;

    // 컴파일 성공 : 런타임에 타입 변환에 성공하며 Derived 타입의 포인터를 리턴한다.
    pDerived = dynamic_cast<Derived*>( pDerived1 );
    if ( pDerived )
        pDerived->Put();

    // 컴파일 성공 : 이런 경우에는 캐스팅이 필요 없다.
    pDerived = pDerived2;
}


위의 [예제 1] 에서 dynamic_cast 의 기본 동작을 볼 수 있다.
[예제 1] 에서 중요한 내용은 'pDerived = dynamic_cast<Derived*>( pBase );' 에서 볼 수 있듯이 포인터가 실제로 가리키는 대상이 기본 클래스의 객체라면 변환은 실패한다는 것이다.
dynamic_cast 가 캐스팅해주는 것은 포인터나 참조자의 타입을 다운 캐스팅하는 것이지 객체의 타입을 캐스팅하지는 못한다.

dynamic_cast 는 캐스팅에 실패할 때 대상이 포인터라면 널을 리턴하고 참조자였다면 bad_cast 예외를 던진다.
이것이 위에서 언급한 '안전한 타입 캐스팅'의 의미다.
A에서 B로 포인터의 타입을 캐스팅하는 것이 문제없는지 런타임에 검사하여 처리할 수 있다.

다중 상속의 상황에서 기본 클래스 간의 타입 캐스팅을 보자.


[예제 2]
class BaseOne
{
public :
    virtual void Put( void ) { cout << "BaseOne" << endl; }
};

class BaseTwo
{
public :
    virtual void Put( void ) { cout << "BaseTwo" << endl; }
};

class Derived : public BaseOne, public BaseTwo
{
public :
    void Put( void ) { cout << "Derived" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    BaseOne* pBaseOne = NULL;
    BaseTwo* pBaseTwo = new Derived;

    // 컴파일 오류 : 타입 변환을 할 수 없다.
    //pBaseOne = pBaseTwo;

    // 컴파일 성공 : 런타임에 타입 변환에 성공하며 BaseOne 타입의 포인터를 리턴한다.
    pBaseOne = dynamic_cast<BaseOne*>( pBaseTwo );
    if ( pBaseOne )
        pBaseOne->Put();
    
    return 0;
}


[예제 2] 는 아래 [그림 1] 처럼 다중 상속 관계의 클래스 구성에서 기본 클래스 간의 타입 캐스팅을 보여준다.

 

 

사용자 삽입 이미지
[예제 2] 와 같은 캐스팅의 한 사용 예로는 각 기본 클래스 포인터(또는 참조자) 타입의 컨테이너 혹은 배열을 운용하면서 서로 간의 요소를 교환할 때 사용할 수 있다.

[예제 1] 과 같은 캐스팅을 다운 캐스팅이라고 하며 [예제 2] 와 같은 캐스팅을 크로스 캐스팅이라고 한다.
별도로 적지는 않았지만 파생 클래스에서 기본 클래스로의 캐스팅을 업 캐스팅이라고 한다.
업 캐스팅은 캐스팅 연산자가 있으나 없으나 안전하게 캐스팅된다.

- 참고 사항
다중 상속이 발생한다면 설계를 다시 검토하라는 말이 있듯이(있나..-_-?) 다중 상속은 그리 권장하지 않는 방식이다.

 


http://prostars.tistory.com/65  중에서..

 

 - reinterpret_cast 사용
reinterpret_cast 를 사용하기 전에 용도와 제약 사항을 확인하자.
전혀 관계없는 타입 간의 변환
상속관계가 없는 클래스 간의 변환 포함
const_cast 의 기능은 수행하지 못함

 

 


http://prostars.tistory.com/64

 

++의 4가지 캐스트 연산자에 대한 이야기 중 세 번째다.
이번은 그중에서 static_cast 에 대해서 이야기한다.

static_cast 는 기본적으로 C 스타일의 캐스팅과 가장 비슷한 기능을 한다.
물론 C 스타일의 캐스팅처럼 만능은 아니다. 4가지의 캐스트 연산자로 분리된 만큼 const_cast의 역할인 상수성을 날린다거나 하는 등의 다른 캐스트 연산자의 고유 기능은 수행하지 못한다.
다른 캐스트 연산자와 같이 static_cast 도 static_cast 만의 용도가 있다.

- static_cast 사용
static_cast 를 사용하기 전에 용도와 제약 사항을 확인하자.
실수형과 정수형, 정수형과 열거형등의 기본 데이터 타입 간의 변환
상속관계의 클래스 계층 간의 변환
런타임 타입 검사를 하지 않음
다형성이 없어도 변환 가능 (RTTI 옵션이 꺼져있어도 된다)
다중 상속에서 기본 클래스 간의 타입 변환은 못함
void 포인터를 다른 타입의 포인터로 변환
서로 다른 타입의 포인터 간의 타입 변환은 못함

서로 다른 타입의 포인터 간의 타입 변환에 reinterpret_cast 를 사용하기보다는 void 포인터를 경유하는 방식을 추천한다.

[예제 1]
class BaseOne
{
public :
    virtual void Put( void ) { cout << "BaseOne" << endl; }
};

class BaseTwo
{
public :
    virtual void Put( void ) { cout << "BaseTwo" << endl; }
};

class Derived : public BaseOne, public BaseTwo
{
public :
    void Put( void ) { cout << "Derived" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int IntValue = 0;
    double DoubleValue = 100.0;

    // 암시적 캐스팅 : 경고 발생
    IntValue = DoubleValue;
    // 명시적 캐스팅 : 경고 없음
    IntValue = static_cast<int>( DoubleValue );

    // 컴파일 오류 : 변환할 수 없는 타입
    //char* pChar = static_cast<char*>( &IntValue );

    // 컴파일 성공 : void 포인터에서 변환은 가능
    void* pVoid = &IntValue;
    char* pChar = static_cast<char*>( pVoid );

    BaseOne* pBaseOne = NULL;
    BaseTwo* pBaseTwo = new Derived;
    Derived*    pDerived = NULL;

    // 컴파일 성공 : 계층간 타입 변환이 가능 (타입 안전 검사는 안함)
    pDerived = static_cast<Derived*>( pBaseTwo );

    // 컴파일 오류 : 변환할 수 없는 타입 (dynamic_cast 필요)
    //pBaseOne = static_cast<BaseOne*>( pBaseTwo );   

    return 0;
}
위의 [예제 1] 에서 static_cast 의 기본 동작을 볼 수 있다.
[예제 1] 의 'pDerived = static_cast<Derived*>( pBaseTwo );' 에서 타입 변환은 컴파일 타임에 끝난다.
pBaseTwo 에 NULL 이 들어가 있어도 컴파일은 성공한다.
즉, 변환하려는 포인터가 계층 관계에 있다면 어떤 값을 가지고 있던지 컴파일은 성공한다는 말이다.
문제는 런타임에 발생할 것이고 대부분 프로그램의 다운으로 이어질 것이다.
이런 문제점 때문에 클래스 계층 간의 타입 변환에 dynamic_cast 를 추천하는 것이다.

[예제 1] 에는 재미있는 내용이 하나 있다.
서로 다른 타입의 포인터 간의 변환은 금지지만 void 포인터를 경유한다면 가능하다.
'char* pChar = static_cast<char*>( &IntValue );' 이것은 컴파일이 안되지만,
'void* pVoid = &IntValue;
 char* pChar = static_cast<char*>( pVoid );' 이것은 컴파일이 된다. 물론 포인터 값도 정상적으로 넘어온다.

 

반응형
반응형

자세히 나와있는 블로그

 

http://mooyou.tistory.com/42

 

 

따라서 이를 빠르게 발견하고 해결하는 것이 중요합니다.

여기서는 C코드로 짠(콘솔 응용프로그램) 환경에서 메모리릭을 발견하고 잡는 방법을 알아보겠습니다.

다음과 같이 메인 프로젝트를 만들고 메모리릭을 감지하는 코드를 추가합니다. (강조된 부분)



// MemoryLeak.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
#include "stdafx.h"
#ifdef _DEBUG
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
// 메모리릭 발생코드
char * szName = new char[100];
szName = "mooyou";
printf("%s", szName);
return 0;




위의 코드를 실행하면 출력창에 메모리릭이 발생되었다고 나타납니다.

출력 정보를 보면 memoryleak.cpp 파일의 18번째 라인에서 100 bytes 크기의 메모리릭이 발생되었습니다.

이 정보만으로도 충분히 메모리릭을 잡을수 있습니다.



여기서 더 나아가 메모리 블록 위치 정보를 통해서 직접적으로 메모리릭이 발생한 코드로 이동까지 해보겠습니다.

이를위해 다음과 같이 _CrtSetDbgFlag 바로아래 _CrtSetBreakAlloc( 97 ); 코드를 추가합니다.

이는 메모리릭 발생 위치에 BreakPoint를 설정합니다.




// MemoryLeak.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
#include "stdafx.h"
#ifdef _DEBUG
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
_CrtSetBreakAlloc( 97 );
#endif
// 메모리릭 발생코드
char * szName = new char[100];
szName = "mooyou";
printf("%s", szName);
return 0;




즉, 출력된 메모리 블록 위치 정보를 이용하여 BreakPoint를 설정하고, 실행시 설정된 곳으로 이동하겠다라는 것입니다 !

코드를 추가하고 실행하면 다음과 같은 메시지 박스가 나타납니다.

BreakPoint에 의해 중단점을 트리거 했다라는 내용이고 여기서는 중단을 누르면 됩니다.

하지만 누르고 나서 코드를 보면 메모리릭이 발생한 원하는 위치가 아닌 dbgheap.c 파일의 내용이 보이게 됩니다.

모르는 코드화면이 나왔다고 해서 전혀 걱정할 필요가 없습니다. 정상적인 화면이고... 제대로 잘 찾아온 것입니다.

메모리릭이 발생한 곳으로 이동하려면 호출 스택에서 작업위치를 찾아가면 됩니다.

호출 스택은 보통 출력창과 같은 탭에 있고, 없다면 Ctrl + Alt + C를 눌러 호출 스택을 불러옵니다.

호출 스택은 현재 프로그램에서 실행된 명령을 순차적으로 나열한 것으로 맨 위에 찍힌 스택이 최근에 사용한 명령입니다.

현재 보이는 위치(노란색 화살표)가 msvcr100.dll로 dbgheap.c의 내용을 가르킵니다.

즉, 작업한 코드 영역이 아니죠. 따라서 작업한 영역인 MemoryLeak 스택을 더블클릭하여 이동합니다.

짜잔~ 왼쪽에 초록색 화살표가 보이시나요?

호출 스택을 이용하여 메모리릭이 발생한 위치로 이동하였습니다.




반응형
반응형

[1번]

 

float dd1=123.00993;
int ddd=*((int*)(&dd1));

float gg= *((float*)(&ddd));

 

이렇게 해야지만 float 값이 다시 gg 에 나타난다

 


 

 

[2번]

float dd1=123.00993;
int ddd=dd1;

float gg= ddd;

 

이렇게 하면 나타나는 값은 gg==123이 된다

 

즉 상황에 따라 부동소수점의 값을 유지하고 싶다면 [1번]을 간단히 정수부분만을 때어내고 싶다면 [2번] 을 사용하면 된다

반응형
반응형

아래글 원문의 주소는

http://cafe.naver.com/cplc/83117

이지만 글을 조금 수정합니다, 추가한 붉은 글씨가 수정한 부분입니다

 

 

32bit 부동소숫점 연산식을 알아야 합니다.

단순히 데이터 자릿수가 실수로 변환되어지는 것은 아니고요.. 실제론 꽤 복잡한 연산식으로 만들어진 것입니다.

 

32비트의 각 자릿수 마다 정해진 규정이 있고요 풀어보자면,,,,,

맨 끝 비트(32번째 비트)가 부호를 결정짓고요 간단히 부호(s)라고 정의합니다.

그다음 24번째 비트부터 31번째 비트까지의 8비트(1byte)가 지수입니다. 이를 e라고 정해두고,

그다음 맨처음 비트부터 23번째 비트까지가 가수입니다.  이를 m이라 정한다면

 

  부동소숫점 = (-1)^s * 2^(e-127) *1.m

 

 

지수부(2^(e-127))는 첫번째 비트부터 2의 승수를 곱해주는데 즉,2^0,2^1, 2^2, 2^3, 2^4, 2^5, 2^6, 2^7의 값을 첫번째 비트부터 순서대로 곱하도록 하고

 그냥 지수부를 비트열로 표현하면 2^e 로 표현되지만 현재는 DWORD 에서 float 로 변환하는 과정임으로 (2^(e-127)) 와 같은 식이 사용된다

 이렇게 변환하는 이유는  float 의 표현 범위를 늘리기 위함

 

가수부(1.m)는 마지막 비트(23번째=>이미지 숫자상으론 22) 부터 2의 마이너스 승수를 곱해야 합니다.

 

                   즉, 2^-1, 2^-2, 2^-3, 2^-4 .......... 2^-23의 값을 마지막 비트부터 순서대로 곱해야 합니다.

                    => 2^-2 = 1/(2^2) 즉 소수점 부분을 표현하기 위한 수치

 

 

 

 

32bit의 데이터값이 16#4015c28f 이란 것을 기준해서 풀어보도록 해볼까요?

먼저 이 데이터를 2진수로 변환해 봐야 해당된 비트의 상태를 알 수 있습니다.

변환해 보면..... 휘리릭...!!~~

 

(16진수)16#4015c28f  => (2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111 로 되는 것을 알 수 있습니다..

앞의 16#, 2# 가 진수 표현을 나타냄

 

어떻게 변환됬냐구요?

그냥 16진수의 1자리를 4자리의 2진수로 바꾸면 됩니다.

 

    첫번째 비트는 2^0   ---  1

    두번째 비트는 2^1   ---  2

    세번째 비트는 2^2   ---  4

    네번째 비트는 2^3   ---  8

                    => 2^n ( 0<=n <= 8 )

 

  - 0000 = 16#0

  - 0001 = 16#1

  - 0010 = 16#2

  - 0011 = 16#3

  - 0100 = 16#4

  - 0101 = 16#5         : 대충 눈치 채셨죠?

  - 0110 = 16#6

  - 0111 = 16#7

  - 1000 = 16#8

  - 1001 = 16#9          : 아직 모르시겠나요?

  - 1010 = 10 = 16#a   : 16진수에선 0 ~ f 까지랍니다.

  - 1011 = 11 = 16#b

  - 1100 = 12 = 16#c   : 이젠 아셨기를...

  - 1101 = 13 = 16#d

  - 1110 = 14 = 16#e

  - 1111 = 15 = 16#f    : 다 해버렸네...

 

그럼 본론으로 들어가서...

정리해서 보면

 

(2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111

 

[float 로 변환]

 

  [부호] = 2#0 = 0  

  [지수] = 2#0100 0000 0 = 1000 0000

              2#0 에서2# 다음에 오는 0 은 이미 부호비트로 취급했음으로 다음인 1 을 포함한 8(지수부)의 비트열을 가져옴 

            = (1*2^7) + (0*2^6) + (0*2^5) + (0*2^4) + (0*2^3) + (0*2^2) + (0*2^1) + (0*2^0)

            = (1*128) + (0*64) + (0*32) + (0*16) + (0*8) + (0*4) + (0*2) + (0*1) = 128+0+0+0+0+0+0+0 = 128

 

  [가수] = 001 0101 1100 0010 1000 1111 = (0*2^-1) + (0*2^-2) + (1*2^-3) + (0*2^-4) + (1*2^-5) + (0*2^-6) + (1*2^-7) +

              (1*2^-8) + (1*2^-9) + (0*2^-10) + (0*2^-11) + (0*2^-12) + (0*2^-13) + (1*2^-14) + (0*2^-15) + (1*2^-16) +

              (0*2^-17) + (0*2^-18) + (0*2^-19) + (1*2^-20) + (0*2^-21) + (0*2^-22) + (0*2^-23)

            = (0*0.5) + (0*0.25) + (1*0.125) + (0*0.625) + (1*0.3125) + (0*0.015625) + (1*0.0078125) + (1*0.00390625) +

              (1*0.001953125) + (0*0.0009765625) + (0*0.00048828125) + (0*0.000244140625) + (0*0.0001220703125) +

              (1*0.00006103515625) + (0*0.000030517578125) + (1*0.0000152587890625) + (0*0.00000762939453125) +

              (0*0.000003814697265625) + (0*0.0000019073486328125) + (1*0.00000095367431640625) +

              (1*0.000000476837158203125) + (1*0.000000238418579101562) + (1*0.000000119209289550781)

            = 0.169999957084656

 

결과를 계산식에 대입해 보면

    (-1)^0 * 1.169999957084656 * 2^(128-127)

  = 1 * 1.169999957084656 * 2

  = 2.33999991416931

  ≒ 2.34 

 

정리하느라 2시간 걸렸습니다.  -_-;;;

직접 계산하려고 하시지 말고 GM-WIN에서 [REAL to DWORD] 와 [DWORD to REAL] 펑션을 이용해서

시뮬레이션 해보시는게 건강에 이로울 듯 하네요...

 


[float 로 변환과 그 역]

 

 이러한 연산 가수부분의 변환 연산 과정이 있음으로 float 에서 DWORD 로 변환 할대는 가수부를 정수부형태 즉

16#...... 의 형태로 변환 해야 하기 때문에

float 값을 (DWORD) 로 그냥 캐스팅하면 정수부분만 나오게 되고 float 전체 값을 캐스팅 하기 위해선

 

float a;

 

*((DWORD*)&a)

 

의 방법으로 캐스팅하면 float 값을 DWORD 로 변환한 값을 얻을 수 있게 된다

 


[더하는 말로..]

 

directx 에서

float PointSize = 10.0f;
   _pD3dDevice->SetRenderState( D3DRS_POINTSIZE , *((DWORD*)&PointSize));

 

의 함수중 *((DWORD*)&PointSize 이것을 이렇게 변환 하는 이유를 추측해 보자면 이 함수안에서 사용 되는 값이

DWORD 값을 float 로 변환해 쓰이기 때문에 이전에 float 값을 DWORD 로 변환한 값 형태로 넘겨줘야 하는 것 같다

 

 

반응형
반응형


A 클래스  함수내에서 함수포인터로 넘어온 함수를 실행시키기 위해선

 

A 클래스의 this 포인터로 넘어온(또는 인자로 받은) 함수포인터 타입을 실행 시켜야 한다

 

fn 이 함수 포인터 타입이고 어떤 함수가 A의 소식이면서 fn 의 변수로 넘어왔을때

 

 

A에선

void method{

  (this->*fn)();

}

 

으로 호출하면 된다

 

괄호가 중요!


BLOG main image






http://blog.naver.com/sorkelf?Redirect=Log&logNo=40135728128



함수포인터이긴 하지만


클래스에 멤버함수를 함수포인터로 호출 하려면 어떻게 할까?


함수포인터도 어디까지 포인터이므로


멤버함수의 주소를 넘겨 주되 클래스에 소속되어 있으므로


클래스의 scope 연산자 (::)를 반드시 명시해야 한다


또한 어디까지나 멤버함수이므로 함수포인터로 호출시에도


반드시 해당하는 그의 객체가 존재해야 한다


백문이 불여일견.. 코드를 보는게 빠를 듯..









http://www.cyworld.com/blue_wons/7209444


[STL 응용] Map으로 문자열과 함수포인터 매핑하기.




@ 이것을 보게된 계기
 cmd창에 명령어를 if else 구문으로 처리하다가, 좀더 깔끔하면서도 관리되는 방식의 명령어 정리는 하지 못할까라는 생각을 했고
명령어가 많아질수록 제어구문의 길이도 길어지는것을 감안하여 검색하던중, 스택오버플로우에 정확한 구현내용을 포착
이에 한번 따라서 만들어봤습니다. 위 참고 출처에는 클래스 객체 자신 내부함수를 참조하는 것이 아니기때문에
비교하면서 봐야 합니다.

#include <map>
#include <iostream>
#include <string>

class Type
{
typedef void (Type::*FuncPointer)();
typedef map<std::string, FuncPointer > FuncMap;
typedef map<std::string, FuncPointer>::iterator FuncMapIter;

FuncMap _funcMap;
FuncMapIter _pos;
string buff;

public:
Type(){};
~Type(){};

LoadFunc()
{
//왼쪽 값이 key 이면서 first, 오른쪽 값이 value 이면서 second
_funcMap["Start"] = &Type::Start; //굳이 이런식으로 넣지 않아도 된다.
//_funcMap.Insert(make_pair("Start", &Type::Start)); //이렇게 쑤셔박아도 된다.
}

ActivateFunction()
{
cin>>buff;
_pos = funcMap.find(buff);
if(_pos != funcMap.end())
{
FuncPointer func = _pos->second;
(this->*func)(); //참조할때가 가장 중요하다.
//이것은 자기 자신 내부라서 (this->*func)() 이런식으로 참조를 했지만
//객체 포인터가 아닌경우 (name).*func() 이런식으로 참조가 가능하다.
//자칫 잘못하면, 컴파일러가 잘못된 참조 갯수*3배만큼 애러를 토한다.
}
}

private:
void Start(){  std::cout<<"Start function !"<<std::endl;
}


[출처] 멤버 함수 포인터|작성자 풍풍풍



반응형
반응형

http://blog.naver.com/qktxhtkdl/10110879615

 


UINT_PTR (32비트 및 64비트 호환 포인터연산)   

#include "stdafx.h"
#include <Windows.h>
UINT_PTR CalDistance(UINT_PTR a, UINT_PTR b)
{
 return a-b;
}
int _tmain(void)
{
 INT32 val1 = 10;
 INT32 val2 = 20;
 _tprintf(_T("distance : %d \n") , CalDistance((UINT_PTR)&val1, (UINT_PTR)&val2));
 return 0;
}
 


32비트 시스템과 64비트 시스템은 주소값의 범위가 다르기때문에
 
상호 호환이 가능한 코드를 사용하는 것이 좋다.
 
Polymorphic타입의 자료형을 사용해서 매크로정의에 따라 32비트 & 64비트 호환이 되는 코드는 위처럼 UINT_PTR을 사용한다.
 
PTR은 포인터 자체가 아닌, 포인터 연산을 위해 존재한다는 뜻으로 해석하면 된다.

 


 

 

http://vvalkyrie.tistory.com/705

 

INT_PTR 형 분류없음 2007/11/13 11:47
64비트 호환 프로그래밍을 위해 _PTR 접미사 (type for pointer precision의 뜻)가 붙는 형들이 새로 정의되었다는 것을 알았다. 포인터 연산을 위해 포인터 변수를 정수형 변수로 type casting 할 때 발생하는 문제를 해결하기 위해 도입된 듯. 이 중 UINT의 _PTR 형인 UINT_PTR 형은 다음과 같이 정의되어 있다.

[CODE]#if defined(_WIN64)
    typedef unsigned __int64 UINT_PTR;
#else
    typedef unsigned int UINT_PTR;
#endif[/CODE]
원래 이걸 찾게 된 이유는 SOCKET 형의 정체가 UINT_PTR 형이었기 때문이다.

winsock2.h:[CODE]typedef UINT_PTR SOCKET;[/CODE]

반응형

+ Recent posts