MoniWiki메모장_MFC
Login:
Password:
대문|찾기|바뀐글|목록|메모장|책갈피|연꽃|링크
Edit Diff Reload Search Print Info Mail Help RSS

@ 2004-06-16 @
^^* 여기에 정리된 내용은 책과 인터넷 등을 참고로 개인적인 목적으로 정리

  MFC
1 기타
2 (MFC) Class
2.1 (MFC) CImage 클래스
2.1.1 이미지를 출력하는 예 (MFC)
2.1.2 CImage를 이용한 이미지 파일 포맷 변환
2.1.3 화면 캡쳐하기
3 입력 도구
3.1 Button
3.2 Button/ CBitmapButton
3.3 ListCtrl
3.4 Property Sheet
3.5 CFileDialog에서 디렉토리를 설정하기
3.6 Check Box 체크하기
4 함수
4.1 OnInitDialog() 함수; 대화상자가 화면에 처음 나타날 때 호출되는 함수
4.2 OnTimer() 함수
4.2.1 타이머 사용법
4.2.2 2개 타이머를 사용한 예 (출처; www.winapi.co.kr )
4.2.3 (MFC) UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse, (*handler));
4.3 OnWindowPosChanging() 함수; Dialog를 hidden 상태로 실행하기
4.4 새로운 클래스를 클래스 위져드를 통하여 인식시키기
4.5 UpdataData() 함수
4.6 사용자 메시지를 받아 CMyView클라이언트 영역에서 처리하기
4.7 PreTranslateMessage(MSG *pMsg);
4.7.1 정의
4.7.2 (MFC) 다이얼로그에서 Enter나 ESC 키 막기
5 API 함수
5.1 AllocConsole(); API 에서 Console 창으로 변수 출력 하는 방법
5.2 CreateEvent()
5.3 GetLastError()
5.4 ShellExecuteEx(A)
6 MFC의 구성
{_r} 메모장으로 가기

1 기타 #

#.Control
ComboBox ; 많은 정보를 DrowDownList 형태로 담을 수 있는 컨트롤.


* ( (CDirect9MFCView *)(((CMainFrame *)m_pMainWnd)->GetActiveView()) )->OpenFileMapData();
( (CMainFrame *)(this->GetParentFrame()) )->fun();

* if( GetAsyncKeyState( 'F' )) Isfollow = !Isfollow;
if( GetAsyncKeyState( 'T' )) IsThreat = IsThreat;
if( GetAsyncKeyState( 'H' )) IsHeat = IsHeat;
if( GetAsyncKeyState( 'Z' )) IsZoom = IsZoom;

* MFC에서 Escape 키정의
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
BEGIN
VK_ESCAPE, ID_APP_EXIT, VIRTKEY, NOINVERT
......
END
* MessageBox(NULL, "BuildHeightMap- Error", "Cannot found", 0);
AfxMessageBox( "파일을 읽을 수 없습니다." );

2 (MFC) Class #

2.1 (MFC) CImage 클래스 #

CImage는 MS에서 .net 이후부터 제공되는 그림 관련 클래스로
이전 CBitmap의 bmp만 다루던거에서 좀더 확장하여 Png, Jpg, gif등 다양한 포멧을 지원한다.

CImage 를 이용하면 이미지 로딩은 물론이고
지정한 포맷의 이미지 파일을 만드는것 또한 간단히 할수 있다.

CImage를 사용하기 위해서는 반드시
#include <atlimage.h> 를 해 주어야 한다.

2.1.1 이미지를 출력하는 예 (MFC) #
출처 ; http://mrhook.co.kr/149

// View::OnPaint()
// 이미지가 저장된 위치
CString strImagePath = _T("Image.bmp");

// CImage 클래스 변수를 생성한다.
CImage Image;

// 저장 된 위치에 이미지를 불러온다.
HRESULT hResult = Image.Load( strImagePath );

// 이미지를 불러 오는데 실패 했을 경우.
if( FAILED( hResult ) )
{
CString strtmp = _T("ERROR : Failed to load");
strtmp += strImagePath + _T("\n");
TRACE(strtmp);
return;
}
// 프로그램이 종료 되지 않았다면 이미지가 정상적으로 불러와진 것이다.

// 이미지를 출력해 보자.
Image.TransparentBlt( dc.m_hDC, 0, 0, Image.GetWidth(), Image.GetHeight(), RGB(255, 0, 0) );
Image.BitBlt( dc.m_hDC, 300, 0 );

// 흑백으로 변경해 보자.
COLORREF rgb;

for( int x=0; x<Image.GetWidth(); ++x )
{
for( int y=0; y<Image.GetHeight(); ++y )
{
rgb = Image.GetPixel( x, y );
RGBtoGray( rgb );
Image.SetPixel( x, y, rgb );
}
}

// 흑백의 이미지 출력
Image.BitBlt( dc.m_hDC, 0, 300 );

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// View.cpp ; 흑백으로 변경하는 알고리즘
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
inline void RGBtoGray( COLORREF& rgb )
{
BYTE byGray = (GetRValue(rgb) * 30
+ GetGValue(rgb) * 59
+ GetGValue(rgb) * 11 ) / 100;

rgb = RGB(byGray, byGray, byGray);
}

2.1.2 CImage를 이용한 이미지 파일 포맷 변환 #
출처 ; http://blog.naver.com/PostView.nhn?blogId=kzh8055&logNo=140100162253

1. 이미지 파일를 로딩하고
2. 로딩한 이미지의 특정 영역을 지정한뒤
3. 그것을 다시 또 다른 이미지 파일에 저장하는 것이다.

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 원본이미지의 특정 영역을 하나의 이미지로 만든다
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
BOOL MakeCellFrameImage(
LPCTSTR InSourceImageFileName, LPCTSTR InDestImageFileName,
const CRect& InTagetRect, REFGUID InImageFormatToMake )
{
CImage TempSourceImage;
CImage TempDestImage;

CRect TempTargetRect = InTagetRect;
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 1. 원본 이미지를 TempSourceImage에 로딩
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
TempSourceImage.Load( InSourceImageFileName );
CDC* pSourceDC = CDC::FromHandle( TempSourceImage.GetDC() ); // 원본 이미지의 DC를 얻는다

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 2. 파일로 만들 이미지 객체 TempDestImage 생성
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
int BitPerPixel = pSourceDC->GetDeviceCaps( BITSPIXEL );
TempDestImage.Create( TempTargetRect.Width(), TempTargetRect.Height(), BitPerPixel );
CDC* pDestDC = CDC::FromHandle( TempDestImage.GetDC() );

if( !pSourceDC || !pDestDC )
{ return FALSE; }

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 3. 타겟 DC( 만들 이미지의 DC )에 원본 이미지 DC 의 내용을 를 쓴다
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
pDestDC->BitBlt( 0, 0, TempTargetRect.Width(), TempTargetRect.Height(),
pSourceDC, TempTargetRect.left, TempTargetRect.top, SRCCOPY );

TempDestImage.ReleaseDC();
TempSourceImage.ReleaseDC();

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// 4. 이미지 파일로 저장
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
TempDestImage.Save( InDestImageFileName, InImageFormatToMake );

return TRUE;
}

// CImage::Save( LPCTSTR ImageFileName, REFGUID ImageFormatToMake );
The file type to save the image as. Can be one of the following:
ImageFormatBMP         An uncompressed bitmap image.
ImageFormatPNG         A Portable Network Graphic (PNG) compressed image.
ImageFormatJPEG       A JPEG compressed image.
ImageFormatGIF         A GIF compressed image.

만일 BMP 포맷의 이미지 파일을 만들고 싶다면 다음과 같이 하면된다.
...
Image.Save( _T("Test"), Gdiplus::ImageFormatBMP );
...


CImage capImage;
HDC hProcessDC = ::GetDC(this->m_hWnd);     // <-- 캡쳐 하고자하는 process의 HWND 를 넣어준다.
CRect rect;
::GetClientRect(this->m_hWnd, rect);         // <-- 캡쳐 하고자하는 process의 HWND 를 넣어준다.

if(!capImage.Create(rect.Width(), rect.Height(), 32))
return;

HDC hDC = capImage.GetDC();
BitBlt(hDC, 0, 0, rect.Width(), rect.Height(), hProcessDC, 0, 0, SRCCOPY);

capImage.Save(_T(".\\Image\\test.jpg"), Gdiplus::ImageFormatJPEG);
capImage.ReleaseDC();


3 입력 도구 #

3.1 Button #

* 비슷한 류의 버튼 처리
MFC에서 지원하는 아래와 같은 매크로를 사용하는 방법
ON_COMMAND_RANGE(id1, id2, memberFxn )
1) 여기서 리소스 번호 id1, id2 는 시퀀스한 번호로 부여
예로
IDC_BUTTON1 1000 이였다면
IDC_BUTTON2 1001
IDC_BUTTON3 1002
:
IDB_BUTTON8 1007
id1 = IDC_BUTTON1, id2 = IDC_BUTTON8 로 설정하면
멤버함수 memberFxn 을 해주면 IDC_BUTTON1 ~ IDC_BUTTON8 버튼을
눌러 줄 때마다 멤버함수 memberFxn 함수를 호출한다.
2) 이것은 위자드로 안되고 수작업으로 코딩한다.
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// ...
ON_COMMAND_RANGE(IDC_BUTTON1, IDC_BUTTON8, OnButtonClick)
END_MESSAGE_MAP()

void CMyView::OnButtonClick(UINT nID)
{
switch ( nID )
{
case IDC_BUTTON1:
break;
}
}
헤더파일에는...
// Generated message map functions
//{{AFX_MSG(CSourceEditView)
...
afx_msg void OnButtonClick(UINT nID)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
3) 만약 이벤트가 발생하지 않으면 메인프레임에 IDR_MYMENU 이런식으로 메뉴를 하나 더 등록하고 여기에 각각의 버튼 리소스 아이디랑 같은것으로 메뉴를 추가한다.

3.2 Button/ CBitmapButton #

* CBitmapButton 만드는 법_1
CBitmapButton으로 만든 버튼은 push button 형식이다.
1) 먼저 Form에 버튼을 만들고 Style에서 Owner Draw를 꼭 체크
2) 버튼의 캡션을 간단하게 지정(중요)
예를 들어 캡션이 "ONE"이라면..
3) 이제 리소스에 버튼 그림들을 추가합니다.
이때 추가 하는 리소스 이름은
2번에서 지정한 캡션의 이름뒤에 "D" "F" "U" "X" 를 붙입니다
이를테면 "ONED" "ONEF" "ONEU" "ONEX" 같은 식으로 말입니다.
"U"=UP/ "D"=Down/ "F"=포커스/ "X"=disable 의 뜻
4) CBitmapButton 객체를 멤버변수로 만듭니다.
CBitmapButton m_ButtonOne;
5) OnInitialUpdate() 에 다음과 같이 씁니다
m_ButtonOne.AutoLoad(IDC_BUTTON_ONE,this);

* CBitmapButton 만드는 법_2
방법 1과 크게 다르지 않고, 맵 연결 방법에서 차이가 있다.
1) 버튼의 속성중에 property -> style 에서 Owner draw 꼭 check !!
2) 비트맵으로 씌울 그림들을 프로젝트의 Bit 멥 부분에 Insert 시킴
(정적인 느낌을 피하기 위해 그림은 두개를 준비)
3) 해당 다이알로그 박스의 버튼에 해당하는 변수를 추가시켜 줌(물론 해드에 변수를 정의)
.h 파일 부분에서 public 부분에 만들어 주시고
CBitmapButton m_OK;
CBitmapButton m_Cancel;
.cpp 부분에
void CADD_AND_DEL_FRIEND::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CADD_AND_DEL_FRIEND)
...
DDX_Control(pDX, IDOK, m_OK); // 변수를 해당 아이디와 연결 시켜준다.
DDX_Control(pDX, IDCANCEL, m_Cancel);
//}}AFX_DATA_MAP
}
4) 다이알로그가 생성되는 초기화 부분에서 버튼과 이미지를 연결시켜준다.
BOOL CADD_AND_DEL_FRIEND::OnInitDialog()
{
CDialog::OnInitDialog();
// 버튼이 눌러지기전 이미지와 눌렸을때 이미지를 설정해 준다.
m_OK.LoadBitmaps( IDB_OKU, IDB_OKD );
m_Cancel.LoadBitmaps( IDB_CancelU, IDB_CancelD );

// 버튼을 이미지 크기에 맞춰서 씌워준다.
m_OK.SizeToContent();
m_Cancel.SizeToContent();
return true;
}

3.3 ListCtrl #

* Item을 변경했을때 이벤트 처리
Item을 변경했을때 즉 OnItemChanged메세지가 발생하는데
클래스 위저드에서 NM_CLICK 이벤트 핸들러를 넣고..
아래와 같은 내부 코드를 삽입하여 원하는 작업을 한다
void CxxxDlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pList = (NM_LISTVIEW*)pNMHDR;
ASSERT(pList);

'*' pResult = 0;

if(pList->iItem == -1) return;

리스트컨트롤.GetItemData(pList->iItem);
리스트컨트롤.GetItemText(pList->iItem, 서브아이템 인덱스);
}
* 초기화시 임의의 아이템 선택하기
먼저 OnInitDialog()에서 초기화 한다.
m_ListCtrl.SetExtendedStyle(LVIS_SELECTED| LVS_NOLABELWRAP | LVS_EX_GRIDLINES | LVS_EX_FLATSB | LVS_EX_FULLROWSELECT);
m_ListCtrl.ModifyStyle(0,LVS_SHOWSELALWAYS); <-- 요거 중요함 반드시 할것.

선택하고자 할때 요렇게 하면 됨니다.
m_ListCtrl.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); <-- 선택하고, 회색으로됨
m_ListCtrl.EnsureVisible(i,false); <--스크롤하고
m_ListCtrl.SetFocus();<--포커스주고 파란색으로 됨
선택하면 이전꺼는 자동 해제.
* Full Row Select style로 변환
m_ListCtrl.SetExtendedStyle( LVS_NOLABELWRAP | LVS_EX_GRIDLINES | LVS_EX_FLATSB | LVS_EX_FULLROWSELECT );
* 특정 아이템이 보이도록 스크롤 하기
m_ctrlPacketLog.EnsureVisible( num, TRUE );

* 참고사이트

3.4 Property Sheet #

* 프로퍼티 쉬트에서 첫화면 설정 {_-} mysheet.SetActivePage(npage);

3.5 CFileDialog에서 디렉토리를 설정하기 #

OPENFILENAME CFileDialog::m_ofn ;
typedef struct tagOFN {
...
LPCTSTR lpstrInitialDir; // 여기서 설정 ^^
...} OPENFILENAME;
사용예)
char currentDir[ 256 ]; // 현재의 경로
GetCurrentDirectory( NAME_MAX_SIZE, currentDir );
strcat( currentDir, "\\Data\\" );

CFileDialog dlg(...);
dlg.m_ofn.lpstrInitialDir = currentDir;
dlg.DoModal();

3.6 Check Box 체크하기 #

체크박스.SetCheck(int A); A가 1이면 체크, 0이면 해제
1) CButton* chkbox1 = (CButton*)GetDlgItem(IDC_CHECK1);
int i = chkbox1->GetCheck(); 현재의 상태를 구함
chkbox1->SetCheck(1);
2) GetDlgItem(ID_Model_CHK)->SetCheck(1);
3) Chk :
m_Model_Chk = TRUE;
UpdateData(FALSE);
Chk 해제 :
m_Model_Chk = FALSE;
UpdateData(FALSE);


4 함수 #

4.1 OnInitDialog() 함수; 대화상자가 화면에 처음 나타날 때 호출되는 함수 #

-. DoModal 함수로 호출된 다이알로그 위치 제어
BOOL CDialogMD2::OnInitDialog()
{
CDialog::OnInitDialog();
SetWindowPos(NULL, 100, 150, 0,0, SWP_NOZORDER | SWP_NOSIZE);
return TRUE;
}

4.2 OnTimer() 함수 #

윈도우 체제하에서 어플은 메시지와 프로시져로부터 실행이 시작되고, 실상은 모두 콜백 처리한다.

타이머란 것은 어떻게 생각하면 쓰레드와 비슷한 면이 있는데, 차이점을 알아야 한다.
타이머란 지정한 시간마다 WM_TIMER 메시지를 발생시켜 이것의 핸들러를 통해 주기적으로 어떤 JOB을 처리하는 것을 말한다. 주기적으로 메시지를 발생시켜야 하기 때문에 이 역할은 물론 쓰레드가 담당한다.

여기서 WM_TIMER 메시지를 주기적으로 발생시켜주는 쓰레드는 커널에서 돌아간다. 사용자가 쓰레드를 만들어 타이머의 기능을 100% 구현할 수 있지만 운영체제에서 지원하는 이유는 크게 2가지이다.
    1. 운영체제가 관리하는게 불필요한 리소스 소모를 줄임
    2. 정확도 향상

쓰레드가 일정한 시간 간격으로 메시지큐에 WM_TIMER 를 넣어주는 것과 그냥 쓰레드를 이용하는 것은 차이가 있다.
쓰레드에서 필요한 처리를 했을 경우 실행 흐름은 당연히 쓰레드 자신이 가지게 된다. 하지만 WM_TIMER 를 메시지큐에 넣어줄 경우 운영체제가 이와 연결된 핸들러 함수를 CALL 해준다.
#.
결과적으로 어플은 자기 자신에 할당된 틱타임 일부를 쪼개 처리하는 형식이 된다. 이것은 매우 중요한데,
어떤 메인 쓰레드 A 에서 B 라는 쓰레드를 생성했다고 가정할 때... A 쓰레드가 커널 리소스를 할당받아 사
용하고 있다고 가정했을 때 B 쓰레드에서 이 리소스에 접근하려고 할 경우 거부당할 가능성이 높다. API
를 통해 어떤 어플이든 프로세스 이름만 알면 윈도우 핸들이나, 인스턴스 핸들을 쉽게 가져올 수 있다.

즉, 운영체제에서 막아주지 않으면 악의적인 목적의 어플이 다른 어플을 쉽게 조작할 수 있다는 뜻이다.
쉽게 예를 들어 어떤 프로세스 상에서 윈도우(커널 리소스)를 생성하고, 쓰레드에서 그 프로세스의 윈도우
를 조작할 경우 운영체제에게 블럭당해 오류 발생 확률이 높다.
#.
물론 이럴 때 타이머를 사용하면 자기 자신이 접근하는 것이므로 아무런 제약이 없다. 따라서 주기적으로 그래픽 변화가 필요한 게임 등에서의 처리는 모두 타이머로 처리된다.

타이머는 약간의 제약 사항이 있다. 위에서 말했듯 메시지큐가 필요한데, 메시지큐는 윈도우를 생성 할 경우 만들어진다. 그래서 네트웍 관련 어플 중 타이머 사용을 위해서 빈 윈도우를 하나 생성하는 방식의 설계도 있다. MFC의 경우 CWnd 클래스를 상속받은 클래스를 만들어 Create 해주는 등의 처리가 들어간다.

커널 리소스 접근이 필수인 경우, 타이머를 이용하는게 바람직하다.

4.2.1 타이머 사용법 #
UINT_PTR SetTimer(m_hWnd, nIDEvent, nElapse, lpfnTimer);
1번 인자는 윈도우 핸들. 왜 핸들이 들어가느냐.. 위서 설명했듯 메시지큐가 필요하기 때문
2번 인자는 이벤트 ID. 쉽게 말해 타이머를 여러개 돌릴 수 있으므로 이것을 구분하기 위한 넘버값으로 0 을 제외한 값을 넣어준다.
3번 인자는 ms 단위의 시간 간격. 1초는 1000ms..
4번 인자는 타이머 이벤트 발생 시 콜백 핸들러.

void Func(HWND, UINT, UINT_PTR, DWORD) 형태로 함수 포인터를 지정해줄 수 있는데,
이것은 NULL 로 지정할 경우 따로 프로시져에서 WM_TIMER 발생 시 핸들러 처리를 해도 상관없다.

리턴값은 nIDEvent 에 지정한 값으로 나온다. 이때 이미 지정한 ID로 생성한 타이머가 있을 경우 혹은 기타 다른 이유로 타이머 등록이 실패하면 리턴값은 0 이 된다. 에러 여부는 0 이냐 아니냐로 판단되므로 nIDEvent 에 0 을 지정할 수 없다.

4.2.2 2개 타이머를 사용한 예 (출처; www.winapi.co.kr ) #
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rt;
char Mes[]="타이머 예제입니다.";

switch(iMessage) {
case WM_CREATE:
SetTimer(hWnd,1,500,NULL);
SetTimer(hWnd,2,3000,NULL);
return 0;
case WM_TIMER:
switch(wParam)
{
case 1:
GetWindowRect(hWnd,&rt);
rt.left+=5;
rt.right+=5;
MoveWindow(hWnd,rt.left,rt.top,rt.right-rt.left,rt.bottom-rt.top,TRUE);
break;
case 2:
MessageBeep(0);
break;
}
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
TextOut(hdc,10,10,Mes,lstrlen(Mes));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
KillTimer(hWnd,1);
KillTimer(hWnd,2);
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

4.2.3 (MFC) UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse, (*handler)); #
MFC의 경우 CWnd 의 메써드로 등록된 SetTimer 를 호출한다.

UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse, (*handler)); 형태로,
CWnd 자체가 윈도우를 생성 및 관리하기 위한 래핑 클래스이므로 따로 윈도우 핸들이 필요하지 않다.
또 핸들러를 지정해주지 않을 경우 기본 핸들러는 CWnd::OnTimer 로 지정된다.

타이머를 죽일 때는
BOOL CWnd::KillTimer(UINT_PTR nIDEvent); 를 사용한다.

핸들러는 MFC의 경우 기본으로
void CWnd::OnTimer(UINT nIDEvent); 로 지정되며,
ID 를 통해 타이머 이벤트로로 구분되고 처리된다.

프로세스는 우선 순위에 따라 운영체제로부터 지정된 틱단위로 실행 흐름을 할당받는다.
틱타임은 서버용이냐 개인용이냐에 따라 다른데, 중요한 것은 현재 프로세스에 언제 실행 흐름이 돌아올지, 그리고 몇 틱이 할당될지 모른다는 점이다.

따라서 타이머 생성 시 지정한 시간은 매우 정확도가 떨어진다. 그래서 별도의 보정이 필요한데,
시간 측정 함수류를 사용한다.
-. time 함수의 경우 초단위므로 일단 제외
-. clock 함수의 경우 ms 단위이긴 하지만 단순히 현재 프로세스의 시작 시간과 틱이 돌아온 시간만 계산한 것이므로 제외..
-. QueryPerfomanceCounter 는 정밀도는 가장 높지만 부하가 커서 제외..
-. 결국 가장 많이 사용하는 건 timeGetTime 이다.

4.3 OnWindowPosChanging() 함수; Dialog를 hidden 상태로 실행하기 #

-. 윈도우즈 프로그램에서 윈도우를 감출 때는 ShowWindow(SW_HIDE); 를 호출하면 되지만, 문제는 다이얼로그를 hidden상태로 실행 하려고, ShowWindow(SW_HIDE)를 OnInitDialog()에서 호출하면 처리가 안된다.
OnInitDialog()의 처리가 끝나는 시점에서 ShowWindow(SW_SHOW)를 호출하기 때문이다.

-. hidden으로 실행하기 위해서는 생성되는 윈도우의 스타일에서 속성을 빼주어야 한다.
속성 처리를 위해서 "WM_WINDOWPOSCHANGING" 윈도우 메시지 처리 함수를 생성하고 다음과 같이 설정한다.

void CYourDialog::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
lpwndpos->flags &= ~SWP_SHOWWINDOW; // 속성에서 SWP_SHOWWINDOW 제거
CDialog::OnWindowPosChanging(lpwndpos);
}

4.4 새로운 클래스를 클래스 위져드를 통하여 인식시키기 #

-. 먼저 프로젝트 이름으로 된 XXXX.clw 파일을 삭제한다. 만일을 위해 이름을 바꿔두는 것이 좋다.
-. 그 다음 클래스 위져드를 실행시키면 클래스에 대한 정보가 없기 때문에 새롭게 설정하는 다이얼로그가 나타난다. 여기서 Project List에 모두 등록시키면 된다. 그런 후 다시 클래스 위져드를 실행시키면 기존의 클래스 뿐만 아니라 새롭게 추가한 클래스도 위져드가 인식하는 것을 알 수 있다.

4.5 UpdataData() 함수 #

-. 화면에 데이터를 나타내기 위해서는 변수에 등록되어 있는 데이터를 대화 상자로 전송하는 함수
(변수에 데이터를 기록하였다 하여도 화면에는 입력된 모습이 나타나지 않는다는 의미)
-. UpdateData(FALSE); 변수를 대화 상자로 전송
(현재 변수에 데이터를 등록하고 이것을 대화 상자로 전송할 때)
UpdateData(TRUE); 대화 상자에서 변수로 데이터를 전송
(대화 상자에서 에디터 상자 같은 자원에 정보를 변형하고, 이 변형된 데이터를 다시 자원으로 받아야 할 경우)

4.6 사용자 메시지를 받아 CMyView클라이언트 영역에서 처리하기 #

-. 뷰클래스와 다이얼로그클래스의 역할은 다음과 같고, 다이얼로그클래스는 뷰클래스를 알고 있어야한다.
뷰클래스 ==> 감시자 (스타크래프트의 옵저버와 동일한 역할)
다이얼로그클래스 ==> 정보의변화
-. 물론 SetParent() 후에 GetParent()로 해도 되지만
위와 같은 디자인패턴을 고려하면 다음과 같다.(옵저버패턴)
1) 다이얼로그클래스에서 뷰클래스의 포인터를 저장할수 있는 멤버변수를만들고
2) 뷰클래스 초기화함수부분에서 다이얼로그클래스에 포인터를 넘겨주면 됩니다.
3) 그리고 메시지를 정의합니다.

WM_USER +1 의 메시지를 만든다구 할때..
  • 뷰클래스의 헤더부분..
    afx_msg long OnMyMsg(WPARAM) wparam,LPARAM lparam);
  • 뷰클래스의 메시지맵 부분..
    ON_MESSAGE ( WM_USER+1,OnMyMsg)
  • 다이얼로그클래스 내부에서 메시지호출시..
    pViewClass -> SendMessage(WM_USER+1 ,정보1, 정보2);


  • 4.7 PreTranslateMessage(MSG *pMsg); #

    @ 2010-03-17 @
    4.7.1 정의 #
    virtual BOOL PreTranslateMessage(MSG *pMsg);
    pMsg : 처리하는 메세지를 포함한 MSG 구조체의 포인터
    이 포인터에는 메세지를 보내쪽의 윈도우 핸들과 보내온 메세지등의 정보가 저장된다.

    MSG구조체
    typedef struct tagMSG {
    HWND hwnd; // 메세지를 건내받는 윈도우의 핸들
    UINT message; // 메세지 번호(WM_RBUTTONDOWN등)
    WPARAM wParam; // 메세지의 부가 정보
    LPARAM lParam; // 메세지의 부가 정보
    DWORD time; // 메세지가 포스트된 시간
    POINT pt; // 메세지가 포스트되었을 때의 커서위치(스크린 좌표)
    } MSG;

    -. PreTranslateMessage는 윈도우 메세지가 TranslateMessage나 DispatchMessage에 전해지기 전에 처리할 필요가 있을때 사용되며, 윈도우 메세지가 발생되면 TranslateMessage나 DispatchMessage에 가기전에 PreTranslateMessage를 거치게 된다.

    -. PreTranslateMessage는 메세지 맵을 사용할 수 있고, 윈도우 메세지가 각 컨트롤에 전해지기 전에 그 메세지를 얻을 수 있다.
    • MFC에서 키입력을 처리할 때, OnKeyDown message를 주로 사용하지만
      OnKeyDown message의 경우 View 클래스에 우선권이 있기 때문에 MainFrame 클래스 등에서 사용하려할 때 원하는대로 사용되지 않는 경우에 PreTranslateMessage를 사용한다.
    • 사용형식
      BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
      {
      // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
      if (pMsg->message == WM_KEYDOWN)
      {
      if (pMsg->wParam == VK_SPACE)
      ...
      }
      return CFrameWnd::PreTranslateMessage(pMsg);
    • }

    4.7.2 (MFC) 다이얼로그에서 Enter나 ESC 키 막기 #
    -. MFC에서 대화 상자 기반으로 프로젝트를 만들어서 실행시키면, 그 다이얼로그에서 Enter나 ESC 키를 누르면 다이얼로그가 종료하는데, 이것을 막기 위해 PreTranslateMessage를 활용한다.
    ▷ ***Dlg.h에서
    class CTestDlg : public CDialog
    {
    // 생성
    public:
    CTestDlg (CWnd* pParent = NULL); // 표준 생성자
    virtual BOOL PreTranslateMessage (MSG* pMsg);
    // 대화 상자 데이터
    enum {IDD = IDD_TEST_DIALOG };
    Protected:
    virtuala void DoDataExchange(CDataExchange* pDX); // DDX/ DDV 지원

    // 구현
    protected:
    HICON m_hIcon;

    // 생성된 메시지 맵 함수
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP();

    public:
    afx_msg void OnBnClickedOk();
    };

    ▷ ***Dlg.cpp파일에서
    BOOL CTestDlg ::PreTranslateMessage(MSG* pMsg)
    {
    switch(pMsg->message)
    {
    // 키가 눌렸을때
    case WM_KEYDOWN:
    switch(pMsg->wParam)
    {
    // 리턴키\tab
    case VK_RETURN:
    return TRUE;
    // ESC키
    case VK_ESCAPE:
    return TRUE;
    }
    }
    return CDialog::PreTranslateMessage(pMsg);
    }


    5 API 함수 #

    5.1 AllocConsole(); API 에서 Console 창으로 변수 출력 하는 방법 #

    @ 2010-12-27 @
    Visual Studio Tool을 사용해서 Windows 프로그램을 하다보면 디버깅 할 때 어려움을 느끼곤 한다. Message처리로 동작 하기 때문에 함수의 특정 부분의 변수의 값등을 읽기가 여간 불편한 일이 아니다.
    이럴때 다음과 같은 방법을 사용하면 콘솔창과 윈도우즈 창이 함께 뜨기 때문에 여러 변수 명을 바로바로 직접 읽어 볼수가 있다.

    헤더에 다음을 추가 하고
    #include <stdio.h>

    WinMain 첫째 줄에 아래와 같이 추가
    AllocConsole();
    freopen("CONOUT$", "wt", stdout);
    그러면 printf() 함수를 코드 내에서 사용 가능하다.

    5.2 CreateEvent() #

    @ 2010-04-12 @ 이름이 정해진 또는 이름이 없는 이벤트 객체를 열거나 생성하는 함수 {_-} {_o} API_CreateEvent()

    5.3 GetLastError() #

    @ 2010-04-09 @     출처 ; http://infoki.net/586
    GetLastError
    호출측 스렛드가 가지는 최신의 에러 코드를 취득합니다.에러 코드는 스렛드 마다 보관 유지되기 위해, 복수의 스렛드가 서로의 에러 코드를 덧쓰기할 것은 없습니다.
    DWORD GetLastError(void);
    KERNEL32.DLL

    인수 ; 인수는 없습니다.

    반환값 ; 호출측 스렛드가 가지는 최신의 에러 코드를 돌려줍니다.

    해설 ; FormatMessage함수로FORMAT_MESSAGE_FROM_SYSTEM플래그를 지정하는 것으로, 에러 코드에 대응하는 에러 문자열을 취득할 수 있습니다.

    에러 코드는32비트치입니다(비트31하지만 최상위비트).비트29(은)는 어플리케이션 정의의 에러 코드용으로 예약되고 있습니다.어플리케이션 독자적인 에러 코드를 정의하는 경우는, 이 비트를 세트 하고, 시스템 정의의 에러 코드와 충돌하지 않게 해 주세요.

    대응 정보 ; Windows 95 이후 / Windows NT 3.1 이후

    5.4 ShellExecuteEx(A) #

    @ 2010-04-09 @     출처 ; http://infoki.net/586
    ShellExecuteEx(A)
    새로운 프로세스를 기동합니다.폴더를 익스플로러로 열리거나 문서 파일을 관련지을 수 있었던 어플리케이션으로 열리거나 할 수도 있습니다.
    BOOL ShellExecuteExA(
    LPSHELLEXECUTEINFO pExecInfo // SHELLEXECUTEINFO구조체
    );
    SHELL32.DLL

    인수 ; pExecInfo
    실행되는 어플리케이션에 대한 정보를 격납했다 SHELLEXECUTEINFO 구조체의 주소를 지정합니다.

    반환값 ; 성공하면 0 이외의 값이 돌아갑니다.
    실패하면 0 하지만 돌아갑니다.확장 에러 정보를 취득하려면 , GetLastError 함수를 사용합니다.

    해설 ; 함수의 호출이 성공하면, SHELLEXECUTEINFO 구조체의 hInstApp 멤버는 실행된 어플리케이션의 인스턴스 핸들이 됩니다.함수가 실패했을 경우는 32 이하의 SE_ERR_ 에러치가 됩니다.이 에러치는 ShellExecute 함수와 호환의 것입니다. ShellExecuteEx 함수로 에러치를 얻기 위해서는 GetLastError 함수를 사용하도록 해 주세요.

    GetLastError 함수는 이하의 몇개의 값을 돌려줍니다.이러한 값은 SE_ERR_ 값에 대응하고 있습니다.
    의미
    2 (ERROR_FILE_NOT_FOUND) 지정된 파일이 발견되지 않습니다.
    3 (ERROR_PATH_NOT_FOUND) 지정된 패스가 발견되지 않습니다.
    5 (ERROR_ACCESS_DENIED) 지정된 파일에의 액세스는 거부되었습니다.
    8 (ERROR_NOT_ENOUGH_MEMORY) 메모리가 부족하기 때문에 실행할 수 없습니다.
    32 (ERROR_SHARING_VIOLATION) 공유 위반이 발생했습니다.
    1155 (ERROR_NO_ASSOCIATION) 주어진 파일명의 확장자(extension)와 관련지을 수 있었던 어플리케이션이 없습니다.
    1156 (ERROR_DDE_FAIL) DDE 트랜잭션(transaction)가 실패했습니다.
    1157 (ERROR_DLL_NOT_FOUND) 어플리케이션의 실행에 필요한 DLL 의1개가 발견되지 않습니다.
    1223 (ERROR_CANCELLED) 함수는 유저에게 추가 정보를 재촉했습니다만, 유저가 요구를 캔슬했습니다.

    대응 정보 ; Shell32.dll Version 4.00 이후
    Windows 95 이후 / Windows NT 4.0 이후



    6 MFC의 구성 #

    * Application Frameworks(AFS)
    http://gingaminga.com/wiki/imgs/picture/MFC_01.gif

    * AFX 클래스의 상속 관계
    http://gingaminga.com/wiki/imgs/picture/MFC_02.gif

    * AppWizard
    프로그램을 새로 만들 때는 AppWizard를 사용하는데, AppWizard의 역할은 MFC의 AFX클래스에서 상속을 받아, 아래 그림에서와 같은 네 개의 클래스를 생성시켜 주는 것이다.
    http://gingaminga.com/wiki/imgs/picture/MFC_03.gif
    http://gingaminga.com/wiki/imgs/picture/MFC_04.gif

    * AFX 클래스들 간의 상호참조 관계
    http://gingaminga.com/wiki/imgs/picture/MFC_05.gif
    - 도큐먼트 클래스에서 뷰/ 프레임 윈도우 참조
    CDocument::GetFirstViewPosition, GetNextView - 하나의 도큐먼트는 여러 개의 뷰와 결합될 수 있기 때문에 도큐먼트에서 그와 연결된 뷰를 얻을 때는 몇 번째로 연결된 뷰를 얻을 것인지도 생각해야 한다.
    CWnd * AfxGetMainWnd();


    {_r} 메모장으로 가기

    July, 2019
     123456
    78910111213
    14151617181920
    21222324252627
    28293031 

    지혜는 갖고 다니기엔 쉽지만 얻기엔 힘든 것이다.

    책갈피_Be_Lonely
    MoniWikiIdeas
    RandomQuoteMacro
    책갈피_ExtremeProgrammingExplained
    last modified 2014-05-28 14:36:41
    고치기|찾기|쪽 지우기|비슷한 쪽 Valid XHTML 1.0! Valid CSS! powerd by MoniWiki
    0.6812 sec