반응형

툴바와 상태바는 모두 콘트롤 바에서 상속받은 것으로, SDI나 MDI의 경우 기본적으로 나타나도록 되어 있습니다. 메인 프레임 클래스에 보면 이들이 멤버 변수로 설정되어 있는 것을 볼 수 있습니다.

CStatusBar  m_wndStatusBar;

CToolBar    m_wndToolBar;

 

기본적으로 "보기" 메뉴에 이들을 보이거나 숨기는 기능이 있지만 사용자가  새로운 컨트롤바를 첨가하였을 경우에는 이를 직접 처리해주어야 합니다.

 

툴바를 보이거나 숨기도록 해보겠습니다.

먼저 메뉴의 "보기"에 "새 도구모음"이라는 메뉴 항목을 만듭니다. 그리고 현재 툴바의 상태를 저장하기 위한 변수를 선언합니다.

BOOL m_bShowToolBar;

 

메뉴에 대한 COMMAND와 UPDATE_COMMAND_UI에 각각의 핸들러를 작성합니다.

콘트롤 바를 보이거나 숨기는 함수는 ShowControlBar로 이는 메인 프레임 클래스의 멤버 함수입니다. 메뉴 COMMAND에서 이를 조절합니다.

void CMainFrame::OnShowToolbar() 
{

    // TODO: Add your command handler code here
    if(m_bShowToolBar){
        m_bShowToolBar = FALSE;
        ShowControlBar(&m_wndToolBar, 
FALSE
, FALSE); // 숨기기
    }
    else{
        m_bShowToolBar = TRUE;
        ShowControlBar(&m_wndToolBar, 
TRUE
, FALSE); // 보이기
        }
}

 

 다음은 콘트롤 바가 보이는 경우 메뉴에 체크 표시를 해야 합니다. 이는 메뉴의

UPDATE_COMMAND_UI에서 해주면 됩니다.

 

void CMainFrame::OnUpdateShowToolbar(CCmdUI* pCmdUI) 
{
        // TODO: Add your command update UI handler code here
        BOOL visible = m_wndToolBar.IsWindowVisible();

 

        if(visible) pCmdUI->SetCheck(TRUE);
        else pCmdUI->SetCheck(FALSE);
}

 

콘트롤 바는 CWnd 클래스에서 상속을 받아 만들어졌기 때문에 IsWindowVisible 함수로 보이는 지의 여부를 알아낼 수 있습니다.

 

새로운 메뉴 항목은 기존의 "보기->도구모음" 메뉴 항목과 동일하게 동작합니다.


 

상태바나 사용자가 첨가한 툴바의 경우에도 같은 방법으로 처리가 가능합니다.

반응형
반응형

현재 마우스 위치의 윈도우 핸들 얻기 - WindowFromPoint()

등록자 : 유광희, 05-08-05 01:29:37

google_protectAndRun("render_ads.js::google_render_ad", google_handleError, google_render_ad); POINT  Point;
char ClassName[128];

GetCursorPos(&Point);

HWND hWnd = WindowFromPoint(Point);

GetClassName(hWnd, (char *)&ClassName, 128);

if (*ClassName)
OutputDebugString(ClassName);

반응형

'프로그래밍(Programming) > MFC&API' 카테고리의 다른 글

메뉴바 숨기기  (0) 2012.11.01
툴바,상태바 보이기/숨기기  (0) 2012.11.01
현재 마우스 좌표얻기  (0) 2012.11.01
AfxMessageBox 인자 총정리  (0) 2012.11.01
파일 세이브 다이얼로그  (0) 2012.11.01
반응형

GetCursorPos( &ptCursor );
 ScreenToClient( m_hWnd, &ptCursor );


반응형
반응형

http://blog.naver.com/cysatan?Redirect=Log&logNo=10051502240



1. MSDN 설명

int AfxMessageBox(
   LPCTSTR lpszText,
   UINT nType = MB_OK,
   UINT nIDHelp = 0 
);
int AFXAPI AfxMessageBox(
   UINT nIDPrompt,
   UINT nType = MB_OK,
   UINT nIDHelp = (UINT) -1 
);
lpszText

Points to a CString object or null-terminated string containing the message to be displayed in the message box.

nType

The style of the message box. Apply any of the < >message-box :Track(?ctl00_MainContent_cpe90361_c|ctl00_MainContent_ctl05?,this);?>to the box.

nIDHelp

The Help context ID for the message; 0 indicates the application's default Help context will be used.

nIDPrompt

A unique ID used to reference a string in the string table.

 

2 . 기본설정

 

int AfxMessageBox(LPCTSTR lpszText, UINT nType = MB_OK, UINT nIDHelp = 0);

 

MessageBox와는 달리 제목을 넣는 부분은 사라졌습니다.

 

Example)

 

AfxMessageBox(“메시지 박스 테스트”, MB_OK | MB_ICONEXCLAMATION);

 

 

nType은 다음 중에서 각각 하나씩 조합해서 사용할 수 있습니다.

 

버튼 스타일                                    의미

MB_ABORTRETRYIGNORE                [취소], [재시도], [무시]

MB_OK                                           [확인]

MB_OKCANCEL                               [확인], [취소]

MB_RETRYCANCEL                         [재시도], [취소]

MB_YESNO                                     [], [아니오]

MB_YESNOCANCEL                         [], [아니오], [취소]

 

 

아이콘 스타일                                  의미

MB_ICONEXCLAMATION                   [느낌표]

MB_ICONINFORMATION                    [느낌표]

MB_ICONQUESTION                          [물음표]

MB_ICONSTOP                                 [X]

 

 

디폴트 버튼                                     의미

MB_DEFBUTTON1                             첫 번째 버튼

MB_DEFBUTTON2                             두 번째 버튼

MB_DEFBUTTON3                             세 번째 버튼

 

 

모달리티                                         의미

MB_APPLMODAL                              메시지 박스를 종료시켜야 프로그램을 계속 진행할 수 있음

MB_SYSTEMMODAL                          메시지 박스를 종료시켜야 시스템을 사용할 수 있음

 

 

반환값                                            의미

IDABORT                                         [취소(Abort)]가 눌러 졌음

IDCANCEL                                       [취소(Cancel)]이 눌러 졌음

IDIGNORE                                        [무시]가 눌러 졌음

IDNO                                               [아니오]가 눌러 졌음

IDOK                                               [확인]이 눌러 졌음

IDRETRY                                          [재시도]가 눌러 졌음

IDYES                                             []가 눌러 졌음

3. 추가 옵션

 

CString temp;

int nScore;

nScore = 99;

 

temp.Format(_T("당신의 점수는 %d점입니다."), nScore);

AfxMessageBox(temp);

 

참고 : http://blog.naver.com/cysatan/10047219216

 

4. 기타

 

일일이 창띄워서 확인을 해야한다면 모르겠지만 아니라면

 

TRACE(""); 함수를 써도 괜찮은거같다.

 

괄호 안의 형식은 Afxmessagebox와 비슷하며 출력은 Output창에 표시된다.

 

 

 

출처: 0. http://msdn.microsoft.com/en-us/library/as6se7cb.aspx

1. AfxMessageBox()를 써 보자

2.  http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10104&eid=uFAjiv1k4JmCyEjQuiylTjEnq1MrK8xU&qb=YWZ4bWVzc2FnZWJveA==&enc=utf8&section=kin&rank=1&sort=0&spq=0&pid=fCbs6doi5UNsscSEda0sss--387710&sid=SkFx15BeQUoAAAr4MIk

반응형
반응형

// TODO: 여기에 명령 처리기 코드를 추가합니다.
 CFileDialog  ofn(FALSE);  //false : 저장 true 불러오기
 CString selectedFilePath;
 char szFilter[] = "3DS files (*.3DS)\0*.3DS\0\0";

 ofn.m_ofn.lpstrFilter = szFilter;
 ofn.m_ofn.Flags |= OFN_NOCHANGEDIR;
 if( ofn.DoModal() == IDOK)
 {
  selectedFilePath = ofn.GetPathName();
  C3dsExporter export3DS;
  selectedFilePath+=".3DS";
  char* fname=(LPSTR)(LPCSTR)selectedFilePath;
  export3DS.save3ds( C3DSParser::GetInstancePtr()->Get3DModel(), fname);
  


 }

반응형
반응형

char szFilter[] = "Image Files(*.BMP, *.GIF, *.JPG, *.TIF)|*.BMP;*.GIF;*.JPG;*.TIF|All Files(*.*)|*.*||";

 CFileDialog fileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter);
 if(IDOK == fileDlg.DoModal())
 {
  CString fpath =  fileDlg.GetPathName();
  OpenDocumentFile(fileDlg.GetPathName());
 }

반응형
반응형

과거 클래스 위자드에서 어떤 이벤트 메시지를 추가하면 적당한 위치에 알맞게 잘 들어갔던 반면, 지금은 추가를 하게 되면 코드의 가독성도 꽝이고, 무엇보다 전혀 의도하지 않은 위치에 들어가버린다. 그 중 메시지 맵에서 이벤트 메시지를 추가할 때 매우 조심스럽게 해야할 부분이 있어 글을 남긴다.


사용자가 이벤트 핸들러 함수를 추가 했을 경우, 또는 재정의 했을 경우 반드시 아래의 굵게 표시한 곳으로 옮기던지, 이전의 동일한 아이디의 핸들러 함수를 주석처리하여야 사용자의 함수가 정상적으로 호출된다.


BEGIN_MESSAGE_MAP(CMyToolApp, CWinApp)
    ON_COMMAND(ID_APP_ABOUT, &CMyToolApp::OnAppAbout)
    ON_COMMAND(ID_FILE_OPEN, &CMyToolApp::OnFileOpen)
    // 표준 파일을 기초로 하는 문서 명령입니다.
    ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
    //ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
    ON_COMMAND(ID_FILE_OPEN, &CMyToolApp::OnFileOpen)
END_MESSAGE_MAP()





CWinApp 를 상속받은 사용자 App 클래스가 있다고 하자. 그 클래스를 CMyToolApp 라고 하자. 그리고 프로그램에서 파일을 열고 새 파일을 만드는 아이디를 각각 ID_FILE_OPEN 과 ID_FILE_NEW 처럼 기본으로 정의되어 있는 아이디를 사용한다고 하자.

이때 ID_FILE_OPEN 을 재정의하여 몇가지 기능을 추가한다고 한다면 클래스 뷰에서 CMyToolApp 에서 속성창을 열고, 속성창에서 이벤트 아이콘을 누른 후 이벤트 핸들러 함수로 만들 아이디 ID_FILE_OPEN 에서 COMMAND 이벤트 헨들러 함수를 추가하면 자동으로 코드가 추가가된다.

그 중 아래의 메시지 맵에서 매우 조심해야하는 부분이 있다.


BEGIN_MESSAGE_MAP(CMyToolApp, CWinApp)
    ON_COMMAND(ID_APP_ABOUT, &CMyToolApp::OnAppAbout)
    // 표준 파일을 기초로 하는 문서 명령입니다.
    ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
    ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
    ON_COMMAND(ID_FILE_OPEN, &CMyToolApp::OnFileOpen)
END_MESSAGE_MAP()


자동으로 추가가 되는 위치가 위에서 굵게 표시한 곳이다.
하지만 저곳에 추가가 되면 다음과 같은 문제가 생긴다.

우리가 새롭게 재정의한, 추가한, OnFileOpen() 함수가 호출되지 않고 CWinApp 의 OnFileOpen() 함수가 호출이 되어버린다.

즉, 동일한 아이디는 가장 먼저 만나는, 소스 코드 상에서 위쪽에 있는 것만을 실행시키고 메시지 맵을 벗어나버린다. 마치 switch() 문에서 동일한 케이스가 있다면 위쪽의 case만 되는 것과 같다.

과거 VC++ 6.0 일 때는 위와 같은 상황에서 메시지 맵이 추가가 되는 부분이 정해져있었다.


BEGIN_MESSAGE_MAP(CMyToolApp, CWinApp)
    //{{AFX_MSG_MAP(CMyToolApp)
    ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
    ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
    //}}AFX_MSG_MAP
    // Standard file based document commands
    ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()


위에서 굵게 되어 있는 블럭 안에 사용자 이벤트 핸들러 함수를 자동으로 추가를 시켜줬었다. 항상 상위 클래스의 것보다 자신의 클래스의 핸들러 함수가 위쪽에 추가되었기 때문에 문제가 없었다.


반응형
반응형

mainframe 에서 ..

 

ON_COMMAND(ID_BUTTON_MAINMENU, OnMainMenu) //메시지맵에 툴바버튼 아이디와 함수를 넣어준다.

 

afx_msg void OnMainMenu(); //헤더파일에 함수를 선언한다

 

void CMainFrame::OnMainMenu() //소스파일에 함수를 정의한다.

{

 

}

반응형
반응형

해당 핸들을 제일 상단에 배치시킨다


::SetForegroundWindow(g_hWnd);

반응형
반응형

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://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 쉬프트 눌린상태로 버튼이 눌러졌느냐?

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

반응형

+ Recent posts