유돌이

calendar

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

Notice

2009. 1. 2. 17:57 C/C++/MFC

MFC를 사용하여 웹 브라우저 만들기


1. Project 생성

  • Application Type : SDI
  • Application Name : myWebBr

2. 메뉴 추가

아래의 그림을 참조하면서 메뉴를 만들자.

1) 메인 메뉴

Caption

이동(&M)

2) 이동(&M)의 서브 메뉴

각 ID에 해당하는 함수는 View 클래스에서 만들자.

ID

Caption

Function

Prompt

ID_MOVE_NEXT

앞으로(&N)

OnMoveNext()

앞으로 이동한다.

ID_MOVE_BACK

뒤로(&B)

OnMoveBack()

뒤로 이동한다.

Separator

 

 

 

ID_MOVE_STOP

멈춤(&S)

OnMoveStop()

페이지 다운로드를 멈춘다.

ID_MOVE_RELOAD

다시읽기(&R)

OnMoveReload()

현재 페이지를 다시 읽는다.

Separator

 

 

 

ID_MOVE_HOME

홈페이지(&H)

OnMoveHome()

기본으로 설정된 홈페이지로 이동한다.

3. 툴바 추가

1) 이동 메뉴에서 [뒤로], [앞으로], [멈춤]에 해당 하는 툴바를 만들자.

2) 만들어진 툴바와 메뉴 아이템과 ID를 연결하자.

Menu ID

ToolBar

ID_MOVE_BACK

[뒤로]

ID_MOVE_NEXT

[앞으로]

ID_MOVE_STOP

[멈춤]

 

4. 다이알로그바 만들기

 

1) ToolBar는 일반적으로 단순한 아이콘형 버튼들로 이루어진 도구 모음이지만, 다이얼로그바는 보다 다양한 윈도우 컨트롤을 포함할 수 있는 도구모음이라 하겠다. 다이얼로그바는 AppWizard가 자동으로 생성해 주지는 않지만 VC++가 제공하는 Components를 사용하여 간단하게 만들 수 있다.

Project -> Add To Project -> Components and Controls

로 이동하면 VC++이 제공하는 Gallery폴더로 이동하는데 그중에서

Visual C++ Components -> Dialog Bar

를 선택하여 아래와 같이 설정한후 "OK"버튼을 만든다.

Dialog bar Name : My Dialog Bar
Member variable Name : m_wndAddressBar

2) 다이얼로그바를 아래와 같이 편집하자

만들어진 Dialog Bar의 ID는 자동으로 "CG_ID_VIEW_DLGBAR"로 만들어 진다.

Control

Caption

ID

Function

Static Text

주 소

IDC_STATIC

 

Combo Box

 

 

 

Button

이 동

IDC_GO_SITE

OnGoSize()

OnGoSize()함수는  MainFrame클래스에서 만든다. 그러나 다이얼로그바에서 추가한 컨트롤의 ID는 WizardBar에 나타나지 않는다. 따라서 다이얼로그바의 컨트롤에서 전달되는 WM_COMMAND를 처리하기 위해 ON_COMMAND()매크로를 직접 작성한다.

(1) 먼저 MainFrame.h에 매크로 함수임을 선언하고

  1. // CMainFrame.h
  2. class CMainFrame : public CFrameWnd
  3. {
  4. ...        
  5. protected:
  6.         CDialogBar m_wndAddressBar;
  7.         //{{AFX_MSG(CMainFrame)
  8.         afx_msg void OnGoSite();
  9.         afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  10.                 // NOTE - the ClassWizard will add and remove member functions here.
  11.                 //    DO NOT EDIT what you see in these blocks of generated code!
  12.         //}}AFX_MSG
  13.         DECLARE_MESSAGE_MAP()
  14. };

(2) 메시지맵에 컨트롤의 ID와 연결되는 함수명을 정의하고

  1. // CMainFrame.cpp
  2. BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  3.         ON_COMMAND(IDC_GO_SITE, OnGoSite)
  4.         ON_COMMAND_EX(CG_ID_VIEW_DLGBAR, OnBarCheck)
  5.         ON_UPDATE_COMMAND_UI(CG_ID_VIEW_DLGBAR, OnUpdateControlBarMenu)
  6.         //{{AFX_MSG_MAP(CMainFrame)
  7.                 // NOTE - the ClassWizard will add and remove mapping macros here.
  8.                 //    DO NOT EDIT what you see in these blocks of generated code !
  9.         ON_WM_CREATE()
  10.         //}}AFX_MSG_MAP
  11. END_MESSAGE_MAP()

(3) 실제 함수의 내용을 기술한다.

  1. #include "MyWebBrDoc.h"
  2. #include "MyWebBrView.h"
  3. void CMainFrame::OnGoSite()
  4. {
  5.         CComboBox *pCombo = (CComboBox*)(m_wndAddressBar.GetDlgItem(IDC_COMBO1));
  6.         if(pCombo)
  7.         {
  8.                 CString urlStr;
  9.                 pCombo->GetWindowText(urlStr);
  10.                 if(urlStr != "")
  11.                 {
  12.                         ((CMyWebBrView*)GetActiveView())->m_WebBr.Navigate(urlStr,NULL,NULL,NULL,NULL);
  13.                 }
  14.                 else
  15.                 {
  16.                         AfxMessageBox("input url\n");
  17.                 }
  18.         }
  19. }

5. 다이얼로그바의 토클 추가하기

메뉴를 추가하는 방법과 동일하게 ResourceView에서 작업하면된다.

  • 위치 : [보기]메뉴의 서브 메뉴로 추가 등록
  • ID : CG_ID_VIEW_DLGBAR
  • Caption : 주소 이동줄

DialogBar를 만들 때 사용된 ID를 그대로 사용하는 이유는 일반적으로 메뉴의 ID를 Toolbar가 그대로 사용하는 것과 동일하다. DialogBar에 주워진 자동처리 부분을 그대로 받아 사용하기 위해 메뉴의 ID를 DialogBar의 ID를 그대로 사용한다.

 

6. ActiveX 컨트롤 추가

웹문서를 보여주도록 프로그램은 매우 복잡한 프로그램으로, 컨트롤을 사용하지 않고는 거의 불가능하다고 볼 수 있다. 따라서 우리는 HTML문서를 보여주는 기능은 MS 익스플로에서 제공하는  ActiveX 컨트롤중 "웹브라우저 컨트롤"을 사용하겠다.

1) Microsoft웹브라우저 컨트롤 추가하기

Project -> Add To Project -> Components and Controls

로 이동하면 VC++이 제공하는 Gallery폴더로 이동하는데 그중에서

Resigtered ActiveX Controls -> Microsoft웹 브라우저

를 선택하여 아래와 같이 설정한후 "OK"버튼을 만든다.

그러면 ClassViewer에 "CWebBrowser2"  클래스가 추가된다.

7. Microsoft Control 제어하기

추가가된 컨트롤을 제어하기 위해 크게 두가지 작업을 해야 한다.

  • 웹 브라우저 컨트롤 생성하기
  • 웹 브라우저 컨트롤의 크기를 현재 View의 크기에 맞게 조정하기

1) 웹 브라우저 컨트롤 생성하기

웹 브라우저 컨트롤를 View클래스에서 사용하기 위해 헤더파일을 삽입하고 멤버 변수를 설정한다.

  1. #include "webbrowser2.h" //추가
  2. class CMyWebBrView : public CView
  3. {
  4. protected: // create from serialization only
  5.         CMyWebBrView();
  6.         DECLARE_DYNCREATE(CMyWebBrView)
  7. // Attributes
  8. public:
  9.         CMyWebBrDoc* GetDocument();
  10.         CWebBrowser2 m_WebBr; // 추가

2) ClassWizard 나 WizardBar에서 WM_CREATE와 WM_SIZE의 메시지 헨들러 함수를 만든다.

  1. int CMyWebBrView::OnCreate(LPCREATESTRUCT lpCreateStruct)
  2. {
  3.         if (CView::OnCreate(lpCreateStruct) == -1)
  4.                 return -1;
  5.         
  6.         // Source Append----------------------------------
  7.         CRect rc;
  8.         GetClientRect(rc);
  9.         if(m_WebBr.Create("Web Control", WS_CHILD|WS_VISIBLE,rc, this, 1010) == FALSE)
  10.         {
  11.                 AfxMessageBox("Fail \n");
  12.                 return -1;
  13.         }
  14.         m_WebBr.GoHome(); // 프로그램이 시작되면 자동으로 기본 페이지로 이동
  15.         // end Append--------------------------------------
  16.         return 0;
  17. }
  18. void CMyWebBrView::OnSize(UINT nType, int cx, int cy)
  19. {
  20.         CView::OnSize(nType, cx, cy);
  21.         
  22.         m_WebBr.MoveWindow(0,0,cx,cy); //추가(현재 View의 크기에 맞게 크기를 조정한다.
  23.         
  24. }

8. 메뉴 아이템 헨들러 추가하기

1) 메인메뉴-[이동]

이미 만들어진 각 메뉴에 해당하는 함수에 아래와 같은 내용으로 수정한다.

  1. void CMyWebBrView::OnMoveNext()
  2. {
  3.         m_WebBr.GoForward(); // 추가
  4.         
  5. }
  6. void CMyWebBrView::OnMoveBack()
  7. {
  8.         m_WebBr.GoBack(); // 추가
  9.         
  10. }
  11. void CMyWebBrView::OnMoveHome()
  12. {
  13.         m_WebBr.GoHome(); // 추가
  14.         
  15. }
  16. void CMyWebBrView::OnMoveStop()
  17. {
  18.         m_WebBr.Stop(); // 추가
  19.         
  20. }
  21. void CMyWebBrView::OnMoveRelaod()
  22. {
  23.         m_WebBr.Refresh(); // 추가
  24.         
  25. }

2) 업데이트 UI 핸들러 추가

다이얼로그바 중에 [앞으로][뒤로]에 해당 하는 화살표 도구의 활성화와 비활성화를 제어하도록 핸들러를 추가한다. 메뉴나 툴바의 버튼의 상태를 정하기 위해 아이템 ID에 연결된 업데이트 핸들러가 호출되는데 이를 "UPDATE_COMMAND_UI"라 한다.

(1) 먼저 ClassWizard에 ID_MOVE_BACK, ID_MOVE_NEXT에 해당하는 UPDATE_COMMAND_UI 메크로 함수를 작성한다.

(2) [뒤로][앞으로] 메뉴의 현재 상태를 저장하기 위한 변수를 선언하고, 위에서 만들어진 OnUpdateMoveBack(), OnUpdateMoveNext()함수의 내용을 수정한다.

  1. // CMyWebBrView.h
  2. class CMyWebBrView : public CView
  3. {
  4.  ...
  5. public:
  6.         BOOL m_bForward; // 추가
  7.         BOOL m_bBack; //---추가
  8.  ...
  9. };
  1. // CMyWebBrView.cpp
  2. CMyWebBrView::CMyWebBrView()
  3. {
  4.         m_bBack = FALSE;
  5.         m_bForward = FALSE;
  6. }
  1. void CMyWebBrView::OnUpdateMoveBack(CCmdUI* pCmdUI)
  2. {
  3.         pCmdUI->Enable(m_bBack);
  4.         
  5. }
  6. void CMyWebBrView::OnUpdateMoveNext(CCmdUI* pCmdUI)
  7. {
  8.         pCmdUI->Enable(m_bForward);
  9.         
  10. }

(3) ActiveX 컨트롤 이벤트

ActiveX 컨트롤은 컨트롤을 포함하고 있는 부모 윈도우에 자신의 정보를 넘겨주기 위해 이벤트를 발생시킨다. 부모 윈도우는 이 이벤트를 받기 위해 다음과 같은 매크로를 사용한다.

  1. // CMyWebBrView.h
  2. class CMyWebBrView : public CView
  3. {
  4. ...
  5.         DECLARE_MESSAGE_MAP()
  6.         DECLARE_EVENTSINK_MAP() // 추가
  7. };
  1. CMyWebBrView.cpp
  2. ...
  3. BEGIN_EVENTSINK_MAP(CMyWebBrView, CView)
  4.         ON_EVENT(CMyWebBrView, 1010,105, OnCommandStateChange, VTS_I4 VTS_BOOL)
  5. END_EVENTSINK_MAP()
  6. ...

(참고) ON_EVENT() 매크로

ON_EVENT(theClass, id, dispid, pfnHandler, vtsParams)

theClass

이벤트 맵이 정의된 클래스

id

이벤트를 보내는 ActiveX 컨트롤의 ID, 즉 웹 브라우저 컨트롤의 ID

dispid

받고자 하는 이벤트 ID로 컨트롤마다 정해져 있다.

ptnHandler

이벤트를 받을 때 호출되는 함수

vtsParams

이벤트 파라미터 타입

(4) 이벤트 핸들러 함수(OnCommandStateChange())

  1. // CMyWebBrView.h
  2. class CMyWebBrView : public CView
  3. {
  4. protected:
  5.         //{{AFX_MSG(CMyWebBrView)
  6.         afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  7.         afx_msg void OnSize(UINT nType, int cx, int cy);
  8.         afx_msg void OnMoveNext();
  9.         afx_msg void OnMoveBack();
  10.         afx_msg void OnMoveHome();
  11.         afx_msg void OnMoveStop();
  12.         afx_msg void OnMoveRelaod();
  13.         afx_msg void OnUpdateMoveBack(CCmdUI* pCmdUI);
  14.         afx_msg void OnUpdateMoveNext(CCmdUI* pCmdUI);
  15.         afx_msg void OnCommandStateChange(long Command, BOOL Enable); // 추가할 핸들러 함수
  16.         //}}AFX_MSG
  17.         DECLARE_MESSAGE_MAP()
  18.         DECLARE_EVENTSINK_MAP()
  1. // CMyWebBrView.cpp
  2. void CMyWebBrView::OnCommandStateChange(long Command, BOOL Enable)
  3. {
  4.         switch(Command)
  5.         {
  6.         case CSC_NAVIGATEFORWARD:
  7.                 m_bForward = Enable;
  8.                 break;
  9.         case CSC_NAVIGATEBACK:
  10.                 m_bBack = Enable;
  11.                 break;
  12.         }
  13. }

9. URL 입력창의 Enter 키 처리

주소 입력창에 주소를 입력한후 엔터키를 쳤을 경우 이동 버튼을 클릭한 것과 같은 효과를 내면된다. 즉, 엔터키를 쳤을 경우 OnGoSite()함수를 Call 하면 될 것이다.

CMainFrame에서 엔터키를 쳤다는 얘기는 윈도우 메시지가 "IDOK" 발생했다는 말과 동일하다. 그럼, "IDOK"가 발생하면 OnGoSite()함수를 Call 할 수 있도록 메크로만 작성해 주면된다.

  1. // CMainFrm.cpp
  2. BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  3.         ON_COMMAND(IDC_GO_SITE, OnGoSite)
  4.         ON_COMMAND(IDOK, OnGoSite)
  5.         ON_COMMAND_EX(CG_ID_VIEW_MYDIALOGBAR, OnBarCheck)
  6.         ON_UPDATE_COMMAND_UI(CG_ID_VIEW_MYDIALOGBAR, OnUpdateControlBarMenu)
  7.         //{{AFX_MSG_MAP(CMainFrame)
  8.         ON_WM_CREATE()
  9.         //}}AFX_MSG_MAP
  10. END_MESSAGE_MAP()

10. 파일 열기 메뉴 사용하기

VC++이 기본적으로 제공하는 OnFileOpen()함수를 Overriding(재정의)해서 사용하면된다. View Class에서 "ID_FILE_OPEN"을 사용하여 매크로 함수를 만들고 아래와 같이 소스를 입력하면 된다.

1) View Class에서 마우스 오른쪽 버튼을 클릭한후 "Add Windows Message hadler"를 선택한다.

2) Class or object to handle에서 "ID_FILE_OPEN"을 선택한후 Add Handler를 선택하여 함수를 생성한다.

3) 만들어진 함수에 아래의 코드를 추가한다.

  1. void CWebBrView::OnFileOpen()
  2. {
  3.         CString str="*.htm;*.html";
  4.         CFileDialog fileDlg(TRUE, NULL, str, OFN_HIDEREADONLY,str);
  5.         if(fileDlg.DoModal() == IDOK)
  6.         {
  7.                 m_WebBr.Navigate(fileDlg.GetPathName(),NULL,NULL,NULL,NULL);
  8.         }
  9.      

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

CWnd 클래스  (0) 2009.01.05
포인터 사용법  (0) 2009.01.05
[API]버튼만들기  (0) 2009.01.02
Invalidate() , OnDraw(CDC* pDC)  (0) 2009.01.02
MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
posted by 유돌이
2009. 1. 2. 17:55 C/C++/MFC
출처 왜목마을(臥木) | 체키럽
원문 http://blog.naver.com/pbk7318/9119084
#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="Class";

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
,LPSTR lpszCmdParam,int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst=hInstance;

WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=(WNDPROC)WndProc;
WndClass.lpszClassName=lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass);

hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);

while(GetMessage(&Message,0,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(iMessage) {
case WM_CREATE:
CreateWindow("button","버튼1",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,20,100,23,hWnd,(HMENU)0,g_hInst,NULL);
CreateWindow("button","버튼2",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,50,70,23,hWnd,(HMENU)1,g_hInst,NULL);
CreateWindow("button","버튼3",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,80,150,33,hWnd,(HMENU)2,g_hInst,NULL);
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 0:
MessageBox(hWnd,"첫번째 버튼을 누질렀습니다","Button",MB_OK);
break;
case 1:
MessageBox(hWnd,"두번째 버튼을 누질렀습니다","Button",MB_OK);
break;
case 2:
MessageBox(hWnd,"세번째 버튼을 누질렀습니다","Button",MB_OK);
break;
}

case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));

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

포인터 사용법  (0) 2009.01.05
MFC를 사용하여 웹 브라우저 만들기  (0) 2009.01.02
Invalidate() , OnDraw(CDC* pDC)  (0) 2009.01.02
MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
posted by 유돌이
2009. 1. 2. 17:53 C/C++/MFC

CView::OnDraw(CDC* pDC)

화면이나 프린트 DEVICE에 출력할 때 사용하는 함수입니다. pDC라는 CDC 즉 Device Context 클래스를 받아서 그 안에 화면 출력을 설정하면 바로 화면에 뿌려집니다. 다른 프로그램을 수행하다가 현 프로그램으로 돌아오거나 윈도 사이즈 크기를 변화시키면 화면에 현재 상태를 다시 보여 주어야 하는데 이 때 윈도에서는 OnDraw 함수를 호출합니다. 또한 프로그래머가 화면을 변화시키고 싶을 때 InValidateRect라는 함수를 실행시키면 OnDraw 함수가 실행됩니다.

.......................

......................

Invalidate()

전에 InvalidateRect라는 함수를 배웠을 것입니다. InvalidateRect란 화면의 특정 부분만 재표시하라는 함수이고 Invalidate 함수는 전체를 재표시하라는 함수입니다.안에 인자를 FALSE를 사용하면 화면의 배경색은 나두고 나머지 부분을 재출력하는 것이고 TRUE나 또는 Invalidate()함수를 사용하며 화면의 배경색부터 다시 출력하게 됩니다.

Invalidate(FALSE);//화면을 배경색은 그대로 나두고 재출력

Invalidate() 또는 Invalidate(TRUE);//화면을 배경색부터 재출력 

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

MFC를 사용하여 웹 브라우저 만들기  (0) 2009.01.02
[API]버튼만들기  (0) 2009.01.02
MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
API 기초??  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:16 C/C++/MFC

디바이스 컨텍스트(Device Context)

우리가 화면에 무언가를 출력하기 위해서는 반드시 윈도우즈 os로 부터 화면을 사용할수 있는 권한(디바이스 컨텍스트)를 얻어야함.(문자하나를 화면에 표시하려고 해도 얻어야만 화면출력이 가능)

디바이스 컨텍스트는 화면이나 프린터,플로터등 출력장치에 문자나 그림을 표시하기 위한 정보를 지닌 구조체이다. 윈도우즈에서 프로그램의 모든 출력요구는 디바이스 컨텍스트(DC)를 통해 이뤄진다.

 

GDI(Graphics Device Interface)

윈도우즈가 제공하는 GDI는 서로 다른 구조를 지닌 출력 장치에 대한 정보를 스스로 판단하고 분석하여 실제로 사용해야 할 드라이버를 로드한다. 응용프로그램과 여러 출력 장치간에 의사소통이 원활히 이루어지도록 통역관 역할을 하기때문에 출력장치마다 프로그램을 달리해야하는 프로그래머의 번거로운 작업을 대신해준다.

 

EX) 도화지 : DC   , 그림그리는 도구 : GDI

 

MFC의 디바이스 컨텍스트 관련 클래스

*DC는 무한정제공되는 것이 아니기 때문에 사용후에는 윈도우즈 os에 반납해야함

  DC클래스를 사용하면 DC를 사용후 윈도우즈에 반환해야하는 번거로운 절차가 없다.

 

1.CDC클래스

디바이스 컨텍스트에 대한 기초 클래스로 화면이나 프린터 출력에 관계된 대부분의 멤버함수를  포함한다.

2.CWindowDC

캡션바,메뉴바, 상태바 등 넌클라이언트 영역을 포함한 전체 윈도우를 표시하는 DC를 관리

3.CClientDC

캡션바,메뉴바,상태바 등을 제외한 클라이언트 영역만을 표시하는 DC를 관리

CClientDC의 생성자는 내부적으로 GetDC()함수를 호출하고 소멸자로 ReleaseDC()함수를 호출한다.

4.CPaintDC

WM_PAINT메시지가 발생했을 때 다시 그려져야 할 영역에 대한 DC를 관리하며 WM_PAINT의 메시지 핸들러인 OnPaint()함수에서 사용한다. 윈도우즈에서의 화면복원 기능을 수행하는

부분

내부적으로 BeginPaint()함수를 호출하고 소멸자는 EndPaint()함수를 호출한다.

5.CMetaFileDC

윈도우즈 메타파일(.WMF나 .EMF)에 대한 DC를 관리

메타파일:그래픽 이미지를 생성해낼수 있는 GDI명령들로 구성된 파일 ex)클립아트


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

[API]버튼만들기  (0) 2009.01.02
Invalidate() , OnDraw(CDC* pDC)  (0) 2009.01.02
윈도우즈와 메시지  (0) 2008.12.30
API 기초??  (0) 2008.12.30
const 사용법  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:14 C/C++/MFC

<윈도우 프로시저>

1. 윈도우 프로시저는 윈도우 클래스당 하나씩 배정된다
2. 같은 윈도우 클래스로 부터 만들어진 윈도우는 같은 윈도우 프로시저를 공유하며 동일한 동작을 한다

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
- 운영체제에 의해 호출되는 콜백함수이므로 사용자가 호출할 수 없다. SendMessage함수 등으로 간접 호출 가능
- 관심없는 메시지는 DefWindowProc으로 보내 디폴트 처리한다

3. 윈도우 프로시저는 재진입(ReEntrant)이 가능하다. 즉 WndProc 실행 중에 또 WndProc이 호출될 수 있다
- 재진입이 가능하므로 가급적이면 지역변수를 적게 사용하는 것이 좋다


<메시지 큐>

1. 큐 메시지 : 메시지 큐로 들어가는 메시지
- 주로 사용자 입력으로 생성되는 메시지
- WM_KEYDOWN, WM_LBUTTONDOWN, WM_PAINT, WM_TIMER, WM_QUIT 등
- 발생 직후 시스템 메시지 큐에 저장되어 스레드 메시지 큐로 보내지며 최종적으로 윈도우 프로시저에 의해 입력된 순서로 처리된다

2. 비큐 메시지 : 큐로 들어가지 않고 곧바고 윈도우 프로시저로 보내지는 메시지
- 윈도우에게 특정 사실을 알리거나 명령을 보내기 위해서 보내지는 메시지
- 신속하게 처리된다
- API함수 실행 중이나 또느 큐 메시지를 처리하는 중에 추가로 발생한다
- WM_CREATE, WM_SIZE, WM_MOVE, WM_DESTROY, WM_ACTIVATE

3. 운영체제는 하나의 시스템 메시지 큐를 관리하며 각 스레드 별로 하나씩 메시지 큐를 생성한다

4. 시스템 아이들(System Idle)
- 시스템 큐의 메시지를 하나씩 꺼내 어떤 스레드로 보낼 것인지 판단하여 스레드 메시지 큐로 보내고 시스템 큐에서 메시지를 지운다

5. 스레드 메시지 큐에 들어온 메시지는 메시지 루프에 의해 꺼내지고 해당 윈도우의 윈도우 프로시저로 보내져 처리되고 스레드 메시지 큐에서 삭제된다

6. 스레드가 생성될 때 시스템은 디폴트로 메시지 큐를 생성하지 않으며 스레드가 최초 GDI함수나 User함수를 호출할 때 메시지 큐를 만들어 준다


<메시지 루프>

typedef struct tagMSG{
 HWND hwnd;
 UINT message;
 WPARAM wParam;
 LPARAM lParam;
 DWORD time;  // 발생한 시간
 POINT pt;  // 발생시의 마우스 좌표
} MSG;  // 메시지 구조체

DWORD GetMessagePos(VOID);
LONG GetMessageTime(VOID);
- 마우스 트리플 클릭 등을 검출할 때 시간과 위치 정보가 중요하게 사용된다

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
- 스레드 큐에 대기중인 메시지를 꺼내 첫번째 인수로 전달된 MSG구조체에 복사하고 메시지를 큐에서 제거하고 TRUE를 리턴한다
- 가져온 메시지가 WM_QUIT이면 FALSE를 리턴한다
- hWnd에 윈도우 핸들을 주면 이 윈도우와 그 차일드로 보내지는 메시지만 가져오고 NULL이면 현재 스레드에 속한 모든 메시지를 가져온다
- 메시지 필터링을 하지 않을 경우 세네번째 인자는 0으로 한다
- 메시지 필터링은 프로그램 무한 대기 상태에 빠지거나 오작동 위험이 있으므로 사용하지 않는게 좋다

TranslateMessage함수
- 가상키 입력을 문자 입력(WM_CHAR)로 바꾸는 역할을 한다
- 가상키 입력이 아니면 아무 처리도 하지 않는다

DispatchMessage함수
- 메시지를 윈도우 프로시저로 보내 처리하도록 한다
- MSG 구조체의 hwnd를 보고 정확하게 목적 윈도우의 메시지 처리 함수로 메시지를 전달한다


<PeekMessage>

1. GetMessage함수가 스레드에서 메시지를 가져올 때 메시지가 없다면 메시지가 들어올 때까지 무한 대기한다
- 이 시간에 다른 프로세스가 CPU를 쓸 수 있도록 양보한다
- 멀티 태스킹의 핵심 함수이다
- GetMessage에서 놀고 있는 시간을 데드 타임이라고 한다
- GEtMessage함수는 메시지를 받기 전에는 리턴하지 않으므로 데드 타임을 사용할 수 없다

BOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg);
- 메시지 큐에서 메시지를 꺼내거나 검사하되 메시지가 없더라도 즉각 리턴한다
- 리턴값이 TRUE이면 메시지가 있다는 뜻이고 FALSE이면 메시지가 없다는 뜻이다
- wRemoveMsg 메시지가 있을 경우 큐에서 제거할 것이지 말것인지 지정(PM_REMOVE, PM_NOREMOVE)
- PM_NOREMOVE를 이용하여 살짝 엿보기만 할 수 있다

2. 메시지 펌프
- 다른 메시지를 처리하는 중에 큐에서 메시지를 꺼내는 코드
- PeekMessage를 이용한 메시지 펌프는 메시지 시스템을 이해하는데도 비중이 높은 주체이다


<아이들 타임>

1. 아이들 타임(Idle Time), 데드 타임(Dead Time)
- 응용 프로그램이 아무 것도 하지 않고 노는 시간
- 대부분 GetMessage함수에서 대기중이다

2. 아이들 타임 작업
- 아이들 타임에 할만한 작업은 툴바, 상태란 등의 상태 갱신, 사용하지 않는 자원 회수, 단순 장식을 위한 애니메이션
- 수시로 해야 하되 긴급하지 않고 속도를 희생하면서까지 정확할 필요 없는 작업을 한다

3. 아이들 타임을 찾는 방법
- 멀티 스레드나 타이머는 본질적이 아이들 타임이 아니다
- GetMessage함수를 경우 찾을 수 없고 PeekMessage함수를 써야 한다
- 아이들 타임 작업은 틈틈이 하되 꼭 필요할 때만 하도록 해야 한다

WaitMessage();
- 큐에 메시지가 들어올 때까지 대기하되 메시지가 없으면 다른 프로세스에게 실행 시간을 양보한다


<키 상태 조사하기>

WM_KEYDOWN
- 키가 눌러질 때 발생한다
- wParam을 읽어 어떤 키가 입력 되었는지 알 수 있다

SHORT GetKeyState(int nVirKey);
SHORT GetAsyncKeyState(int nKey);
- 키가 눌러져 있는지 떨어져 있는지 조사하는 함수
- 일반 키가 눌러졌을 경우 최상위 비트가 1, 그렇지 않으면 0. Caps Lock같은 토글키가 켜져 있으면 최하위 비트가 1, 아니면 0
- GetKeyState함수는 메시지가 발생했을 때 상황을 조사하고 GetAsyncKeyState함수는 메시지가 처리될 때 상황을 조사

BOOL GetKeyboardState(PBYTE lpKeyState);
- 모든 가상키의 상태를 한꺼번에 조사
- 256바이트의 배열을 넘겨주어야 한다

폴링(Polling)
- 입력 메시지를 받지 않고 자신이 원할 때 정보를 조사하는 방식
- 신호를 기다리는 것이 아니라 직접 조사한다


<트리플 클릭>
- 일정한 범위 내의 화면 좌표를 짧은 시간안에 연속으로 세번 누르는 동작


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

Invalidate() , OnDraw(CDC* pDC)  (0) 2009.01.02
MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
API 기초??  (0) 2008.12.30
const 사용법  (0) 2008.12.30
C프로그래밍 별모양~  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:13 C/C++/MFC

기본형식

 

#include <windows.h>

/*
 - Winmain()의 첫번째 매개변수는 인스턴스 핸들(instance handle)이라 한다.
 
 - 핸들은 애플리케이션이 무언가를 식별하기 위해 사용하는 단순한 숫자다.
 
 - Winmain()에 사용되는 handle은 프로그램을 유일하게 식별한다.
 
 - 때로는 instance handle은 다른 windows함수를 호출할때 인자로 사용되기도 한다.

 

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

===============================
 
 - 초창기 윈도우즈에서는 사용자가 같은 프로그램을 두 번 이상 실행하면 해당 프로그램의
   인스턴스가 여러 개 생성되었다. 한 프로그램의 모든 인스턴스는 프로그램 코드와 더불어, 메

뉴나 대화상자
   템플릿 리소스와 같은 read-only데이터를 공유하였다.
   프로그램 실행시 WinMain()의 두번째 매개변수인 hPrevInstance를 이용하면 이미 다른 인스턴

스가 실행중인지를
   판단할 수 있었다.만약 다른 인스턴스가 실행중이라면 이전 인스턴스의 일부 데이터를 자신의

데이터 영역(data area)으로
   가져옴으로써 일부 작업을 생략할 수 있었다.
   하지만 32비트 윈도우즈에서는 이러한 개념이 더이상 사용되지 않음으로써 WinMain()의 두 번

째 매개변수는
   항상NULL이다.

 

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

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

   - WinMain()의 세 번째 매개변수는 프로그램을 실행할 때 사용하는 명령행(command line)이다

.
     일부 윈도우즈 프로그램은 시작할 때이를 이용하여 특정 파일을 메모리에 로드하기도 한다.
  
 

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

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

   - WinMain()의 네 번째 매개변수는 프로그램이 처음에 어떤 형태로 표시(display)될지를 나타

낸다.
   이 값에 따라 윈도우는 정상(normal)적인 현태, 화면 전체를 가득 채우는 최대화(maximized)

된형태, 그리고 작업 표시줄에 최소화(minimized)된형태 중 하나로 시작한다.
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPerInstance,
       PSTR szCmdLine, int iCmdShow)
{
 /*
  첫번째 인자 - 윈도우 핸들
  두번째 인자 - 메시지 상자에 표시되는 문자열
  세번째 인자 - 메시지 상자의 캡션 바에 나타나는 문자열
  네번째 인자 - WINUSER.H에 정의된 MB_로 시작하는 상수값의 조합을 사용한다. 버튼의 종류결


                OR연산으로 상수값을 조합할수 있다.

  각문자열은 TEXT매크로가 감싸고 있다.
  보통 문자열은 TEXT매크로로 감쌀 필요는 없지만,
  나중에 유니코드 문자셋을 사용하도록 프로그램을 변환하려면 이렇게 하는 것이 좋다.
 */
 MessageBox (NULL, TEXT("Hello Windows!"), TEXT("HelloMsg"), 0);
 
 // 이 프로그램에서 MessageBox는 1을 리턴하지만 좀더 정확하게는 IDOK(WINUSER.H 에서 1로정

의)를 리턴한다.
 // MessageBox는 다른 값을 리턴할 수 있다.
 // ex) IDYES, IDNO, IDCANCLE, IDABORT, IDRETRY, IDIGNORE 등이 있다.

 return 0;
}

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

WPARAM과 LPARAM

 

WPARAM은 unsigned int 형이고, LPARAM은 unsigned long 형이다.

특징은, WPARAM는 주로 값들을 넘기는데 사용하고, LPARAM는 값들 뿐만 아니라 포인터를 넘겨줄 때 사용된다.

 

보통 window procedure 또는 callback function의 인자로, 먼저 나오는 wParam에 자주 쓰이는 정보를 기술하고, lParam에 추가 정보를 기술한다. 지금은 모두 32bit이므로 사용자가 정의하기 나름이다.

 

윈도우는 메시지 방식으로 프로그램이 진행된다. WPARAM나 LPARAM는 모두 MS에서 지정해 놓은 형식이다. 메시지를 보낼 때 추가로 부가적인 정보를 넣는 것이다. 구체적으로 어떤 정보인지는 아무도 모른다. 이것은 각 메시지마다 다르게 구성되어 있다는 것을 뜻한다.


WM_KEYDOWN을 사용한다면, MS에서 키다운 메시지에 대한 제어를 하고 싶을 땐 이것을 써라. 그리고 부가적인 정보에는 WPARAM와 LPARAM를 사용해라. WPARAM에는 Virtual Key정보를 주고 LPARAM에는 KeyData 정보를 넣을 것이니 구현은 사용자가 알아서 제어를 해라. 하는 것과 같다.

 

W : word

L : long

 

typedef UINT WPARAM;
typedef LONG LPARAM;

현재는 둘다 32bit 값을 갖는 데이터 타입이다. 그냥 unsigned int, long 이라 해도 상관은 없겠지만, 굳이 이름을 WPARAM, LPARAM이라 한 것은 예전 16bit OS 시절에 이름 붙인 것이 이어져 온 것이다. 당시는 WPARAM은 word 형 파라미터, LPARAM은 long 형 파라미터라는 뜻이어서, (여기서 word형은 2바이트, 참고로 dword(double word)형은 4바이트, byte형은 말그대로 1바이트) 그 때도 위처럼 typedef되었었다. 그런데 32bit OS로 오면서 int형이 4바이트가 되었다. 그런데 typedef은 그대로 두다보니 현재 WPARAM는 이름과는 다르게 4바이트를 갖는 형이되었다. 그러니까 지금은 WPARAM이나 LPARAM이나 4바이트를 갖는 데이터 타입이다. 참고로 windef.h를 보면 여러가지 데이터 타입이 typedef되어 있는 걸 볼 수 있다.

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


WNDCLASS 구조체

 

typedef struct tagWNDCLASS

{

        UINT style;

        WNDPROC lpfnWndProc;

        int cbClsExtra;

        int cbWndExtra

        HINSTANCE hInstace;

        HICON hIcon;

        HCURSOR hCursor;

        HBRUSH hbrBackground;

        LPCSTR lpszMenuNmae;

        LPCSTR lpsClassNmae;

}WNDCLASS;

 

style

   윈도우 클레스의 스타일 지정

   CreateWindow 함수에서 지정하는 개별 윈도우의 스타일과는 다름

 

lpfnWndProc

    메시지 처리 함수 지정

 

cbClsExtra

    윈도우 클레스에서 사용하고자 하는 여분의 메모리양을 바이트 수로 지정

    윈도우 클레스를 운영체제에 등록할때 메모리를 추가로 할당 받을 양 지정

 

cbWndExtra

     cbClsExtra 와 유사하되 개별 윈도우에서 사용하고자 하는 여분 메모리양 지정

     개별 윈도우가 만들어 질때 마다 메모리를 추가적으로 할당 받음

 

hInstance

    윈도우 클레스를 등록한 응용 프로그램의 인스턴스 핸들

    지정한 응용 프로그램이 윈도우 클레스의 소우주가 되며 소유주가 파괴 되면 윈도우도 같이

파괴 됨

 

hIcon

    윈도우가 최소화 되었을 때 보여줄 아이콘 지정

    NULL인 경우 아이콘이 그려야 할 때 WM_ICONERASEBKGND 메시지 발생

 

hCursor   

    클레스 커서 지정

    윈도우의 작업 영역에 마우스가 위치해 있을 때 사용될 커서 지정

    리소스에 커서를 추가한 후 LoadCursor 함수로 읽은 후 핸들러에 대입해주거나 시스템에 등

록되 어 있는 디폴트 커서중에서 하나를 대입해줌

    NULL로 지정될 경우 커서 정의가 되지 않으며 마우스 이동시마다 SetCursor함수를 호출하여

윈하는 커서로 변경해주어야 함

 

hbrBackground

    윈도우의 작업 영역에 칠할 배경 브러시 지정

    GetStockObject 나 그외 브러시를 만드는 함수를 사용하여 브러시 핸들을 얻은 후 대입

 

lpszMenuNmae

    윈도우가 사용할 메뉴를 지정

    CreateWindw 함수에서도 개별 윈도우를 만들때 메뉴를 지정하며 CreateWindw에서 별도의 메

뉴를 지정한 경우 이곳에서 지정한 메뉴는 무시됨

 

lpzsClass Name

    등록하고 자는 윈도우 클레스 이름을 나타내는 문자열

    윈도우 클레스 구분에 사용

    대 소문자 구분은 하지 안음


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

MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
const 사용법  (0) 2008.12.30
C프로그래밍 별모양~  (0) 2008.12.30
BHO 란?  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:12 C/C++/MFC
const에 대해 정리해봤습니다. 약간 불명확한 부분이 있을수도 있겠네요.

const는 변수, 포인터형, 참조형, 함수, 클레스 등에 붙을수 있음.

1. 변수
예)
    const i = 100;
i 값 변경불가

2. 포인터형 : 기본적으로 2가지 형태가 있을 수 있음. 그외 여려형태가 가능
예1) 값은 변경 불가능하지만 주소는 변경가능한 형태
    int temp = 100, temp2 = 200;
    const int *ipConst = &temp;  // *ipConst 값 변경 불가, ipConst(주소)값은 변경가능
    // int const *ipConst = &temp;  // 이런형태로 써도 위와 같은 의미

    // *ipConst = 300;   // 불가능한 형태
    ipConst = &temp2;  // 가능한 형태

예2) 주소는 변경 불가능하지만 값은 변경가능한 형태
    int temp = 100, temp2 = 200;
    int * const iConstp = &temp;  // *iConstp 값 변경 가능, iConstp(주소)값은 변경불가

    *iConstp = 300;   // 가능한 형태
    //iConstp = &temp2;  // 불가능한 형태

주의 : const가 결합되는 위치가 값인지 주소인지에 유의


3. 참조형
예1) 직접적으로 값과 주소 모두 변경 불가능하지만 참조 원본을 통한 값변경은 가능한 경우
   int temp3 = 100, temp5 = 200;
   int const &ircVal = temp3;  
  
   //ircVal = 2000; // 컴파일 에러 발생 (const 참조는 값 변경불가)
   //ircVal = temp5;  // 주소도  변경불가
   temp3 = 9000;  // 참조 원본은 변경가능, 결과적으로 ircVal의 값도 변하게됨
    

예2) 직접적으로 값과 주소 모두 변경 가능하지만 참조가 가르키는 값은 변화가 없는경우
    int temp4 = 300, temp5 = 500;
    int & const icrVal = temp4;

    icrVal = 6000;  // 값변경 가능, 하지만 값에 변경이 안됨  
    cout << " icrVal " << icrVal << endl;  // 여전히 300이 찍힘
    icrVal = temp5; // 주소도 변경가능 역시 값에 변경이 안됨
    cout << " icrVal " << icrVal << endl;  // 여전히 300이 찍힘

4. 함수 : class의 멤버함수인 경우만 const 함수 사용가능. 해당 class의 멤버변수를 변경할수 없음.
예)
class ConstTest
{
public:
        int m_iA;

        ConstTest()
        {        m_iA = 1;           }
        int const_func1( int &a_iA,  int &a_iB) const
        {
                int a = 1;
                int b = 2;
                int c = 0;

                c = a + b;
                a_iA += 100;  
                // m_iA += 100; // 에러발생. 멤버변수는 변경 불가  
                return m_iA;
        }
};
                

5. 클레스
예)
  const CMyConstClass CC;  
  
  // 내부 멤버변수 전체를 변경불가능한 클레스,(생성자 함수만은 예외)
  // 모든 내부 멤버 함수는 기본적으로 const 함수가 되야만함.
  // 내부 함수의 지역 변수및 인자로 받은 변수는 변경가능.

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

MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
API 기초??  (0) 2008.12.30
C프로그래밍 별모양~  (0) 2008.12.30
BHO 란?  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:11 C/C++/MFC

하드 코딩 ㅡㅡV

 

#include<stdio.h>

main()
{
 int num, over, star;

 for(over=1; over<=3; over++){
  for(num=9;num>=over; num--){
   printf(" ");
  }
  for(star=1; star<=over*2-1; star++){
   printf("*");
  }
  printf("\n");
 }
 for(over=0; over<=2; over++){
  for(star=19; star>=over*2; star--){
   printf("*");
  }
  printf("\n");
  for(num=0; num<=over; num++){
   printf(" ");
  }
 }
 for(over=3; over>=1; over--){
  for(star=19; star>over*2; star--){
   printf("*");
  }
  printf("\n");
  for(num=2; num<=over; num++){
   printf(" ");
  }
 }
 for(over=3; over>=1; over--){
  for(num=9;num>=over; num--){
   printf(" ");
  }
  for(star=1; star<=over*2-1; star++){
   printf("*");
  }
  printf("\n");
 }
}


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

MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
API 기초??  (0) 2008.12.30
const 사용법  (0) 2008.12.30
BHO 란?  (0) 2008.12.30
posted by 유돌이
2008. 12. 30. 23:10 C/C++/MFC

[BHO - Browser Helper Object]

 

인터넷 익스플로러를 손쉽게 제어할 수 있는 DLL 파일.

 

인터넷 익스플로러를 시작할 때마다 생겨나 메모리를 공유하고

브라우저 창과 모듈에 어떠한 지시라도 할 수 있는 객체이다.

 

이벤트를 감지하여 띄워진 페이지에 추가 정보를 보여주기 위해 브라우저 창을 만들고,

메시지와 작업을 감시할 수도 있다. 또한 다른 브라우저 침투에 의한 배너 광고 변경,

페이지 감시 및 보고, 홈 페이지 변경 등 스파이 기능도 있다.

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

MFC (디바이스 컨텍스트와 관련 클래스)  (0) 2008.12.30
윈도우즈와 메시지  (0) 2008.12.30
API 기초??  (0) 2008.12.30
const 사용법  (0) 2008.12.30
C프로그래밍 별모양~  (0) 2008.12.30
posted by 유돌이
2008. 12. 29. 23:51 델파이
이제 우리는 TBaseDShow의 마지막 함수인 다이렉트쇼 카테고리에서 원하는 장치를 불러오는 함수에 대하여 이야기를 할 것입니다.

[1] 카테고리에서 입력장치 불러오기 

여기서 우리는 한가지 인식하고 넘어가야 할 것이 있습니다. 그것도 구체적이며 비주얼하게 말이죠. 일단 GraphEdit 를 실행하여 '필터삽입윈도우'를 띄웁니다. 그곳에는 수많은 카테고리가 존재할 것입니다. 먼저 DirectShow Filters라는 카테고리를 클릭하여 아래로 쭉 풀어 놓습니다.
(이 필터삽입 윈도우의 크기가 고정되어 있는게 정말 불만이다. 앞으로 여러분은 필터를 만드실 경우가 있는데요, 만드신 필터를 이 GraphEdit를 사용하여 시뮬레이트를 하여야 합니다. 그런데 필터를 레지스트리에 등록해 놓고서, 이곳 GraphEdit의 DriectShow Fitler라는 카테고리에서 찾을 때마다 참 번거롭습니다. 보시다시피 등록되어 있는 필터들이 너무 많아서 좁은 창안에 한꺼번에 보이지 않을 뿐더러... 아무튼 이래저래 불편합니다. )


DirectShow Filters 카테고리 안에 놓여진 수많은 필터를 보시면 두개의  GUID가 나란히 놓여져 있는게 보이실 것입니다. 아마도 여러분의 눈에는 하나의 GUID(혹은 다른말로 CLSID)만 보이실 지도 모르겠습니다. 이것은 '필터삽입 윈도우'가 작아서 입니다. 수평스크롤바를 클릭하여 오른쪽으로 이동시키면 나머지 GUID도 보이실 것입니다. 이것의  한 예로 다음과 같습니다. 

  Video Renderer
    DisplayName : @device:sw:{083863F1-70DE-11D0-BD40-00A0C911CE86}\
                                                                                                {70E102B0-5556-11CE-97C0-00AA0055595A}
    FileName : C:\WIndows\system32\quartz.dll
    Merit : 00400000
    pin: 00 
    Version : 2


대충 위와같이 보이실 것입니다. 자, 위에서 보시다시피 하나의 필터에 두개의 CLSID가 보입니다. 이게 웬 하늘에 봉창 떨어질 일입니까. 하나의 COM Object에 두개의 CLSID가 있다니요. 하지만 당황하지 말고, 다른 필터들을 살펴보시기 바랍니다. 모두 두개의 CLSID가 있는데요, 앞의 CLSID는 하나같이 동일한 값이라고 확인 하실 수 있습니다. 자, 앞의 CLSID는 바로 카테고리의 CLSID를 의미하는 것입니다. 이것을 정리하면 아래와 같습니다.  
   
    DisplayName : @device:sw(종류):{카테고리 클래스 관리자의 CLSID}\{필터 CLSID 또는 ID} 

만일 우리가 특정한 카테고리에서 원하는 필터나 장치의 인터페이스를 가져오고 싶다면, 우리는 제일먼저 해당하는 카테고리의 CLSID를 알아야 할 것입니다. 그렇다면 여기서 우리는 한가지 의문이 들 것입니다. DirectShow Filters 카테고리 안에 있는 필터의 경우에는 별도로 카테고리 CLSID를 알지 못해도 CoCreateInstance를 사용하여 잘만 생성하여 사용하였는데, 왜 다른 카테고리에 있는 장치(혹은 광의의 필터)의 경우에는 반드시 카테고리 CLSID를 알아야 하는 것인가 하고... 

이 의문에 대한 해답은 이렇습니다. 우리가 DShow를 사용하기 위해서는 반드시 필터그래프 매니저를 필요로 합니다. 즉, 필터그래프가 필터들을 연결시키고, 스트림의 Run, Stop, Pause등과 같은 필수적인 제어를 하기 때문입니다. 그런데 필터그래프에 필터를 추가하기 위해서는 반드시 IBaseFilter 라는 인터페이스가 필요한데요, 이 인터페이스가 모든 COM 객체나 혹은 예전방식의 어떤 장치들(VFW 방식의 압축코덱이나  혹은 WDM 디바이스 드라이버)에 존재하는 것은 아니라는 사실입니다. 

예를 든다면 Video Compressors 카테고리에 있는 코덱들은 대부분 VFW의 구조를 가지고 있습니다. 이 구조는 COM 객체가 아니라 일종의 DLL 함수들만이 뿔뿔이 존재할 뿐입니다. 그런데 만약 여기서 IBaseFilter를 가져오지 못한다면 DShow는 예전방식의 모든 압축코덱이나 혹은 입출력 디바이스 드라이버를 사용하지 못할 것인데요, 이것은 말도 안돼는 것이겠죠.(MS가 세상물정에 어둡거나 혹은 볼랜드 같이 완전히 새로운 패러다임의 작품을 만들기를 시도한다면 모르겠지만... 쩝.)

자, 결론적으로 말하자면 DShow의 구조가 아닌 COM 객체나 혹은 예전방식의 모듈에서 IBaseFilter라는 인터페이스를 바인딩해주는 서비스가 별도로 필요하게 된 것인데요, 이 서비스를 받기 위해서 카테고리의 CLSID가 필요한 것이라고 생각하시면 되겠습니다. (이 서비스가 바로 IMoniker라는 일종의 COM의 인터페이스인데요, 이것은 비단 DShow에서만 사용하는 것은 아닙니다. 하기야 수많은 DShow의 인터페이스를 반드시 DShow에서만 사용하라는 법은 없는 것과 마찬가지일 것입니다. 우리가 TList를 데이터베이스와 같이 특정한 곳에만 사용해야 한다는 법칙이 없는것과 마찬가지 일 것입니다. )

여기까지 우리는 DirectShow Filters 카테고리 이외의 카테고리에서 원하는 장치(혹은 광의의 필터)를 얻기 위해서는 반드시 카테고리 CLSID를 알아야 하며, 그 이유는 DShow 방식의 구조가 아닌 객체나 모듈에서 IBaseFitler를 얻기위한 서비스에 반드시 필요한 요소이기 때문이라고 설명하였습니다. (제가 장치를 광의의 필터라고 하는 것은 바로 위에서 설명했던 것처럼 IBaseFilter라는 인터페이스를 얻을 수 있기 때문입니다.)

우리가 RS232시리얼 통신 프로그램을 만드는데 있어서 그 통신모듈을 직접 제작할 필요가 없이 잘 알려진 컴포넌트나 라이브러리를 사용하듯이, 델파이에서 DSPack를 가지고 DShow용 어플을 제작하면서 굳이 IMoniker서비스를 받기까지의 로직을 별도로 구현할 필요가 없다고 생각합니다. 하지만 정 궁금하시다면 DSPack에 있는 소스를 보시면  별로 어렵지 않다고 생각하실 것입니다. 아무튼 우리는 DSPack에서 미리 구현되어 있는 TSysDevEnum라는 클래스를 사용하여 아주 간단히 이러한 서비스를 받아 볼수가 있습니다.  그 전에 우선 주요한 카테고리의 선언을 보시면 다음과 같습니다.  

  CLSID_VideoInputDeviceCategory   //비디오 입력장치.
  CLSID_AudioInputDeviceCategory   //오디오 입력장치.
  CLSID_VideoCompressorCategory  //비디오 압축코덱
  CLSID_AudioRendererCategory      //오디오 랜더러.


위의 것들은 모두 DSPack의 DirectShow9.pas 파일에 정의되어 있습니다. 이곳에는 위의 4가지 말고도 모든 카테고리의 정의가 선언되어 있는데요, 이것을 필요할 때마다 찾기가 번거롭습니다. 그래서 차라리 TBaseDShow의 유닛에다가 몽땅 주석과 함께 옮겨 놓던지 하는게 좋습니다. 이상하게도 자주 까먹습니다. 

자, 이제 우리가 원하는 함수의 모습은 아래와 같습니다.  (저 같은 경우는 SysEnum 변수를 TBaseDShow의 멤버객체로서 정의해 놓고 사용해 왔는데요, 왜 그랬는지 기억이 가물가물 나지 않습니다. 현재 TSysDevEnum 의 내부 로직을 살펴보니까 로컬로 사용해도 무리는 없을 것 같아서 이해하기 쉽게 변경하였습니다.)

  function TBaseDShow.GetCamFilter: IBaseFilter;
  var
    SysEnum: TSysDevEnum;
  begin
    SysEnum := TSysDevEnum.Create;
    try
      SysEnum.SelectGUIDCategory(CLSID_VideoInputDeviceCategory);
      Result := SysEnum.GetBaseFilter(0)// 가장 첫번째 장치를 가져온다.
    finally
      SysEnum.Free;
    end;
  end;



위에서 가장 첫번째 장치를 가져온다는 부분에 유의하셔야 합니다. 사실 여러분은 장치의 이름으로도 검색하실 수가 있습니다. 예를 들어 압축코덱은 수많은 종류가 PC마다 순서의 기준이 없이 등록되어 있기 때문에 위와 같이 '몇번째 놓여있는 것을 가져와' 하는 것은 위험한 일입니다. 따라서 분명히 압축코덱과 같은 것들은 카테고리에서 이름으로 찾아야 할 것입니다.
그러나 카메라의 경우는 다릅니다. 압축 코덱의 경우와는 반대로 카메라의 경우에는 특정한 USB 드라이버의 이름에 맞게 DShow 어플이 제작되어서는 안되기 때문입니다. 그래서 어쩔 수 없이 카테고리의 순서에서 찾아서 장치를 가져옵니다. 혹시 여러분이 동영상 채팅같은 것을 할 경우에 클라이언트 프로그램을 실행시킴과 동시에 다음과 같은 문구를 보신적은 없으신지요. 

   '현재 컴퓨터에 비디오 입력 장치가 1개이상이 발견되었습니다. 몇번째 장치를 사용할 것인지를 선택하세요.'

위와 같이 선택 다이알로그 창이 뜨는 것을 보신적이 있으신지요. 이것들은 모두 위와같은 이유 때문인 것입니다. 
(장치가 1개 이상인 경우가 보통은 드물지만 DShow 개발자들에게서는 흔히 일어납니다. 예를 들어서 PCI 슬롯에  TV수신카드(혹은 BT878보드)나 엠팩보드가 동시에 꽂혀있고, USB에는 USB용 카메라까지 꽂혀 있으니까요. 그래서 예전에 저같은 경우는 PC를 살때 파워서플라이를 항상 일반인 용보다 월등히 성능 좋은 것을 사용해야 했던 기억이 납니다.) 

이제, TBaseDShow를 비로소 완성하였습니다. 이것의 풀소스를 아래와 같이 적겠습니다. 

unit cBaseDShow;
interface
uses
  Windows, SysUtils, Classes, Registry, DirectShow9, ActiveX, ExtCtrls, DsUtil;
 
type
  TBaseDShow = Class
  private
  public
    //필터그래프와 인터페이스...
    FilterGraph: IGraphBuilder;
    MediaControl: IMediaControl;
    VideoWindow: IVideoWindow;
 
    constructor Create;
    destructor Destroy; override;
    function GetCamFilter:IBaseFilter;
    function CreateFilterGraph(var Graph: IGraphBuilder): Boolean;              //필터그래프를 생성한다.
    function CreateFilter(const clsid:TGUID; var Filter:IBaseFilter): Boolean;  //각종 필터를 생성한다.
    function FindPinOnFilter(const Filter: IBaseFilter;                         //필터에서 내가 원하는 핀을 찾아주는 함수.
                             const PinDir: TPinDirection; var Pin: IPin): HRESULT;
  end;
 
implementation

 
{ TBaseDShow}

 
constructor TBaseDShow.Create;
begin
  inherited Create;
  CoInitialize(nil);                                                            //COM을 초기화한다.
  CreateFilterGraph(FilterGraph);                                               //필터그래프를 생성한다.
  FilterGraph.QueryInterface(IID_IMediaControl, MediaControl);                  //필터그래프의 인터페이스.
  FilterGraph.QueryInterface(IID_IVideoWindow, VideoWindow);                //필터그래프의 인터페이스.

end;

 
destructor TBaseDShow.Destroy;
begin
  if Assigned(MediaControl) then MediaControl.Stop;
  While Assigned(VideoWindow) do VideoWindow := nil;
  While Assigned(MediaControl) do MediaControl := nil;
  While Assigned(FilterGraph) do FilterGraph := nil;
  CoUninitialize;                                                               //COM을 셧다운시킨다.
  inherited Destroy;
end;

 
function TBaseDShow.CreateFilterGraph(var Graph: IGraphBuilder): Boolean;
var
  ID : Integer;
begin
  Result := False;
  if Failed(CoCreateInstance(CLSID_FilterGraph,
                              nil,
                              CLSCTX_INPROC_SERVER,
                              IID_IFilterGraph,
                              Graph))
  then Exit;
  //GraphEdit로 현재의 필터그래프 구성을 볼수 있게한다.
  AddGraphToRot(Graph,ID);
  Result := True;
end;

 
function TBaseDShow.CreateFilter(const clsid: TGUID; var Filter: IBaseFilter): Boolean;
begin
  Result := False;
  if Failed(CoCreateInstance(clsid,
                             NIL,
                             CLSCTX_INPROC_SERVER,
                             IID_IBaseFilter,
                             Filter))
  then Exit;
  Result := True;
end;

 
function TBaseDShow.FindPinOnFilter(const Filter: IBaseFilter;
  const PinDir: TPinDirection; var Pin: IPin): HRESULT;
var
  IsConnected : Boolean;
  hr: DWORD;
  EnumPin: IEnumPins;
  ConnectedPin: IPin;
  PinDirection: TPinDirection;
begin
  Result := S_False;
  if not Assigned(Filter) then exit;
  hr := Filter.EnumPins(EnumPin);
  if(SUCCEEDED(hr)) then
  begin
   while (S_OK = EnumPin.Next(1, Pin, nil)) do
    begin
      //핀이 연결되었는지 조사.
      hr := Pin.ConnectedTo(ConnectedPin);
      if hr = S_OK then
      begin
        IsConnected  := True;
        ConnectedPin := nil;
      end else IsConnected := False;
      //핀의 방향을 검사
      hr := Pin.QueryDirection(PinDirection);
      //매개변수의 핀방향과 동일하고 현재 연결된 상태가 아니라면 루프에서 탈출.
      if (hr = S_OK) and (PinDirection = PinDir) and (not IsConnected) then break;
      pin := nil;
    end;
    Result := S_OK;
  end;
  EnumPin := nil;
end;

function TBaseDShow.GetCamFilter: IBaseFilter;
var
  SysEnum: TSysDevEnum;
begin
  SysEnum := TSysDevEnum.Create;
  try
    SysEnum.SelectGUIDCategory(CLSID_VideoInputDeviceCategory);
    Result := SysEnum.GetBaseFilter(0)// 가장 첫번째 장치를 가져온다.
  finally
    SysEnum.Free;
  end;
end;
end. 
 

출처 : 델마당  dong님의 글(dongsoft)
posted by 유돌이