'Windows > MFC' 카테고리의 다른 글

드래그 앤 드롭 ( Drag-And-Drop)  (0) 2011.11.18
트리 컨트롤 ( CTreeCtrl )  (0) 2011.11.18
RGB 와 CMYK, HSV, HLS  (0) 2011.11.15
ATL, STL, COM 은 무엇인가?  (0) 2011.11.15
GDI 고급  (0) 2011.11.15

'Windows > MFC' 카테고리의 다른 글

트리 컨트롤 ( CTreeCtrl )  (0) 2011.11.18
이미지 프로세싱  (0) 2011.11.15
ATL, STL, COM 은 무엇인가?  (0) 2011.11.15
GDI 고급  (0) 2011.11.15
윈도우 화면 캡쳐하기  (0) 2011.11.14
ATL 이란?
Active Template Library 의 약자로 C++ 또는 비주얼 C++ 를 이용하여 ASP 코드나 다른 ActiveX 프로그램 컨포넌트를 만들 때 사용하는 프로그램 라이브러리이다.

STL 이란?
The Standard Template Library (STL) is a C++ software library which later evolved into the C++ Standard Library. It provides four components called algorithms, containers, functors, and iterators. The STL provides a ready-made set of common classes for C++, such as containers and associative arrays, that can be used with any built-in type and with any user-defined type that supports some elementary operations (such as copying and assignment). STL algorithms are independent of containers, which significantly reduces the complexity of the library.


COM 이란?
Component Object Model 의 약자로 소프트웨어 구성 요소를 개발하기 위한 마이크로소프트의 플랫폼이다. 마이크로소프트가 1993년에 소개하였다. COM은 이 기술을 지원하는 모든 언어에서 프로세스간 통신과 동적 오브젝트 생성을 가능하게 하기 위해 사용된다. 소프트웨어 개발 세계에서는 COM이라는 용어를 종종 OLE, OLE 자동화, ActiveX, COM+, DCOM 기술을 포함하는 포괄적 용어로 사용한다. 다양한 플랫폼에서 COM이 구현되었지만, 주로 마이크로소프트 윈도에서 사용된다. COM 은 닷넷 프레임워크와 같은 다른 기술로 대체되리라 전망된다.

'Windows > MFC' 카테고리의 다른 글

이미지 프로세싱  (0) 2011.11.15
RGB 와 CMYK, HSV, HLS  (0) 2011.11.15
GDI 고급  (0) 2011.11.15
윈도우 화면 캡쳐하기  (0) 2011.11.14
CImage 클래스  (0) 2011.11.14

CRgn 클래스는 윈도우 영역 자체를 객체화한 클래스이다.
다음은 CRgn 클래스를 이용한 코드이다.


void CTextRgnView::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 CRect RectLeft = CRect(50,50,250,150);
CRect RectRight  = CRect(250,50,450,150);
dc.FillSolidRect(&RectLeft, RGB(192,0,0));
dc.FillSolidRect(&RectRight, RGB(192,192,192));

// 앞서 만든 두 사격형을 별도의 영역으로 만든다.
CRgn rgnLeft, rgnRight;
rgnLeft.CreateRectRgnIndirect(RectLeft);
rgnRight.CreateRectRgnIndirect(RectRight);

// DC 의 폰트 설정을 바꾼다.
LOGFONT lf;
::ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = 72;
wsprintf(lf.lfFaceName, _T("%s"), _T("Arial"));

CFont Font;
Font.CreateFontIndirect(&lf);
CFont *pOldFont = dc.SelectObject(&Font);
dc.SetBkMode(TRANSPARENT);
//dc.TextOut(60,65,_T("TEST STRING");

// 왼쪽 영역을 선택하여 문자열을 출력한다.
dc.SetTextColor(RGB(192,192,192));
dc.SelectClipRgn(&rgnLeft); //rgnLeft 를 벗어난 문자열은 출력되지 않는다.
dc.TextOut(60,65,_T("TEST STRING"));

// 오른쪽 영역을 선택하여 문자열을 출력한다.
dc.SetTextColor(RGB(192,0,0));
dc.SelectClipRgn(&rgnRight); //rgnRight 를 벗어난 문자열은 출력되지 않는다.
dc.TextOut(60,65,_T("TEST STRING"));

dc.SelectObject(pOldFont);


}



다음은 이미지를 투명하게 해놓고 마우스 왼쪽 버튼을 누루면서 움직이면 200범위로 불투명하게 보이는 코드다.

void CImageRgnView::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 m_ImgSample.AlphaBlend(dc.m_hDC, 0,0,50);

 CRgn Rgn;
 Rgn.CreateEllipticRgn(m_rectVisible.left, m_rectVisible.top, m_rectVisible.right, m_rectVisible.bottom);
 dc.SelectObject(&Rgn);

 m_ImgSample.BitBlt(dc.m_hDC, 0,0);

}


void CImageRgnView::OnMouseMove(UINT nFlags, CPoint point)
{
 
 if(nFlags & MK_LBUTTON) //왼쪽 버튼을 누루면서 움직였는지 체크, 비트연산한다.
 {
  m_rectVisible = CRect(point.x - 100, point.y - 100, point.x + 100, point.y + 100);
  //앞뒤, 옆옆으로 100씩 2번이니까 200의 차이가 난다.
  RedrawWindow();
 }

 CView::OnMouseMove(nFlags, point);
}


void CImageRgnView::OnLButtonUp(UINT nFlags, CPoint point)
{

 m_rectVisible = CRect(0,0,0,0);
 RedrawWindow();

 CView::OnLButtonUp(nFlags, point);
}

곰플레이어 스킨같은거 만드는 방법

CRgn 클래스를 이용해서 만들면 된다. 다음은 기초 코드이다.
CMainFrame 클래스에 OnCreate() 에서 작성하면 된다.


m_RgnRect1.CreateRectRgn(0,0,100,100);
m_RgnRect2.CreateRectRgn(70,70,170,170);
m_RgnRect3.CreateRectRgn(0,0,0,0);

m_RgnRect3.CombineRgn(&m_RgnRect1, &m_RgnRect2, RGN_XOR);

SetWindowRgn( (HRGN)m_RgnRect3, TRUE);

'Windows > MFC' 카테고리의 다른 글

RGB 와 CMYK, HSV, HLS  (0) 2011.11.15
ATL, STL, COM 은 무엇인가?  (0) 2011.11.15
윈도우 화면 캡쳐하기  (0) 2011.11.14
CImage 클래스  (0) 2011.11.14
고급 이미지 출력 함수 ( TransparentBlt() , AlphaBlend() )  (0) 2011.11.14


바탕화면 윈도우 왼쪽 위 모서리(300 * 300) 을 캡쳐하여 뷰 윈도우 화면에 출력한다.  이 때 200 * 200 사이즈는 흑백으로 변환해서 출력한다. 여기에서 중요한 것은 RGB 컬러를 흑백으로 변환하는 방법이다.
GetRValue() , GetGValue(), GetBValue() 함수는 COLORREF 값에서 RGB 값을 각각 추출하는 매크로이다. 24비트 컬러에서 각각은 8비트이며, RGB 각각에 대해 30, 59,11을 곱한 값을 모두 더해 다시 100으로 나누면 흑백에 해당하는 값을 구할 수 있다. 이 값 하나로 RGB 값을 모두 채우면 RGB 흑백 이미지가 된다. 이 이상의 자료는 이미지 프로세싱 관련 자료를 참고하자.

CWindowDC 는 비클라이언트 영역을 포함한 윈도우 전체에 대한 DC.
따라서 밑의 ScrDC 는 바탕화면 윈도우 전체에 대한 DC를 가진다.



void CSaveGrayDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
 
 CWnd* pWndDesktop = GetDesktopWindow();
 CWindowDC SrcDC(pWndDesktop); //바탕화면 윈도우 DC
 CClientDC dc(this);    //뷰 윈도우 DC

 //바탕 화면 크기 및 색상수와 동일한 비트맵 이미지를 만든다.
 CImage Image;
 Image.Create(300,300,SrcDC.GetDeviceCaps(BITSPIXEL)); //DC의 각종 정보를 추출한다.
// 인자로 전달받은 인덱스에 따라 각각 다른 값을 int형으로 반환하는데 여기에서는 한 픽셀을 표시하는데
// 필요한 비트 수인 32비트(트루를 반환할 것임. 
// 4번째 인자(dwFlags)은 0을 기본값으로 갖고, 32비트 비트맵일 경우 알파 채널을 명시한다.
// 세 번째 인자(nBPP) 가 반드시 32가 되어야 알파 채널을 명시할 수 있다.
// GetDeviceCaps 의 인자인 인덱스에는 BITPIXEL 말고도 HORZSIZE, VERTSIZE 등 (모니터의 물리적 크기 mm)
// 대략 40가지 정도가 더 있는데, 경우에 따라 반환 값의 종류도 다시 검사해서 정보를 판단해야한다.
// 상세한 내용은 MSDN을 참고하자.
 
 // 이미지 DC 와 화면 DC 에 바탕 화면 윈도우 DC 를 출력한다.
 CDC* pDC = CDC::FromHandle(Image.GetDC()); //바탕 화면 윈도우 DC를 가져온다.
 pDC->BitBlt(0,0,300,300, &SrcDC, 0,0, SRCCOPY); // 이미지 DC 에 화면 DC(바탕화면) 내용을 뿌린다.
 Image.ReleaseDC(); //DC를 풀어주고

 // 200,200 만큼 흑백 이미지로 변환
 COLORREF rgb;
 for(int x=0; x<200; x++)
 {
  for(int y=0; y<200; ++y)
  {
   // 바탕화면 각 픽셀을 하나씩 가져와서 rgb에 대입, 이 때 비트맵 이미지를 2차원 배열로 가정하였다.
   rgb = Image.GetPixel(x,y); 
   // GRAY RGB 값으로 변환한다.
   RGBtoGray(rgb);

   Image.SetPixel(x,y,rgb);
  }
 }
 Image.BitBlt(dc.m_hDC, 0,0); //바탕화면 이미지(Image)를 화면 DC(dc.m_hDC)에 0,0에 뿌린다.

 CView::OnLButtonDown(nFlags, point);
}



다음 코드는 마우스 오른쪽 버튼을 눌렀을 때 바탕 화면 모든 영역을 캡쳐해서 저장한 후, 이미지 뷰어로 실행하여 보여준다.

void CSaveGrayDemoView::OnRButtonDown(UINT nFlags, CPoint point)
{
 // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

 CWnd* pWndDesktop = GetDesktopWindow();
 CWindowDC SrcDC(pWndDesktop); //바탕화면 윈도우
 CClientDC dc(this);    //뷰 윈도우

 // 바탕 화면 크기를 알아낸다.
 CRect Rect;
 pWndDesktop->GetWindowRect(&Rect);

 // 바탕 화면 크기 및 색상 수와 동일한 비트맵 이미지를 만든다.
 CImage Image;
 Image.Create(Rect.Width(), Rect.Height(), SrcDC.GetDeviceCaps(BITSPIXEL) );
 
 // 이미지 DC 와 화면 DC에 바탕화면 윈도우 화면을 출력한다.
 CDC* pDC = CDC::FromHandle(Image.GetDC()); //바탕화면 이미지 DC
 pDC->BitBlt(0,0,Rect.Width(), Rect.Height(), &SrcDC, 0, 0,SRCCOPY);
 Image.ReleaseDC();

 //JPEG 형식으로 바탕 화면 이미지를 저장한다.
 Image.Save(_T("DeskTop.jpg"), Gdiplus::ImageFormatJPEG);
 // 저장된 이미지를 뷰어로 실행하여 보여준다.
 ::ShellExecute(NULL, _T("OPEN"), _T("DeskTop.jpg"), NULL, NULL, SW_SHOW);
// 윈도우 탐색기에서 해당 파일을 더블 클릭한 것과 완벽하게 일치하는 동작.
// hwnd 인자는 부모 윈도우의 핸들이며, 이 값이 NULL 일 경우 부모 윈도우는 바탕화면이 된다.
// lpOperation 인자에는 함수가 어떤 동작을 할지 명시한다. open 일 경우 lpFile 인자와 연결된 프로세스를 실행하라는 의미
// 여기서 중요한 점은 lpFile 인자로 전달받는 것이 실행 파일이 아닌 경우 레지의 내용을 참조하여 연결 프로그램을 실행
// 만일 lpFile 에 URL을 준다면 웹 브라우저를 실행하여 해당 사이트로 접속을 시도한다. 이외에 explore,edit,find,print ~~
// lpParameters 인자는 lpFile이 실행 파일인 경우 실행 인자를 명시한다.
// lpDirectory 인자는 실행할 프로그램의 현재 폴더 경로를 명시하며 , NULL 일 경우 윈도우 기본 설정을 적용한다.

 

 CView::OnRButtonDown(nFlags, point);
}




'Windows > MFC' 카테고리의 다른 글

ATL, STL, COM 은 무엇인가?  (0) 2011.11.15
GDI 고급  (0) 2011.11.15
CImage 클래스  (0) 2011.11.14
고급 이미지 출력 함수 ( TransparentBlt() , AlphaBlend() )  (0) 2011.11.14
비트맵과 이미지 처리  (0) 2011.11.14


다양한 형식의 파일을 직접 로드하는 것을 지원하며, 로드하거나 생성한 이미지에 대한 DC 를 구해 직접 그리기가 쉬우며, DC에 출력할 내용을 외부 파일(BMP, JPEG, ..) 로 저장하는 메서드도 제공하므로, GDI 를 이용하여 그린 결과를 다양한 형식의 파일로 저장할 수 있다. 또한 CImage 클래스는 MFC 가 제공하는 ATL 클래스 중 하나로서, 내부적으로  COM 객체로 구현되어 있다. C++ 의 클래스가 논리적인 코드를 객체화한 개념이라면, COM 은 실행 바이너리 파일 단위(모듈단위) 로 객체화한 것이라고 할 수 있으며, 메서드가 반환하는 값은 HRESULT 형이다. FAILED() 매크로나 SUCCEEDED() 매크로를 이용해 오류를 확인할 수 있다. CImage::Load() 메서드는 이미지 파일을 로드하여 DIB 이미지를 생성한다. 유사한 함수로 LoadFromResource() 메서드가 있으며, 이 함수는 첫번째 인자로 응용 프로그램의 인스턴스 핸들을 주는 게 다르다.


Load() 사용법

void CImgOutDemoView::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 // TODO: 여기에 메시지 처리기 코드를 추가합니다.
 // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.
CImage Image;
HRESULT hResult = Image.Load(_T("image1.jpg"));
Image.BitBlt(dc.m_hDC, 0,0);

}

LoadFromResouce() 사용법
/*
 CImage Image;
 Image.LoadFromResource(AfxGetInstanceHandle(), IDB_image1);
 Image.BitBlt(dc.m_hDC, 0,0);
*/

void CImgOutDemoView::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 CImage Image;
 Image.LoadFromResource(AfxGetInstanceHandle(), IDB_image1);
 //비트맵인것을 유념할것

 //비트맵 이미지에 대한 핸들을 가져와서 DC를 생성한다.
 CDC* pDC = CDC::FromHandle(Image.GetDC());
 // 이 이미지 DC에 문자열을 출력한다.
 pDC->SetBkMode(TRANSPARENT); //배경을 투명하게 한다.
 pDC->TextOut(200,30,_T("CImage Sample!"));
 Image.ReleaseDC(); //GetDC 와 쌍으로 붙어다님.

 Image.BitBlt(dc.m_hDC, 0,0);

}



'Windows > MFC' 카테고리의 다른 글

GDI 고급  (0) 2011.11.15
윈도우 화면 캡쳐하기  (0) 2011.11.14
고급 이미지 출력 함수 ( TransparentBlt() , AlphaBlend() )  (0) 2011.11.14
비트맵과 이미지 처리  (0) 2011.11.14
DrawText() 함수 예제 및 연습  (0) 2011.11.13
다음은 매우 중요한 TransparentBlt() , AlphaBlend()  함수들의 사용법이다.
투명처리를 해준다. 인터페이스 개발시에 자주 쓰인다.

TransparentBlt()  사용법

dc.TransparentBlt(20,100, bmpInfo.bmWidth * 2 , bmpInfo.bmHeight * 2, &MemDC, 0,0, bmpInfo.bmWidth, bmpInfo.bmHeight, RGB(0,0,0)); //마지막 인자는 투명 처리될 색상의 RGB 값이다.


AlphaBlend() 사용법

BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER; //원본이미지와 대상 이미지를 서로 섞는다는 의미.
bf.BlendFlags = 0; //항상 반드시 0 이어야 한다.
bf.SourceConstantAlpha = 50; //0은 투명 255는 불투명

bf.AlphaFormat = 0; //이 값은 항상 0이거나 AC_SRC_ALPHA 가 되어야 한다.

//AC_SRC_ALPHA 가 되는 경우는 원본 비트맵이 24비트 이하의 비트맵이 아닌 각 픽셀에 대한 알파 채널을 갖는 진정한 32비트 비트맵인 경우만 해당한다. 이런 경우 복잡한 연산 대신 알파 채널을 이용한 별도의 연산으로 이미지를 혼합하게 되며, 이미지 전체의 투명도를 적용하여 출력하지 않고 일부는 불투명하게,일부는 투명하게 출력할 수 있다.
보통 바탕화면에 아이콘이 이렇게 적용되어 있다. 배경화면과 아이콘이 섞여서 출력되어 있는..

//반투명 이미지를 출력한다.
dc.AlphaBlend(20,100, bmpInfo.bmWidth*2, bmpInfo.bmHeight*2, &MemDC, 0,0, bmpInfo.bmWidth, bmpInfo.bmHeight, bf);


블랜드연산에 대해서는 나중에 알아보자. 중요한 것은 AlphaBlend() 함수와 BitBlt 함수의 내부 연산 속도는 비교할 수 없을 만큼 차이가 난다는 것이며, AlphaBlend() 함수는 내부적으로 연산 공식에 의해 느려질 수 밖에 없다. 그래서 윈도우 비스타가 XP보다 상대적으로 느려질 수 밖에 없는 이유이다.

'Windows > MFC' 카테고리의 다른 글

윈도우 화면 캡쳐하기  (0) 2011.11.14
CImage 클래스  (0) 2011.11.14
비트맵과 이미지 처리  (0) 2011.11.14
DrawText() 함수 예제 및 연습  (0) 2011.11.13
직접 버튼 그리기  (0) 2011.11.13

다음은 비트맵 파일을 불러와서 비트맵을 확대해서 보여주는 예제다.


void CBmpDisplayView::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 // TODO: 여기에 메시지 처리기 코드를 추가합니다.
 // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.


 CDC MemDC;
 BITMAP bmpInfo;
 MemDC.CreateCompatibleDC(&dc); //화면 DC 와 호환되는 MemDC 를 생성한다.
 
 CBitmap bmp; //비트맵 이미지를 가져오기 위해 객체를 생성하고
 bmp.LoadBitmap(IDB_Test_Image); //IDB_Test_Image에 해당하는 비트맵을 가져온다.
 bmp.GetBitmap(&bmpInfo); //IDB_Test_Image 에 해당하는 비트맵에 대한 정보를 bmpInfo에다 넣는다. 
 
 CBitmap *pOldBmp = MemDC.SelectObject(&bmp); //메모리 DC 에다가 IDB_Test_Image에 해당하는 비트맵을 넣는다
 dc.BitBlt(100,100, 500, 500, &MemDC,300,300, SRCCOPY);//MemDC 에 있는 비트맵을 화면 DC로 전송한다.
 //메모리DC에 있는 비트맵의 300,300 위치에서 가져와서 화면 100,100 좌표에 500,500만큼 보여라.
 //레스터 연산에 자주 쓰이는 것에는 NOTSCRCOPY(반전), DSTINVERT(반전), SRCAND(원본과 대상을 AND연산)
 //원본 : dc.BitBlt(0,0,bmpInfo.bmWidth, bmpInfo.bmHeight, ~~); 나머지는 같다.


 dc.StretchBlt(100,100,250*4,300*4, &MemDC, 300,300, 300,300, SRCCOPY);
 //3,4번째가 확대를 지정하고 나머지는 위와 같다.
 MemDC.SelectObject(pOldBmp);

 //이것들은 비트맵 이미지를 다룬 것이다. 다른 이미지 포멧들은 CImage 클래스를 이용해야한다.
}

'Windows > MFC' 카테고리의 다른 글

CImage 클래스  (0) 2011.11.14
고급 이미지 출력 함수 ( TransparentBlt() , AlphaBlend() )  (0) 2011.11.14
DrawText() 함수 예제 및 연습  (0) 2011.11.13
직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13


void CDrawTextDemoView::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 CRect Rect(10,10,200,50);
 dc.FillSolidRect(&Rect, RGB(192,192,192));
//테투리없는 사각형을 그리고 RGB값으로 내부를 칠함.

 CFont Font;
 LOGFONT lf;
 ::ZeroMemory(&lf, sizeof(lf));

 lf.lfHeight = 20;
 wsprintf(lf.lfFaceName , _T("%s"), _T("Arial"));
 lf.lfItalic = true;
 lf.lfUnderline = true;
 Font.CreateFontIndirect(&lf);
 CFont* pOldFont = dc.SelectObject(&Font);
 
 //폰트 색깔 바꾸고 실제출력
 dc.SetTextColor(RGB(255,0,0));

 dc.DrawText(_T("Text Button"), &Rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER );

 dc.TextOut(50,100,_T("\t이것은 실습을 위한 \n문자열입니다."));

 int nTabStop = 100;

 dc.TabbedTextOut(50,150, _T("\t이것은 실습을 위한 \n문자열입니다."),1, &nTabStop,20);

 dc.DrawText( _T("\t이것은 실습을 위한 \n문자열입니다."), CRect(50,220,300,200), DT_SINGLELINE | DT_CENTER | DT_VCENTER  );
 
 dc.SelectObject(pOldFont);
}

'Windows > MFC' 카테고리의 다른 글

고급 이미지 출력 함수 ( TransparentBlt() , AlphaBlend() )  (0) 2011.11.14
비트맵과 이미지 처리  (0) 2011.11.14
직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12


void CVirtualButtonView::OnLButtonDown(UINT nFlags, CPoint point)
{
 // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
 if(m_BtnRect.PtInRect(point)) //버튼이 위치한 영역을 클릭했는가?
 {
  m_bClicked = !m_bClicked; //flag 토글
  RedrawWindow(&m_BtnRect); //윈도우 지정된 범위만 다시그림, 즉 WM_PAINT 메세지 발생
 }
 CView::OnLButtonDown(nFlags, point);
}

void CVirtualButtonView::OnLButtonUp(UINT nFlags, CPoint point)
{
 // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
 if(m_bClicked) //버튼이 위치한 영역인가?
 {
  m_bClicked=!m_bClicked;
  RedrawWindow(&m_BtnRect);

 }

 if(m_BtnRect.PtInRect(point))
 {
  AfxMessageBox(_T("버튼을 클릭했다."));
 }
 CView::OnLButtonUp(nFlags, point);
}


void CVirtualButtonView::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 // TODO: 여기에 메시지 처리기 코드를 추가합니다.
 // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.

 CRect Rect(m_BtnRect);
 Rect += CRect(1,1,1,1);
 dc.Rectangle(&Rect);
  // 주어진 좌표와 색상으로 테두리 없는 사각형을 그리고 내부를 칠한다. 자주쓰임
 dc.FillSolidRect(&m_BtnRect, ::GetSysColor(COLOR_BTNFACE));
 //GetSysColor() 함수는 인자에 해당하는 시스템 색상을 반환하며, 윈도우 운영체제에 설정된 값이다.

 if(m_bClicked)
 {
  //테두리가 잇는 사각형을 그리며, 왼쪽/위 선의 색상과 오른쪽/아래 선의 색상을 각기 다른 색으로 명시가 가능
  dc.Draw3dRect(m_BtnRect, ::GetSysColor(COLOR_3DSHADOW),
         ::GetSysColor(COLOR_3DLIGHT));
  
 
 }else
 {
  dc.Draw3dRect(m_BtnRect, ::GetSysColor(COLOR_3DLIGHT),
   ::GetSysColor(COLOR_3DSHADOW));
 }

 dc.SetBkColor(::GetSysColor(COLOR_BTNFACE));
 dc.SetTextColor(::GetSysColor(COLOR_BTNTEXT));

 if(m_bClicked)
 {
  CRect Rect = m_BtnRect;
  Rect += CRect(0,0,2,2);
  //DrawText() 함수는 다양한 옵션으로 문자열을 쓸 수 있다.
  dc.DrawText(_T("Test Button"), &Rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
 }else
 {
  dc.DrawText(_T("Test Button"), &m_BtnRect,  DT_CENTER | DT_SINGLELINE | DT_VCENTER);
 }
}

'Windows > MFC' 카테고리의 다른 글

비트맵과 이미지 처리  (0) 2011.11.14
DrawText() 함수 예제 및 연습  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12
MFC 코드의 흐름  (1) 2011.11.12


void CFontDemoView::OnPaint()
{
 CPaintDC dc(this); // device context for painting

 CFont Font;
 CFont* pOldFont;

 LOGFONT lf;
 ::ZeroMemory(&lf , sizeof(lf));
 wsprintf(lf.lfFaceName, _T("%s"), _T("Arial")); //폰트이름은 Arial
 lf.lfHeight = 30; //사이즈는 20
 Font.CreateFontIndirect(&lf); //LOGFONT 구조체 정보를 기반으로 CFont 클래스 객체를 생성함.
//보통 이렇게 LOGFONT 구조체에 크기와 이름값만 명시하고 쓴다. 

 pOldFont = dc.SelectObject(&Font);
 dc.TextOut(10,10, _T("this is text test with logfont"));
 dc.SelectObject(pOldFont);
 Font.DeleteObject(); //폰트객체삭제
 
 // TODO: 여기에 메시지 처리기 코드를 추가합니다.
 // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.
}


경우에 따라 어떤 글골이 시스템에 설치되어있는지 확인할 때가 있는데, 이때는 ::EnumFonts() 함수나
::EnumFontFamiliesEx() 함수를 이용하면 된다.
보통 문자열 출력에는 TextOut () 함수를 많이 쓰는데, 치명적인 약점은 바로 탭 문자(\t) 가 안된다는 것이다.
이것은 텍스트 편집기를 개발하는 상황에는 굉장히 치명적이다. 이 경우에는 TabbedTextOut() 함수를 사용한다.

int nTabStop = 40; //탭 사이즈가 40임을 명시
dc.TabbedTextOut(20,70,_T("\tText String"), 1, &nTabStop, 20); //직접  \t 를 사용해서 탭출력, 반환값은 CSize, 주로 좌표보다는 폭과 높이를 명시하는 경우에 사용함. (cx,cy) , 이것은 에디트 컨트롤을 직접 구현할 때 중요하다. 아니면 신텍스 컬러링 편집기 구현할때나 쓰인다.
dc.SelectObject(pOldFont);

'Windows > MFC' 카테고리의 다른 글

DrawText() 함수 예제 및 연습  (0) 2011.11.13
직접 버튼 그리기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12
MFC 코드의 흐름  (1) 2011.11.12
메시지맵 (MessageMap)  (0) 2011.11.12

대표적인 키보드 메시지로는 WM_KEYDOWN , WM_KEYUP , WM_CHAR 가 있으며,

Alt키와 f10 이 두키를 누르면 WM_KEYDOWN 이 발생하지 않고 WM_SYSKEYDOWN,UP 이 발생

만약 WM_KEYDOWN 메시지의 파라미터에 아스키 코드의 문자가 입력되면 메시지 루프의 TranslateMessage() 함수가
 
WM_KEYDOWN 메시지를 다시 WM_CHAR 메시지로 전환하여 전송하기도 한다.

다음은 키보드 방향키를 누르면 자식 윈도우가 움직이는 예제다.

void CKeyMoveView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//nChar : 어떤 키를 눌렀는지에 대한 정보가 들어잇다. WinUser.h 파일에 정의
//nRepCnt : 반복 횟수를 의미, 보통 한 번누루므로 1
//nFlags : 키보드에 대한 상세한 상태 정보를 담고 있다. 비트 단위로 살펴봐야 한다. 확장키값!

 CRect rect; //차일드 윈도우의 좌표 및 크기
 CPoint ptChild; //차일드 윈도우의 좌표
 m_wndChild.GetWindowRect(&rect); //자식 윈도우 크기를 가져와서 rect에 저장
 ptChild.x = rect.left; // 자식 윈도우 x좌표 대입
 ptChild.y = rect.top; // 자식 윈도우 y좌표 대입

 ScreenToClient(&ptChild);

 switch(nChar)
 {
 case VK_LEFT:
  ptChild.x -= 10;
  break;
 case VK_RIGHT:
  ptChild.x += 10;
  break;
 case VK_UP:
  ptChild.y -= 10;
  break;
 case VK_DOWN:
  ptChild.y += 10;
  break;
 default:
  break;
 }

 m_wndChild.SetWindowPos(&CWnd::wndTop, ptChild.x, ptChild.y, 0,0, SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOSIZE);

 CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

다음은 엔터나 ESC, 백스페이스바를 제외한 문자열을 입력햇을 때 윈도우에 해당 문자열이 나오는 예제다.

//ESC,엔터,백스페이스바도 이상하게 문자열로 취급되므로 제외시켜준다.

void CKeyMoveView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 CString m_strText;
 m_strText.Format(_T("%c"), nChar);
 if(nChar != VK_BACK && nChar != VK_RETURN && VK_ESCAPE)
 {
  m_wndChild.SetWindowText(m_strText);
 }

 CView::OnChar(nChar, nRepCnt, nFlags);
}



시스템 키보드 메시지
Alt 키와 f10 키를 눌렀을 때에 WM_SYSKETDOWN , UP 메시지가 나온다.
다음은 alt 키를 누른 상태에서 스페이스바를 눌렀을 때를 테스트한다.
GetKeyState() 함수가 반환한 16비트 값에서 상위 바이트는 키를 누른 상태인지에 대한 정보가
들어잇으며 하위 바이트에는 토글에 대한 정보가 들어 있다.
스페이스바는 토글 키가 아니니 굳이 하위 바이트 정보를 확인할 필요가 없다.
밑에와 같은 토글키는 GetKeyState() 함수가 반환한 결과의 하위 바이트를 검사하며,
On/Off 상태는 1번 비트를 확인해서 1이면 누른 상태이다. 자주 사용하므로 꼭 알아두자.

void CKeyMoveView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 CString m_StrMessage = _T("");

 WORD wResult = ::GetKeyState(VK_SPACE); //키의 상태를 저장한다
 
 BYTE byHigh = HIBYTE(wResult); //상위 바이트를 떼어내서 확인한다.

 if(byHigh & 0x01) // 상위 바이트의 1번 비트가 1이면 키가 눌린 상태
 {
  m_StrMessage += _T("Alt + space");
  
  //같은 방법으로 caps lock 키 조사
  wResult = ::GetKeyState(VK_CAPITAL);
  BYTE byLow = LOBYTE(wResult);
  if(byLow & 0x01 )
   m_StrMessage += _T(" + Caps Lock On");
  else
   m_StrMessage += _T(" + Caps Lock Off");
  
  AfxMessageBox(m_StrMessage);
}  

 CView::OnSysKeyDown(nChar, nRepCnt, nFlags);
}


WM_CHAR 메시지와 마찬가지로 시스템 키보드 메시지에는 WM_SYSCHAR 메시지가 있으며,
이것 역시 translateMessage() 함수가 생성한다. 이 메시지는 alt 키와 문자키 입력이 조합된 경우에
발생한다. 즉 alt + s 와 같은 조합키를 눌렀을 때 발생하는 메시지이다.

'Windows > MFC' 카테고리의 다른 글

직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
MFC 코드의 흐름  (1) 2011.11.12
메시지맵 (MessageMap)  (0) 2011.11.12
CFrameWnd 클래스  (0) 2011.11.12
MFC 응용 프로그램의 시작

Function: CSdiSeqApp::CSdiSeqApp(void),
Function: CSdiSeqApp::InitInstance(void),
Function: CSdiSeqDoc::CSdiSeqDoc(void),
Function: CMainFrame::CMainFrame(void),
Function: CMainFrame::LoadFrame(unsigned int, unsigned long, CWnd *, CCreateContext *),
Function: CMainFrame::PreCreateWindow(tagCREATESTRUCTW &),
Function: CMainFrame::PreCreateWindow(tagCREATESTRUCTW &), //아직 메인프레임 윈도우 생성 안되었다. 
Function: CMainFrame::OnCreate()
Function: CMainFrame::OnCreateClient(tagCREATESTRUCTW *, CCreateContext *),
Function: CSdiSeqView::CSdiSeqView(void),
Function: CSdiSeqView::Create(const wchar_t *, const wchar_t *, unsigned long, const tagRECT &, CWnd *, unsigned int, CCreateContext *),
Function: CSdiSeqView::PreCreateWindow(tagCREATESTRUCTW &),
Function: CSdiSeqView::OnCreate(tagCREATESTRUCTW *),
Function: CSdiSeqView::OnShowWindow(int, unsigned int), //눈에 보이는 윈도우가 생성된다.
CFrameWnd::OnCreateClient() - Return
CMainFrame::OnCreate() - Return
Function: CSdiSeqDoc::OnNewDocument(void),
Function: CSdiSeqView::OnInitialUpdate(void), // 문서,뷰에서 매우 중요, 화면을 깨끗이 정리하고 적합한 내용을 출력
Function: CMainFrame::OnActivateApp(int, unsigned long),
Function: CMainFrame::OnActivate(unsigned int, CWnd *, int),
Function: CMainFrame::OnShowWindow(int, unsigned int),
Function: CSdiSeqApp::Run(void),

MFC 응용 프로그램의 종료


CWinApp::Run()
Function: CMainFrame::OnClose(void), // DestroyWindow() 함수를 호출 후 -> WM_DESTROY
Function: CMainFrame::OnShowWindow(int, unsigned int),
Function: CMainFrame::OnActivate(unsigned int, CWnd *, int),
Function: CMainFrame::OnActivateApp(int, unsigned long),
Function: CMainFrame::DestroyWindow(void), //자식 윈도우인 클라이언트 뷰윈도우를 먼저 파괴시킨다.
Function: CMainFrame::OnDestroy(void),
Function: CSdiSeqView::OnDestroy(void),
//자식 윈도우를 먼저 소멸한다.
Function: CSdiSeqView::PostNcDestroy(void), //PostNcDestroy() - 반환하면 윈도우 객체 메모리 소멸
Function: CSdiSeqView::~CSdiSeqView(void), //부모 윈도우도 소멸되기 시작한다.
Function: CMainFrame::OnNcDestroy(void), //모든 자식이 죽고 화면에 보이는 메인 프레임도 소멸
Function: CMainFrame::PostNcDestroy(void),
Function: CMainFrame::~CMainFrame(void),
CFrameWnd::OnNcDestroy() - Return
Function: CSdiSeqDoc::~CSdiSeqDoc(), // CView와 연결된 CDocument도 소멸
CMainFrame::OnClose() - Return // 메인 프레임 윈도우의 WM_CLOSE 메시지 처리 끝
Function: CSdiSeqApp::ExitInstance(void), // App객체가 소멸되기 전에 필요한 정리 수행
Function: CSdiSeqApp::Run(void) - Return, // 위 함수가 반환하면 메인 메시지 루프종료 -> 프로그램종료

'Windows > MFC' 카테고리의 다른 글

직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12
메시지맵 (MessageMap)  (0) 2011.11.12
CFrameWnd 클래스  (0) 2011.11.12

메시지 맵은 Win32 APi 윈도우 프로시저 함수에서 볼 수 있는 switch case 문을 대체 하는 것이다.
메시지 맵이란 특정 메시지가 발생했을 때 어떤 함수를 호출해야 하는지 명시하는 매크로의 집합체이다.
다음은 CMainFrame 클래스에 대한 메시지 맵이다.

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
 ON_WM_CREATE()
END_MESSAGE_MAP()

MFC  메세지 맵은 BEGIN_MESSAGE_MAP 매크로와 END_MESSAGE_MAP() 매크로를 이용하여 시작과 끝을 명시한다.
ON_WM_CREATE() 는 WM_CREATE 메시지 핸들러 함수를 등록할 경우 추가되는 매크로이다.


'Windows > MFC' 카테고리의 다른 글

직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12
MFC 코드의 흐름  (1) 2011.11.12
CFrameWnd 클래스  (0) 2011.11.12
SDI 문서 / 뷰 구조를 갖는 MFC 응용 프로그램을 개발할 때 최상위 프레임 윈도우에 해당하는 클래스는 CMainFrame 클래스이며 이 클래스는 CFrameWnd 클래스를 상속받아 만들어진다. 단 MDI 에서는 CMDIFrameWnd 클래스로 대체된다.
CObject -> CCmdTarget -> CWnd -> CFrameWnd

CFrameWnd 클래스는 프레임이 있는 윈도우가 가져야할 기본 기능을 정의한 클래스라 할 수 있고, 부모 윈도우가 없는 최상위가 될 수 있는 윈도우이며, 다양한 형식의 자식 윈도우를 가질 수 있다.

프레임 윈도우의 특징은 컨트롤 윈도우와의 상관관계인데, CToolBar 클래스같은 컨트롤 윈도우가 프레임 윈도우에 도킹이 되는 것을 말한다. 이것들은 CMainFrame 클래스의 OnCreate() 함수에서 생성하는 코드를 확인할 수 있다.

'Windows > MFC' 카테고리의 다른 글

직접 버튼 그리기  (0) 2011.11.13
글꼴과 문자열 다루기  (0) 2011.11.13
키보드 입력  (0) 2011.11.12
MFC 코드의 흐름  (1) 2011.11.12
메시지맵 (MessageMap)  (0) 2011.11.12




개인적으로 정말 조아하는 zxr400r!!

외수용은 최고속이 240이나 된다~!

크~~ 물론 이 차도 cbr400rr처럼 단종된 모델이긴 하지만

아직도 인기가 너무나 많다!!

솔직히 zxr400r은 매니아층이 두껍기로 유명한데

그 특유의 갈갈대는 엔진소리와 박력있는 모습으로

많은 매니아층을 형성햇다.

돈있으면,,솔직히 말해서,, 수리비와 유지비가 더 많이 들긴하지만

zxr400r을 택할것같다!!

 

'Review > Moto Bike Review' 카테고리의 다른 글

CBR 600RR  (0) 2011.07.15


--------파일의 시간 정보 얻어오기

윈도우에서는 대표적으로 다음과 같은 정보를 얻을 수 있다.

1) 만든 날짜
2) 수정한 날짜(마지막으로)
3) 액세스한 날짜(마지막으로)


이를 위해 다음 함수를 사용한다.

bool GetFileTime(
 HANDLE hFile,
 LPFILETIME lpCreationTime,
 LPFILETIME lpLastAccessTime,
 LPFILETIME lpLastWriteTime
);

1) 시간 관련 정보를 얻을 대상 파일의 핸들을 지정한다
2) 파일이 생성된 시간을 얻기 위해 FILETIME 구조체 변수의
주소값을 전달하며, NULL을 전달하는 것도 가능하다.
3) 파일의 마지막 접근 시간을 얻기 위해 ~~
4) 파일의 마지막 데이터 갱신 시간(덮어쓰기 포함)을 얻기 위해 ~~~

FILETIME 구조체의 멤버들을 자세히 알려고 하는 것보단
다음 사실이 중요하다.

"FILETIME 구조체는 시간 정보를 나타내는 8바이트 자료형(DOWRD * 2)이다.
그리고 이 구조체는 UTC 기반으로 시간을 표현한다.

GetFileTime 함수는 UTC 기반으로 시간 정보를 돌려준다.

UTC 란, Coordinated Universal Time 의 간략한 표현으로, 세계 시간의 기준을
만들기 위해 정의된 시간이다. 간단히 설명하면,
세계 각 나라의 시간은 현재 제각각이며, 초 단위 아래로 내려가면 오차도 심하다.
그래서 나노초 단위의 높은 정밀도를 기준으로 세계 시간을 구성할 필요성이 대두되어
1601년 1월 1일을 기준으로 100 나노초 단위 기준으로 지나간 시간을 계산하는 것이다.
쉽게 말해 시,분,초는 생략하고, 지금이 2011년 8월 5일이라면,
현재의 UTC는 [2010-08-05] - [1601-01-01] 을 100나노초 단위로 환산한 값이 UTC가 된다.
이러한 UTC는 지금도 오차없이 유지되고 있으며, 정밀도를 최대한으로 높이기 위하여
높은 정밀도의 Atomic Clocks 라는 시계가 사용된다고 한다.
따라서 시간 정보를 얻을 경우 대부분 UTC 기반으로 얻게 되며,
문제는 우리가 원하는 것은 우리가 이해할 수 있는 스케일의 시간 정보라는 것이다.
이를 위해 우리가 원하는 타입으로 시간 정보를 변경하는 함수가 필요하며,
이를 다음 예제를 통해 우리가 이해할 수 있는 형태의 스케일로 변환하는 과정을 보여준다.

TCHAR fileName[] = _T("RealScaleViewer.exe");

TCHAR fileCreateTimeInfo[100];
TCHAR fileAccessTimeInfo[100];
TCHAR fileWriteTimeInfo[100];

FILETIME ftCreate, ftAccess, ftWrite;

SYSTEMTIME stCreateUTC, stCreateLocal;
SYSTEMTIME stAccessUTC, stAccessLocal;
SYSTEMTIME stWriteUTC, stWriteLocal;

HANDLE hFile = CreateFile(

 fileName, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATRRIBUTE_NORMAL, 0
);

//파일 시간 정보 추출
if( !GetFileTime(hFile, &ftCreate, &ftAccess,  &ftWrite))
{
 _tprintf(_T("GetFileTime Function call Fault!!\n"));
 return false;
}

//시간 정보 변환
FileTimeToSystemTime(&ftCreate, &stCreateUTC);
SystemTimeToTzSpecificLocalTime(
 NULL, &stCreateUTC, &stCreateLocal
);

~~ (Create대신 Access와 Write로 바꿔서 대입)

~~

_stprintf(
 fileCreateTimeInfo, _T("%02d / %02d / %d   %02d: %02d"),
 stCreateLocal.wMonth, stCreateLocal.wDay, stCreateLocal.wYear,
 stCreateLocal.wHour, stCreateLocal.wMinute
);

~~ (Create대신 Access와 Write로 바꿔서 대입)



---------------------------------------------파일을 열어서 읽고 쓰고 닫는 예제
// 유니코드 기반 //

int _tmain(int argc, TCHAR* argv[])
{
 TCHAR fileName[] = _T("data.txt");
 TCHAR fileData[] = _T("Just Test String");
 
 HANDLE hFile = CreateFile (  //파일 열기
 
   fileName,  GENERIC_WRITE ,  FILE_SHARE_WRITE,
   0, CREATE_ALWAYS, FILE_ATTRIBUTES_NORMAL,  0
  );

 if( hFile == INVALID_HANDLE_VALUE)
 {
  _tprintf(_T("File Created Fault!!"));
  return -1;
 }

 DWORD numOfByteWritten = 0;
 WriteFile( //파일 저장
  hFile,  fileData,  sizeof(fileData), &numOfByteWritten,  NULL
  );
 
 _tprintf(_T("Written data size = %u \n", numOfByteWritten);
 CloseHandle(hFile);

 return 0;
}

-----------------------------------앞서 만든 파일에 저장된 데이터를 읽어 들이는 예제


TCHAR fileName[] = _T("data.txt");
TCHAR fileData[100];

HANDLE hFile = CreateFile( //파일 열기
 fileName,  GENERIC_READ,  FILE_SHARE_READ,
 0,  OPEN_EXISTING, FILE_ATTRIBUTES_NORMAL, 0 
);

DWORD numOfByteRead =0;
ReadFile(
 hFile, fileData, sizeof(fileData), &numOfByteRead, NULL
);
fileData[numOfByteRead/sizeof(TCHAR)] = 0;

_tprintf(_T("Read Data Size : %u \n"), numOfByteRead);
_tprintf(_T("Read String : %s \n"), fileData);

CloseHandle(hFile);


//파일 핸들 닫기 / 열기
HANDLE WINAPI CreateFile( //파일을 열때
  __in          LPCTSTR lpFileName,
  __in          DWORD dwDesiredAccess,
  __in          DWORD dwShareMode,
  __in          LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in          DWORD dwCreationDisposition,
  __in          DWORD dwFlagsAndAttributes,
  __in          HANDLE hTemplateFile
);

1) 개방할 파일 이름을 지정한다.
2) 읽기 쓰기 모드를 지정한다
3) 파일 공유 방식을 지정한다.
4) 보안 속성을 지정한다
5) 파일이 생성되는 방법을 지정한다
6) 파일의 특성 정보를 설정한다
7) 기존에 존재하는 파일과 동일한 특성을 가지는 새 파일을 만들 때 사용되는 전달인자다.
일반적으로 NULL 을 넣는다.

열린 파일을 종료할 때는 여느 커널 오브젝트의 핸들과 마찬가지로 CloseHandle() 을 호출한다.

파일을 개방할 때도 커널 오브젝트가 생성되고, 핸들이 반환된다.
이 함수의 호출을 통해 생성되는 커널 오브젝트에는 파일에 대한 정보로 가득할 것이다.


//파일 읽기 / 쓰기

파일에 데이터를 읽을 때에는 다음 함수를 사용한다.

BOOL WINAPI ReadFile(
  __in          HANDLE hFile,
  __out         LPVOID lpBuffer,
  __in          DWORD nNumberOfBytesToRead,
  __out         LPDWORD lpNumberOfBytesRead,
  __in          LPOVERLAPPED lpOverlapped
);

1) 데이터를 읽을 파일의 핸들을 지정한다
2) 읽어 들인 데이터를 저장할 버퍼(배열, 메모리)의 주소(포인터)를 지정한다.
3) 파일로부터 읽고자하는 데이터의 크기를 바이트 단위로 지정한다.
4) 실제 읽어 들인 데이터 크기를 얻기 위한 변수의 주소를 지정한다.
5) 나중에 설명한다.

반대로 파일에 데이터를 저장할 때에는 다음 함수를 이용한다.

BOOL WINAPI WriteFile( //데이터 저장을 위해
  __in          HANDLE hFile,
  __in          LPCVOID lpBuffer,
  __in          DWORD nNumberOfBytesToWrite,
  __out         LPDWORD lpNumberOfBytesWritten,
  __in          LPOVERLAPPED lpOverlapped
);


1) 데이터를 저장할 파일의 핸들을 지정한다
2) 데이터를 저장하고 있는 버퍼의 주소를 지정한다
3) 파일에 저장하고자 하는 데이터 크기를 바이트 단위로 지정한다.
4) 파일에 실제 저장된 데이터 크기를 얻기위해 변수의 주소를 지정한다.
5) 나중에 설명한다.


자식 프로세스에게 핸들 정보를 전달하기 위해 파일을 활용하는 방법은
잘 동작하긴 하나 촌스럽고 안정적이지 않은 방식이다.
이보다는 프로세스 생성 시 메인 함수의 매개변수를 활용하는 것이
훨씬 안정적이다.


이번에는 프로세스 환경변수를 활용하는 방법을 알아보겠다.
프로세스별로 별도의 메모리 공간에 문자열 데이터를 저장하고 관리할 수
있도록 되어 있으며, 문자열의 구조는 다음과 같고, 이를 환경 변수라 한다.

key = value

[key,value] 의 형태를 띠므로 둘 이상의 데이터를 관리하기가 좋으며,
부모 프로세스는 자식 프로세스의 환경변수를 등록할 수도 있고,
그냥 부모 프로세스의 환경 변수를 상속시킬 수 있다.

값을 참조하는 과정에서 key와 value를 따로 분리시킬 필요도 없는데,
이는 다음 함수를 통해 우리가 쉽게 값을 등록 및 참조할 수 있기 때문이다.
다음은 프로세스 환경변수를 등록할 때 사용하는 함수다.

BOOL SetEnvironmentVariable(
 LPCTSTR lpName,
 LPCTSTR lpValue
);

1) Key 에 해당하는 값을 지정하며, 이후 Key를 통해 value 값을 참조하게 된다.
2) value 에 해당하는 값을 지정한다.

다음은 위 함수를 통해서 등록한 환경변수를 참조할 때 사용하는 함수이다.

DWORD GetEnvironmentVarible(
 LPCTSTR lpName,
 LPTSTR lpBuffer,
 DWORD nSize
);

1) Key를 전달하며, Key에 해당하는 value를 얻게 된다.
2) value 값을 저장하기 위한 메모리의 주소를 지정한다
3) lpBuffer 가 가리키는 메모리의 크기를 지정한다

이 함수는 성공시에 lpBuffer 에 저장된 문자열의 길이를 반환한다.
다음 예제는 위의 두 함수의 사용법을 보여준다.
부모 프로세스가 자신의 환경변수를 등록하고, 자식 프로세스에게 상속시켜
자식 프로세스가 값을 확인하는 간단한 예제이다.
우선 환경변수를 설정하는 부모 프로세스부터 보자.

int _tmain(int argc, TCHAR* argv[])
{
 SetEnvironmentVariable(
  _T("Good"),  _T("Morning") 
 );

 SetEnvironmentVariable(
  _T("Hey"),  _T("Ho") 
 );

 SetEnvironmentVariable(
  _T("Big"),  _T("Boy") 
 );
 
 STARTUPINFO si={0,};
 PROCESS_INFORMATION ps={0,};
 si.cb=sizeof(si);

 CreateProcess(
  NULL, _T("EnvChild"), NULL, NULL, FALSE,
  CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
  NULL, //이게 NULL이면 부모 프로세스에 등록되어 있는 환경변수를 등록하겠다 라는 의미다.
  NULL,
  &si,  &pi
 );

 CloseHandle( pi.hProcess);
 CloseHandle( pi.hThread);


 return 0;
}


------------정리---------------------------------------------------------------------------

1. 핸들 테이블
커널 오브젝트와 핸들 사이에 핸들 테이블이 존재한다.
이로서 핸들을 참조하여 특정 커널 오브젝트를 가리킬 수 있다.

2. 핸들과 핸들 테이블
핸들 테이블은 프로세스별로 독립적이다. 그리고 숫자가 핸들로서 의미를 지니기 위해서는
해당 숫자가 핸들 테이블에 등록되어야 한다. 등록되는 순간부터 핸들이라 할 수 있으며,
이 핸들을 통해 커널 오브젝트에 접근하는 것이 가능하다.

3. 핸들의 상속
핻늘은 자식 프로세스를 생성하는 과정에서 상속할 수 있다.
핸들이 자식 프로세스에게 상속된다는 말은 부모 프로세스의 핸들 테이블 정보가
자식 프로세스의 핸들 테이블에 복사된다는 뜻이다.

4. 가짜 핸들(Pseudo 핸들)
GetCurrentProcess 함수 호출을 통해 얻은 핸들을 가짜 핸들이라 하며,
핸들 테이블에 등록된 핸들값이 아닌, 자기 자신의 프로세스를
가리키기 위한 약속된 상수이다. 핸들 테이블에 등록된 핸들을 얻기 위해서는
DuplicateHandle() 함수를 사용해야 한다.

5. 파이프
이름없는 파이프와, 이름있는 파이프가 있으며, 이름없는 파이프의 경우 아주
유용하므로 잘 알아둬야 한다.


/*
    ListProcessInfo.cpp
    프로그램 설명: 현재 실행중인 프로세스 정보 출력.
*/

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <tlhelp32.h> /* 프로세스 정보 추출관련 함수 선언 */

int _tmain(int argc, TCHAR * argv[])
{
 /**** Snapshot! 말 그대로 사진을 찍는 함수. ************************
  **** 프로세스 상태 사진을 찍기 위해 TH32CS_SNAPPROCESS 인자 전달 **/
 HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 );
 if( hProcessSnap == INVALID_HANDLE_VALUE )
 {
  _tprintf( _T("CreateToolhelp32Snapshot error! \n") );
  return -1;
 }

 /** 위 함수 CreateToolHelp32Snapshot을 통해 정보는 이미 얻었다. **
  ** 다음은 추출한 프로세스 정보를 순차 접근하는 과정을 보여한다 **/

 PROCESSENTRY32 pe32;   /* 프로세스 정보 얻기위한 구조체 변수 */

 /* PROCESSENTRY32 변수는 사용하기 전에 크기 정보 초기화 해야한다 */
 pe32.dwSize = sizeof( PROCESSENTRY32 );

 /** 첫 번째 프로세스 정보 얻을 때는 Process32First 함수 사용 ***
  ** 그 이후 프로세스 정보 얻을 때는 Process32Next 함수 사용 ****/
 if( !Process32First( hProcessSnap, &pe32 ) )
 {
  _tprintf( _T("Process32First error! \n") );
  CloseHandle( hProcessSnap );
  return -1;
 }

 HANDLE hProcess;
 do
 {
  /* 프로세스 이름, ID 정보 출력 */
  _tprintf(_T("%25s %5d \n"), pe32.szExeFile, pe32.th32ProcessID);

 } while( Process32Next( hProcessSnap, &pe32 ) );

 CloseHandle( hProcessSnap );
 return 0;
}


앞에서 커널 오브젝트의 UsageCount는 공유하는 프로세스의 수만큼 증가한다고 설명했다.
그렇다면 커널 오브젝트를 참조하는 프로세스가 되기 위한 조건은 무엇일까?
핸들 테이블의 관점에서 봤을 때

"프로세스가 핸들을 얻게 되었다"

위의 말의 의미는, 핸들 테이블에 해당 핸들에 대한 정보가 갱신(추가) 되었음을 의미하는 것이다."

예를 들어 createMailslot 함수의 호출을 통해 메일슬롯을 생성햇다고 가정하면,
1) 메일 슬롯 리소스 생성
2) 커널 오브젝트 생성
3) 핸들 정보가 핸들 테이블에 갱신
4) CreateMailslot 함수를 빠져나오며 핸들값 반환

대부분의 경우 프로세스가 핸들을 얻게 되었다고 하면,
보통 4번째 단계에 초점을 맞춰서 생각을 한다. 그러나 이는 잘못된 것이다.
프로세스가 핸들을 얻었다고 말할 수 있는 부분은 3번째 단계 이후부터다.
즉 핸들 테이블에 A 핸들에 대한 정보가 등록되면, A 핸들을 얻은 것이다.

다음은 CreateMailslot 함수와 CreateProcess 함수에서 생성되는 자식 프로세스의
핸들을 상속하기 위한 샘플 코드들이다.

SECURITY_ATTRIBUTES sa;
sa.nLength=sizeof(sa);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE;

CreateMailslot(~,~,~,&sa);

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

SECURITY_ATTRIBUTES sa;
sa.nLength=sizeof(sa);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE;

CreateProcess(~,~,&sa,~~~~);

이렇게 자식 프로세스의 핸들이 상속될 수 있도록 설정하면,
이후에 생성되는 자식 프로세스에게 상속될 것이다.

 

Pseudo 핸들과 핸들의 중복(Duplicate)

현재 실행 중에 있는 프로세스 자신의 해늘을 얻는 방법으로써
GetCurrentProcess 함수를 통해 프로세스 자신의 커널 오브젝트에 접근이 가능하다.
하지만 이 함수 호출을 통해 얻은 핸들을 가리켜 가짜 핸들(Pseudo Handle) 이라 한다.
왜냐면 이렇게 얻어진 핸들은 핸들 테이블에 등록되어 있지도 않으며,
단지 현재 실행 중인 프로세스를 참조하기 위한 용도로 정의해 놓은, 약속된 상수가 반환되는 것이기 때문이다.
(현재 -1이 반환되도록 정의되어 잇다) 따라서 자식 프로세스로 상속되지 않으며,
CloseHandle 함수의 인자로 전달할 필요도 없다.
현재 실행중인 프로세스가 GetCurrentProcess 함수 호출을 통해 얻은 가짜 핸들 이외에 핸들
테이블에 등록되어 있는 진짜 핸들을 얻어야 할 필요성이 있는가?
우선 진자 핸들을 얻는 방법에 대해 알아보자.
다음은 DuplicateHandle 이라는 함수이며, 핸들을 복사하는 기능을 지닌다.
만약 현재 실행중인 프로세스의 진짜 핸들을 얻고자 한다면 이 함수를 사용하자

BOOL WINAPI DuplicateHandle(
  __in          HANDLE hSourceProcessHandle,
  __in          HANDLE hSourceHandle,
  __in          HANDLE hTargetProcessHandle,
  __out         LPHANDLE lpTargetHandle,
  __in          DWORD dwDesiredAccess,
  __in          BOOL bInheritHandle,
  __in          DWORD dwOptions
);

1) 복제할 핸들을 소유하는 프로세스를 지정한다
2) 복제할 핸들을 지정한다
3) 복제된 핸들을 소유할 프로세스를 지정한다
4) 복제된 핸들값을 저장할 변수의 주소를 지정한다
5) 복제된 핸들의 접근권한을 지정한다. 방대한 내용이 잇으므로 msdn 참고할 것
6) 복제된 핸들의 상속 여부를 지정한다. TRUE 전달 시 새로운 자식 프로세스로 상속되며, FALSE 전달 시 상속되지 않는다
7) DUPLICATE_SAME_ACCESS 를 전달하면 원본 핸들과 동일한 접근권한을 가지게 된다.
그 외에 DUPLICATE_CLOSE_SOURCE 가 올 수 있는데 이 인자는 원본 핸들을 종료시킨다( CloseHandle과 같다).
이 둘은 비트 단위 OR 연산자를 통해 동시 전달이 가능하다.
이 함수는 4번째 까지의 전달인자가 가장 중요하다.

프로세스의 핸들 테이블 도입

핸들 테이블은 핸들 정보를 저장하고 있는 테이블로서 프로세스별로 독립적이다.
쉽게 말해 각각의 프로세스가 자신만의 핸들 테이블을 하나씩 구성하고 관리한다.
프로세스가 CreateProcess 함수나 CreateMailslot 과 가은 함수 호출을 통해 리소스 생성을
요구한 결과로 핸들 정보를 얻게 될 경우, 프로세스 자신에게 속해 있는 핸들 테이블에 해당 정보가 등록된다.
여기서 가장 중요한 것은 핸들 테이블은 프로세스별로 독립적이라는 것이다.


핸들의 상속

CreateProcess 함수를 호출하면 새로운 자식 프로세스가 생성된다.
또한 자식 프로세스를 위한 핸들 테이블도 더불어 생성된다.
이때 부모 프로세스의 핸들 테이블과 자식 프로세스의 핸들 테이블은 어떤 관계가 있을까?
그것은 CreaetProcess 함수 호출 시 전달되는 인자(다섯번째)가 무엇이냐에 따라서 부모 프로세스 핸들 테이블에
등록되어 있는 핸들 정보는 새롭게 생성되는 자식 프로세스에게 상속될 수 있다.
이러한 특성은 여러 상황에서 유용하게 사용 될 수 있다.

 

핸들의 상속에 대한 이해

자식 프로세스는 부모 프로세스의 핸들 테이블에 등록되어 있는 핸들 정보를 상속받을 수 있다.
하지만 모든 핸들 정보를 상속받는 것은 아니다.
부모 프로세스 핸들 테이블에서 상속여부가 Y 인 것만 상속받을 수 있으며,
자식 프로세스 핸들 테이블에서 상속여부에 대한 정보도 변경 없이 그대로 상속된다.
따라서 자식이 또 다른 자식을 생성해도 이 핸들에 대한 정보는 계속해서 상속된다.
그렇다면 상속되는 핸들과 상속되지 않는 핸들에 대한 기준은 어디서 나뉠까?
이는 리소스를 생성하는 함수의 전달인자를 통해 프로그래머가 결정할 수 있다.

핸들의 상속을 위한 전달 인자

모든 자식 프로세스가 무조건 부모 프로세스의 핸들을 상속하는 것은 아니다.
다음은 CreateProcess 함수의 선언이다.

BOOL WINAPI CreateProcess(
  __in          LPCTSTR lpApplicationName,
  __in_out      LPTSTR lpCommandLine,
  __in          LPSECURITY_ATTRIBUTES lpProcessAttributes,
  __in          LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in          BOOL bInheritHandles,
  __in          DWORD dwCreationFlags,
  __in          LPVOID lpEnvironment,
  __in          LPCTSTR lpCurrentDirectory,
  __in          LPSTARTUPINFO lpStartupInfo,
  __out         LPPROCESS_INFORMATION lpProcessInformation
);

이 함수의 다섯번째 전달인자는 자식 프로세스에게 핸들 테이블에 등록되어 있는
핸들정보를 상속할 건지 말건지 결정하는 요소다.
TRUE 일 경우 핸들 테이블 정보는 자식에게 상속된다.
핸들의 상속 여부를 결정하는 것이 아니라, 부모 프로세스가 소유하고 있는 핸들 테이블
정보의 상속 여부를 결정하는 것이다.



상태에 대한 이해

커널 오브젝트는 두 가지의 상태를 지니며,
하나는 Signaled 상태(신호를 받은 상태) 이고, 하나는 Non-Signaled 상태이다.
특정 상황 하에서 커널 오브젝트의 상태는 Signaled 에서 Non으로 Non에서 Signal 로 변경된다.
즉 Non-Signaled 상태일 때 FALSE 값을 지니고, Signaled 면 TRUE 값을 가진다.


프로세스 커널 오브젝트의 상태에 대한 이해

커널 오브젝트의 상태는 리소스에 특정 상황이 발생하였음을 알려주기 위해 존재하는 것이다.
특정상황은 리소스마다 다르기 때문에 커널 오브젝트의 상태가 변하는 시점은 커널 오브젝트의 종류에 따라 달라진다.


프로세스 커널 오브젝트는 프로세스가 생성될 때 만들어지며,
처음 생성될 때 커널 오브젝트의 상태는 Non-signaled 상태에 놓이다가, 프로세스가 종료될 때 signaled 상태로 변경된다.
이는 일종의 약속이다. 그러므로 우리는 Signaled 상태의 프로세스 커널 오브젝트를 보고
프로세스가 종료되었음을 알 수 있다.
정리하자면 프로세스 커널 오브젝트는 프로세스 실행 중에는 Non-signaled 상태에 놓이며,
그러다가 프로세스가 종료될 때 Windows 운영체제에 의해 자동적으로 Signaled 상태가 된다.
그러나 종료될 때 Signaled가 되었다고 했는데, 그 반대의 경우는 어떠할까?
물론 불가능하다. 종료된 그 프로세스를 다시 살릴수가 없기 때문이다.
즉 일단 Signaled 가 되면 절대 다시 Non-Signaled 상태로 변경되지 않는다.

 

 

-- 커널 오브젝트의 두가지 상태를 확인하는 용도의 함수

커널 오브젝트가 상태를 지니도록 Windows 운영체제가 디자인 된 것은 프로그래머에게 다양한 기능을 제공하기 위함이다.
다양한 상황을 알리기 위함인데, 커널 오브젝트의 상태 정보가 우리에게 주는 이점이 있다. 이는 나중에 살펴보고, 우선
커널 오브젝트의 상태 정보를 확인하는데 사용되는 대표적인 함수 하나를 보자.
다음 함수는 핸들을 인자로 전달해서 커널 오브젝트의 상태를 확인하는데 사용되는 함수이다.

DWORD WaitForSingleObject(
 HANDLE hHandle;
 DWORD dwMilliseconds
);
// 실패시 WAIT_FAILED 가 리턴된다.

1) 상태 확인을 원하는 커널 오브젝트의 핸들을 인자로 젇날한다.
2) 이 함수는 인자로 전달된 hHandle 이 가리키는 커널 오브젝트가 Signaled 상태가 되엇을 때 반환한다.
그래서 함수의 이름이 Wait~ 으로 시작되며, 커널 오브젝트가 Signaled 상태가 될 때 까지 기다린다.
두번 째 인자는 커널 오브젝트가 Signaled 상태가 될 때까지 기다릴 수 있는 최대 시간을 밀리 세컨드 단위로 지정하는 용도로 사용된다.
즉 타임아웃을 설정하는 인자이며, INFINITE 를 인자로 전달시 Signaled 상태가 될 때까지 반환하지 않고 무한정 기다린다.

== 이 함수가 반환하는 상황은 다양하므로 함수 호출이 완료된 후 반환값을 꼭 확인해야 한다.

VALUE  의미
WAIT_OBJECT_0 커널 오브젝트가 Signaled 상태가 되었을 때 반환하는 값
WAIT_TIMEOUT 커널 오브젝트가 Signaled 상태가 되지 않고, 설정된 시간이 다 된 경우 반환되는 값
WAIT_ABANDONED 소유 관계와 관련하여 함수가 정상적이지 못한 오류 발생에 의해 반환하는 경우 반환되는 값


다음 함수는 상태를 확인하고자 하는 커널 오브젝트가 둘 이상이고, 핸들이 배열로 묶여 있다면 다음 함수를 활용하는 것이 편리하다

DWORD WINAPI WaitForMultipleObjects(
  __in          DWORD nCount,
  __in          const HANDLE* lpHandles,
  __in          BOOL bWaitAll,
  __in          DWORD dwMilliseconds
);

1) 배열에 저장되어 잇는 핸들 개수를 전달한다
2) 핸들을 저장하고 있는 배열의 주소 정보를 전달한다.
이 주소값을 시작으로 총 nCount 개의 핸들이 관찰 대상이 된다.
3) 관찰 대상이 모두  Signaled 상태가 되기를 기다리고자 하는지, 아니면 하나라도
Signaled 상태가 되면 반환할 것인지를 결정한다.

 


메일슬롯과 IPC에 대한 고찰

메일슬롯은 한쪽 방향으로만 메시지를 전달할 수 있으므로 채팅 프로그램 제작에는 한계가 있다.
따라서 두 프로세스가 서로 메시지를 받을 수 있는 채팅 프로그램을 구현하기 위해서는
두개의 메일슬롯을 생성해야만 한다.

이때 양쪽 방향으로 메시지를 주고 받기 위해 사용 가능한 IPC 사용 기법이 있다.
그것은 바로, 파이프를 이용한 IPC인데
파이프에는 Anonymous 파이프와 Named 파이프가 있다.
이 중 Named 파이프는 기본적으로 양방향 데이터 송 수신을 지원하며, 채팅 프로그램 구현에 적합하다

메일슬롯은 브로드 캐스팅 방식의 통신을 지원한다.
즉 하나의 sender는 한번의 메시지 전송으로 여러 receiver에게 동일한 메시지를 동시에 전송하는 것이 가능하다.
예를 들어 동일한 네트워크로 연결되어 있는 서로 다른 컴퓨터에서
실행중인 receiver 프로세스 A,B,C가 존재할 때,
이 Receiver 들은 각각 다음과 같이 동일한 메일슬롯 주소를 기반으로 메일슬롯 오브젝트를 생성하였다.

" \\.\mailslot\mailbox"

그리고 동일한 네트워크상에 존재하는 sender 프로세스 D가 데이터를 전송하기 위해 사용하는
주소의 구성은 다음과 같다
" \\*\mailslot\mailbox"

컴퓨터 이름이 와야 하는 부분에 * 표시가 있다. 이는 모든 컴퓨터를 지칭하는 것이며,
\\.\mailslot\mailbox 을 주소로 생성된 모든 메일슬롯에 동일한 메시지가 전달된다.


메일슬롯은 생성과 동시에 Usage Count 가 1인데, 참조하는 프로세스는 메일슬롯을 생성한
프로세스 하나이기 때문이다. 자식 프로세스의 경우에는 생성과 동시에
참조하는 프로세스가 둘이 되기 때문에 2가 되는 것이다.
메일슬롯뿐만 아니라, 프로세스와 쓰레드를 제외한 다른 모든 커널 오브젝트는 생성과 동시에
Usage Count는 1이 된다. 윈도우즈는 내부적으로 생성하는 대부분의 리소스들에 대해 커널 오브젝트를 생성한다.


--------------------mail receiver-----------------------

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define SLOT_NAME  _T("\\\\.\\mailslot\\mailbox")

int _tmain(int argc, TCHAR* argv[])
{
 HANDLE hMailSlot;  //mailSlot 핸들 생성
 TCHAR messageBox[50];
 DWORD bytesRead; //number of bytes read
 
 /// mailslot 생성
 hMailSlot = CreateMailslot(
  SLOT_NAME,  0,  MAILSLOT_WAIT_FOREVER,   NULL);
 if(hMailSlot == INVALID_HANDLE_VALUE)
 {
  _fputts( _T("Unable to create mailslot"), stdout);
  return 1; //오류 종료시
 }

 //메세지 수신
 _fputts(_T("------------- Message -------------\n"),stdout);
 
 while(1)
 {
  if(  !ReadFile(hMailSlot,  messageBox,   sizeof(TCHAR) * 50, &bytesRead,  NULL))
  {
   _fputts(_T("unable to read!"), stdout);
   return 1;
  }

  if(   !_tcsncmp(messageBox,  _T("exit"),   4))
  {
   _fputts(_T("good bye man!"), stdout);
   break;
  }

  messageBox[bytesRead / sizeof(TCHAR)]=0;  //NULL 문자 삽입
  _fputts(messageBox,stdout);

 }


 CloseHandle(hMailSlot);

 return 0;
}

 


--------------------mail sender-----------------------

 

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>

#define SLOT_NAME  _T("\\\\.\\mailslot\\mailbox")

 

int _tmain(int argc, TCHAR* argv[])
{
 HANDLE hMailSlot;
 TCHAR message[50];
 DWORD bytesWritten;
 //첫번째,두번째, 다섯번째만 확실히 이해할 것.
 hMailSlot=CreateFile(SLOT_NAME,  GENERIC_WRITE,   FILE_SHARE_READ,   NULL,
  OPEN_EXISTING ,  FILE_ATTRIBUTE_NORMAL,  NULL);
 //파일의 개방모드를 지정하는데, 이때 전송자이므로 쓰기모드에 해당하는 GNERIC_WRITE를 쓴다.
 //다섯번째 인자는 파일의 생성방식을 결정짓는 용도로 사용되며, 즉 새로운 파일을 생성할지, 기존 파일을 열어서 접근할지 결정한다
 //여기서는 리시버가 만들어놓은 메일슬롯에 접근하는 것이 목적이므로 OPEN_EXISTING을 전달하며, 이는 기존에 만들어진 파일을 개방할 때 사용한다.


 if(hMailSlot == INVALID_HANDLE_VALUE)
 {
  _fputts(_T("error! unable to create mailslot\n"),stdout);
  return 1;
 }

 while(1)
 {
  _fputts(_T("My CMD ==> "), stdout);
  _fgetts( message,  sizeof(message)/sizeof(TCHAR), stdin);
  
  if(  !WriteFile(hMailSlot,  message,  _tcslen(message)*sizeof(TCHAR), &bytesWritten, NULL))
  {
   _fputts(_T("Unable to Write"), stdout);
   CloseHandle(hMailSlot);
   return 1;
  } 
  
  if(   !_tcscmp(message,  _T("exit")))
  {
   _fputts(_T("Good bye! man! "), stdout);
   break;
  }
 
 
 }
 CloseHandle(hMailSlot);

 return 0;
}


IPC란 Inter - Process - Communication 의 약자로서
프로세스 사이의 통신 이라는 뜻을 가진다.
통신이란 기본적으로 데이터를 주고 받는 행위이며,
따라서 프로세스들이 서로 통신을 한다는 것은 둘 이상의
프로세스가 데이터를 주고 받는 행위라고 정의할 수 있다.


IPC의 종류

1. 메일슬롯 원리 (Mail Slot)
메일슬롯은 파이프와 더불어 대표적인 IPC 기법이다.
메일슬롯은 쉽게 우체통이라 생각하면 되며,
데이터를 주고 받기 위해 프로세스가 우체통을 마련하는 것이다
즉 Receiver 프로세스는 우체통을 하나 마련하고
Sender 프로세스가 데이터를 Receiver 우체통으로 보낸다.
그러면 Receiver가 데이터를 받을 수 있게 된다.

 

메일슬롯 구성을 위해 필요한 요소

1. Receiver 의 조건 사항
Receiver에 해당하는 프로세스는 우체통을 생성해야 하며,
이를 생성하는 함수는 CreateMailslot() 함수이다.

HANDLE WINAPI CreateMailslot(
  __in          LPCTSTR lpName,
  __in          DWORD nMaxMessageSize,
  __in          DWORD lReadTimeout,
  __in_opt      LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

- lpName   생성하는 메일슬롯의 이름을 결정하는데 사용한다.
즉 주소를 지정하는 것이다. 기본 형식은 다음과 같다.

\\ Computername \ mailslot \ [path]name


- nMaxMessageSize  메일슬롯의 버퍼 크기를 지정하는데 사용한다,
즉 우체통의 크기를 지정하는데 사용하면 0일 경우 시스템이 허용하는 최대크기로 설정한다.

lReadTimeout  메일 슬롯을 통해 전송된 데이터를 읽기 위해 ReadFile함수가 사용되며,
메일슬롯에 데이터가 있으면 데이터를 읽으면서 ReadFile 함수를 빠져나오게 된다.

그러나 메일슬롯이 비어있다면 데이터가 채워질때까지 블로킹 상태에 놓이게 된다.
이 인자는 최대 블로킹 시간을 밀리세컨드 단위로 지정하며, 0을 인자로 전달하면
메일슬롯에 데이터가 잇든지 없든지 블로킹 상태없이 빠져 나와 다음 단계를 실행한다.

그리고 상수 MAILSLOT_WAIT_FOREVER 를 인자로 전달할 경우 ReadFile 함수는
읽어 들일 데이터가 존재하게 될 때까지 블로킹 상태에 놓이게 된다.

lpSecurityAttributes 핸들을 상속하기 위한 용도이다.

함수의 반환 타입은 커널 오브젝트의 핸들이며, 메일슬롯도
커널에 의해서 관리되는 리소스이므로, 커널 오브젝트가 더불어 생성되고,
이 커널 오브젝트의 핸들이 반환되는 것이다.

 

둘째 Sender의 조건사항

Sender는 Receiver 가 만들어 놓은 메일슬롯의 이름 즉 주소를 알아야 하며,
다음은 실제 예이다.

//통로 마련 작업, 해당 주소의 메일슬롯을 개방한다. 즉 데이터 스트림을 형성한다.
HANDLE hMailSlot;
hMailSlot = CreateFile ( "\\\\.\\.mailslot\\mailbox", ~~);

 

//데이터 전송
CHAR Message[50];
WriteFile(hMailSlot, Message, ~~);

 

---- 메일슬롯 분석

\\ Computername \ mailslot \ [path]name

실제 채워 넣어야 할 부분은 컴퓨터이름과 pathname 이다.
메일슬롯은 동일한 네트워크 도메인에 존재하는 호스트들
사이의 데이터 전달을 목적으로도 사용할 수 있다.
위 샘플에서 컴퓨터네임 부분에 . 을 넣은 것은 로컬 컴퓨터를 의미한다.

path 네임은 실질적인 메일슬롯 이름이며, 위 샘플은 mailbox 이다.
그래서 다음과 같은 형태의 메일 슬롯 이름이 구성되었다.

\\. \mailslot\mailbox

path name은 path 정보를 포함하여 계층 구조의 형태로 보다 체계화가 가능하다.
즉 다음처럼 구성할 수 있다.

\\.\mailslot\abc\def\ mailbox

메일슬롯에 데이터를 전송하기 위해 해당 메일슬롯의 연결을 의미하는
데이터 스트림이 필요한데, 이는 CreateMailslot 함수의 호출을 통해 생성되는 메일
슬롯과는 또다른 형태의 리소스로서 이 역시도 운영체제의 의해
커널 오브젝트와 핸들의 생성을 동반한다.

'Windows > System Programming' 카테고리의 다른 글

커널 오브젝트의 두 가지 상태  (0) 2011.08.12
메일슬롯과 IPC 에 대한 고찰  (0) 2011.08.12
메일슬롯 (MailSlot) 의 예  (0) 2011.08.12
커널 오브젝트와 Usage Count 란?  (0) 2011.08.12
다양한 문자셋  (0) 2011.07.16


 Usage Count 란?

 자식 프로세스의 종료코드는 커널 오브젝트에 저장된다.
 만약 자식 프로세스가 종료될 시 커널 오브젝트도 동시에 소멸되면
 부모 프로세스는 종료코드를 얻을 수 없게 된다.

 커널 오브젝트를 참조하는 대상이 하나도 없을 때 소멸하는 것이 가장 인상적이며
 이것이 윈도우스가 오브젝트 소멸시기를 결정하는 방식이다.

 즉 커널 오브젝트를 참조하는 프로세스가 하나라도 존재할 시
 커널 오브젝트는 소멸되지 않는다.
 윈도우 운영체제는 이것을 위해 Usage count(참조횟수) 를 관리하며,
 이것이 0이 될 시 커널 오브젝트를 소멸한다.
 또한 커널 오브젝트에 접근 가능한 핸들 개수가 증가 될때 마다 늘어난다.
 
 그러므로 부모 프로세스가 CreateProcess 함수 호출 과정에
 자식 프로세스의 핸들을 얻기 때문에 ( PROCESS_INFORMATION )
 자식 프로세스 생성이 완료되면 Usage Count 는 2가 된다.
 
 자식 프로세스는 GetCurrentProcess 함수를 통해
 언제든지 자신의 커널 오브젝트 참조를 위한 핸들을 얻을 수 있다.
 UsageCount 는 커널 오브젝트의 멤버로 존재한다.

 즉 CloseHandle() 함수는 핸들을 반환하면서 커널 오브젝트의 UsageCount를 하나 감소 시키는
 기능을 가지고 있으며, 또한 프로세스 및 쓰레드도 프로세스가 종료되는 시점에
 Usage Count가 하나 감소한다.

 BOOL WINAPI GetExitCodeProcess( //종료코드를 얻는다.
  __in          HANDLE hProcess,
  __out         LPDWORD lpExitCode
 );

 
 // TerminateProcess() //악성코드 제거를 위해서만 사용할 것.
 //입출력도중 사용시 굉장한 오류가 발생!! 사용자제할 것.

 BOOL WINAPI TerminateProcess(
  __in          HANDLE hProcess,
  __in          UINT uExitCode
 );

CD란?

은행도 돈을 빌린다. 은행은 예끔만 받아서 대출을 하지는 않는데,
대출해줄 돈이 모자라면 돈을 빌려서라도 대출을 해준다.
그런데 은행이 돈을 빌리기 위해 발행하는 증서도 채권이라 해야 할까?
아니다. 은행은 이 것을 예금인 것처럼 양도성예금증서 ( CD : Certificate of Deposit ) 이라 부르며
이 것이 은행이 발행하는 채권이다.
그런데 은행은 CD 로 돈을 빌리면서 장기로는 안 빌린다. 3개월, 6개월 하는 식으로 만기를
정해서 빌리며, CD는 대표적인 머니마켓의 단기 금융 상품인 것이다.

은행은 CD 를 발행해서 돈을 빌리고, 이를 일반 사람들에게 주택담보대출을 해준다.
그러니 자연스럽게 CD 금리보다 높게 주택담보대출 이자를 받아야 은행도 먹고 살 수가 있다.
따라서 CD 금리가 올라가면 어쩔 수 없이 주택담보대출 금리도 올라가는 것이다.



CP란?

회사는 회사채를 발행한다. 하지만 회사채는 장기다.
잠시 자금이 부족해 돈을 빌리고자 할 때는 굳이 회사채를 발생하지 않는다.
쓸데없이 이자만 2~3년간 계속 나가지 않겠는가?
그래서 3개월 , 6개월 이런 식으로 짧게 돈을 빌리기 위해 또 다른 증서를 발행한다.
그런데 이 증서까지 회사채 라 부르면 사람들이 혼란스러워 하므로
기업어음 ( CP : Commercial Paper ) 이라 부른다.
금융 용어사전을 찾아보면 이런 방식의 설명은 없다. 왜냐하면 용어를 괜히 어렵게 표현하기 때문이다.



RP란?

RP (Repurchase Agreements) 라 부르며, 환매조건부채권을 말한다.
단기 금융 상품의 대표 주자이며, 환매조건이란 쉽게 말해, 금융회사가 고객에게 채권을 팔면서
'다시 사겠다' 라는 조건을 붙여 놓은 것이다. 왜 이런 조건을 붙여놨냐면,
우량한 회사가 발행한 채권이나 안전한 국공채의 경우 이자도 짭잘하고 위험도 상대적으로 적어
좋은 투자수단이라 할 수 있지만, 만기가 3~5년이니 하는 식으로 길다 보니 투자자가 선뜻 투자하기는
꺼려지는게 사실이다. 지금은 여윳돈이 있어 투자 했지만, 갑작스럽게 현금이 필요할 경우 낭패이기 때문이다.
이런 문제를 해결하기 위해 증권사에서 만기가 긴 채권을 먼저 매입한 후
환매조건을 붙여서 투자자 (고객) 들에게 파는 것이다.
즉 1~ 3개월 후에 다시 사주겠다는 조건을 붙여서 실제 채권의 만기는 3~5년이지만  증권사가 다시 사준다고
약속했으니 투자자 입장에선 1~3개월로 만기가 줄어든 셈이다.
이렇든 채권 투자를 활성화하기 위해 환매조건을 붙여 장기채권의 만기를 줄여 단기상품으로 만든 것이
환매조건부채권 이다.
물론 증권사는 책임지고 다시 사주는 조건을 붙인 대가로 투자자에게 원래 채권의 이자보다는
조금 적은 이자를 지급하고 그 차액을 먹는다.
물론 RP 가 예금자보호법에 따라 원금이 보장되는 금융 상품은 아니지만, 대상이 국공채 위주의 채권이다 보니
망할 염려가 없으며, 안전하다고 할 수 있다.

'Investment > Fund' 카테고리의 다른 글

채권이란 무엇일까?  (0) 2011.07.24
환매란 무엇일까?  (0) 2011.07.18
펀드란 무엇인가? ( 용어편 )  (0) 2011.07.18

각 경제 주체는 자금을 조달하기 위해 두 가지 방법을 사용한다.
은행에 가서 대출을 받는 것과 직접금융시장에서 자금을 조달하는 것이다.
전자의 경우를 간접금융 이라 하며, 후자의 경우 직접금융 이라 한다.
펀드는 아무래도 직접금융과 관련이 깊다.

직접금융은 자금을 조달하기 위해 자금을 조달하는 쪽 ( 돈을 빌리는 쪽 ) 에서는
인쇄된 증서를 발행해서 돈을 빌려주는 쪽에게 건네주도록 하고 있다.
여기서 회사가 장기로 돈을 빌리면서 발행하는 증서를 바로 회사채 라 한다.
일반적으로 3년 정도의 기간으로 빌리며, 정부 등이 발행할 경우 국공채 라고 한다.
이렇게 장기 금융시장에서 거래되는 대표적인 금융 상품인 채권은 회사나
정부가 돈을 빌리기 위해 발행한 증서다.

그럼 은행이 돈을 빌려주는 것과 채권을 발행하는 것은 뭐가 다를까?
은행은 대출약정서를 쓰고 돈을 빌려준다. 그런데 은행이 제 3자로부터 돈을 받고
이 약정서를 팔 수 없다. 하지만 채권은 다르다. 돈을 빌려주고 채권을 받은 사람은
이를 제 3자에게 다시 팔 수 가 있으며, 이는 국가에서 팔 수 있도록 허락한 것이다.
왜냐면 경제 주체들이 좀 더 돈을 쉽게 빌리게 하기 위해서인데,
은행도 한계가 있으므로 무조건 은행에서 돈을 빌릴 수는 없다. 그래서  직접금융시장으로 달려가
사람들에게 돈을 빌리려고 하니 문제가 있다. 현재는 돈이 있는 사람임에도 적지 않은 돈을
남에게 선뜻 빌려주기가 꺼려진다는 것이다.
"내가 지금 10억원이 있는데 당신이 신용도도 좋고 해서 빌려줄 수는 있는데,
중간에 내가 돈을 필요할 때 도로 돌려 받기가 힘드니까 좀 그러네요.."
이런 일은 충분히 있을 수 있는 일이다.
그래서 정부가 이왕 발행한 증서를 쉽게 사고팔 수 있도록 해준 것이다.
그럼 급한 사정이 생기면 제 3자에게 채권을 팔아 돈을 마련하면 되니까 말이다.
이렇듯 제 3자에게 팔 수 있는 것을 양도성이 있다고 하며,
채권과 일반 대출의 가장 큰 차이점은 바로 이 양도성에 있다.

'Investment > Fund' 카테고리의 다른 글

CD 와 CP, RP 란 무엇일까?  (0) 2011.07.24
환매란 무엇일까?  (0) 2011.07.18
펀드란 무엇인가? ( 용어편 )  (0) 2011.07.18

+ Recent posts