HDC hdc;
HWND hWndMain;
HWND hVfw;
BITMAPINFO Bm;
LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR);

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  static PAINTSTRUCT ps;

  switch(iMessage)
  {
  case WM_CREATE:
    hWndMain = hWnd;
    hVfw = capCreateCaptureWindow(TEXT("동영상 캡처"), WS_CHILD|WS_VISIBLE,0,0,400,300,hWnd,0);
    capDriverConnect(hVfw,0);
    capGetVideoFormat(hVfw, &Bm, sizeof(Bm));
    capSetCallbackOnFrame(hVfw, FramInfo);
    capPreviewRate(hVfw, 33);           // 프레임 설정
    capPreview(hVfw, FALSE);            // FrmaInfo 콜백함수 사용하여 출력해주기위해 FALSE
    return 0;

  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT CALLBACK FramInfo(HWND hWnd, LPVIDEOHDR lpData)
// 캡쳐된화면을 윈도우창에 뿌려주는 함수
  static int iCntX;
  static int iCntY;
  static int iJump;
  
  hdc = GetDC(hWndMain);

  StretchDIBits(hdc, 00, Bm.bmiHeader.biWidth, Bm.bmiHeader.biHeight,
          00, Bm.bmiHeader.biWidth, Bm.bmiHeader.biHeight, lpData->lpData,
          &Bm, DIB_RGB_COLORS, SRCCOPY); 

  iJump = 0;
  for(iCntY=0; Bm.bmiHeader.biHeight>iCntY; ++iCntY)
  {  // RGB값을 찾아서 변환해 주는 곳
    for(iCntX=0; Bm.bmiHeader.biWidth>iCntX; ++iCntX, iJump+=3)
    {
      if(lpData->lpData[iJump+2]>230)    // Red
      {
        if(lpData->lpData[iJump+1]>230)    // Green
        {
          if(lpData->lpData[iJump+0]>230)    // Blue
          {
            lpData->lpData[iJump] = 0;    // Blue
            lpData->lpData[iJump+2= 255;  // Red
            lpData->lpData[iJump+1= 0;  // Green
          }
        }
      }
    }
  }

  ReleaseDC(hWndMain, hdc);
  return 0;
}
1. vfw의 기능 

VFW는  실시간 영상 처리를 위한 비디오를 캡처하고 화면을 캡처 
수정하기 위한 프레임 캡처 기능을 제공한다.

또한 Media Control Interface라고 불리는 MCI 장치를 제어해 주는 기능을
통해 비디오 소스의 시닥과 중지를 제어하는 기능이 있다.

VFW의 다양한 기능 
- 이미지 캡처 및 저장(DIB와 같은 비트맵 파일 형식 )
- 클립보드에 이미지와 팔레트 복사 기능
- 비디오 화면 구현을 위한 창을 생성 가능
- 캡처 비율을 조정 가능 
- 실시간 캡처 기능 지원
- 오디오 및 비디오 동적 연결 및 해제 기능 지원
- 저장 기능 (AVI 파일 형식)

2. VFW 비디오 처리를 위한 주요 함수 

비디오 처리를 위해서는 주로 영상 캡처 관련 함수인 CAP*** 형태의 함수를 이용한다
vfw 에서는 AVICap 라이브러리를 통해 실시간 영상 처리를 가능하게 한다 

capCreateCaputreWindow
- 화면 캡처에 사용할 윈도우 생성 
capDriverConnect
- 영상캡처 드라이버와 캡처된 화면을 보여주는 캡처 윈도우와의 연결 시도 
capDriverDisconnect
- 영상 캡처 드라이버와 캡처된 화면을 보여주는 켑처 윈도우와의 연결 해제 
capPreview
- 미리보기 실행 및 중지 설정(CPU자원 미사용)
capOverlay
- 오버레이보드를 통해 바로 화면 출력(CPU자원 미사용)
capPreviewRate
- 프레임을 출력하는 속도조절( 릴리초 단위)
capDlgViedoFormat
- 캡처 윈도우에 출력되는 영상 화면에 대한 크기 및 포맷 설정 
capGetVideoFormat
- 영상 복사를 위해 사용 포맷의 크기를 바이트로 반환 
capGetvideoFormatSize
- 영상 포맷의 크기를 바이트 크기로 반환 
capSetCallbackOnFrame
- 미리보기 모드로 화면을 캡처할 경우 Callback 함수를 사용하여 처리를
   하게 된다. 이때 이 함수를 설정하여 처리할 Callback함수명을 지정하게 된다
   NULL을 지정하면 해제 된다 
capGrabFrameNoStop
- 실시간 출력 중인 영상 프레임 하나를 정지영상으로 캡처한다


capCreateCaptureWindow
- HWND VFWAPI capCreateCaptureWindow( 
   LPCSTR lpszWindowName, // 캡처된 영상을 출력할 윈도우 이름 
   DWORD dwStyle,  // 윈도우 스타일 
   int x,   // 왼쪽 위부분 x좌표 
   int y,   // 왼족 윗부분 y좌표 
   int nWidth,  // 윈도우의 넓이 
   int nHeight,  // 윈도우의 높이 
   HWND nWnd,   // 캡처 윈도우를 포함하는 부모 윈도우 핸들 
   int nID // 캡처 윈도우의 ID값 지정 );

실시간 영상을 처리할 수 있는 윈도우 창을 생성한다 옆의 주석 처럼 각각의
특성을 지정하여 사용하면 된다 
정상적으로 생성이 되면 캡처 윈도우의 핸들 값이 반환이 되고, 그렇지 않을
경우에는 NULL값이 반환이 된다 

capDriverConnect
 BOOL capDriverConnect(
 HWND hwnd,  // 캡처 윈도우의 핸들 
 int iIndex  //캡처 드라이버 넘버 0~9 까지의 값
 );
 캡처 윈도우를 캡처 드라이버에 연결하는 함수이다. 캡처 장치가 정상적으로 연결되면 
 TRUE , 그렇지 않으면 FALSE 값을 반환한다 

capDriverDisconnect
 BOOL capDriverDisconnect(
 HWND hwnd// 캡처 윈도우 핸들 
  );
  캡처 윈도우에 연결된 드라이버를 분리한다 , 성공하면 TRUE , 실패하면 FALSE 반환한다

capPreview
 BOOL capPreview(
 HWND hwnd,  // 캡처 윈도우 핸들 
 BOOL f     // 활성화 비활성화를 결정하는 플래그 (TRUE 이면 활성화)
 );
 프레임을 캡처하고 callback 함수를 이용하여 직접 디스플레이을 하고자 한다면 f플래그를
 비활성화(FALSE) 시킨다. 프리뷰 모드는 CPU자원을 사용한다.

capOverlay
 BOOL capOverlay(
 HWND hwnd,    //캡처 윈도우 핸들 
 BOOL f           // 활성화 비활성화를 결정하는 플래그 (TRUE 이면 활성화
 CPU 자원을 전혀 사용하지 않고 영상을 출력하는 방법이다 . FALSE 가 리턴 될 경우
 오버레이 모드가 사용할 수 있음을 나타낸다.

capPreviewRate
 BOOL capPreviewRate(
 HWND hwnd,     //캡처 윈도우 핸들 
 wMS               // 밀리초(ms)단위로 프레임 캡처 및 디스플레이 
 );
  프레임 재생 속도를 지정한다 . 만약 66을 지정하면 ,0.066초 마다 디스플레이가 된다 
  , 따라서 1초의 경우 
   1000(md) % 66(ms) =15.1515  즉 1초에 15번 디스플레이하게 된다 

capDlgVideoFormat
 BOOL capDlgViedoFormat(
 HWND hwnd     // 캡처 윈도우 핸들 
 );
  사용자가 비디오의 프레임 크기와 이미지 포맷을 결정하게 하는 함수이다 
  CAPDRIVERCAPS의 구조체의 멤버 값이 업데이트 된다. 성공하면 TRUE, 
   실패하면 FALSE 를 리턴한다

capGetVideoFormat 
 DWORD capGetVideoFormat(
 HWND hwnd,       // 캡처 윈도우 핸들 
 LPVOID  psVideoFormat,         //BITMAPINFO 의 구조체 포인터
size wSize       // psVideoFormat가 참조체는 구조체의 크기
 );
 비디오 포맷의 복사본을 얻을 때 사용하고, 비디오 포맷의 크기를 바이트 크기로
  반환한다 

capGetVideoFormatSize
 CWORD capGetVideoFormatSize(
 HWND hwnd     // 캡처 윈도우 핸들
  );
  비디오 포맷의 크기를 바이트 크기로 반환한다 

capSetCallbackOnFrame
 BOOL capSetCallbackFrame(
 HWND hwnd,             // 캡처 윈도우 핸들 
 LPVOID fpProc          // 콜백 함수에 의해 호출되는 함수의 포인터
 );
비디오 프레임이 캡처 되었을 경우 이값을 처리하기 위한 함수명을 설정해주는
매크로 함수로써 일반적으로 아래와 같은 형식의 방법으로 선언되어 사용한다

LRESULT CALLBACK 콜백 함수면 (
 HWND hwnd,          // 캡처 윈도우 핸들 
 LPVIDEOHDR lpVHdr     // VIDEOHDR 구조체에 대한 포인터 
 );
 lpVHdr 구조체의 LPBYTE lpData(데이터 버퍼에 대한 데이터) 멤버를 참조함으로써
 캡처된 이미지 데이터에 접근할 수 있게 된다 

capGrabFarmeNoStop
 capGrabFrameNoStop(
 HWND hwnd   // 캡처 윈도 핸들 
 );
 압축되지 않는 한 프레임 데이터로 프레임 버퍼를 채운다 보통 콜백 함수를 호출하기
 전에 이 함수를 호출하여 이미지를 캡처한 후 , VIDEOHDR 구조체의 lpData멤버를
 참조하여 이미지를 디스플레이 한다 

3. VFW 관련 구조체 

VIDEOHDR 구조체
비디오 헤더 정보를 나타내는 구조체
콜백 함수 내에 사용되는 구조체로써, 멤버 변수 lpData 의 값을 적절하게 넘겨 주기
위해 사용한다 

typedef struct videohdr_tag(
LPBYTE lpData,   // 데이터 버퍼에 대한 포인터 
DWORD dwBufferLength,    // 데이터 버퍼의 길이 
DWORD dwBytesUsed,      // 실제로 사용된 바이트 
DWORD dwTimeCaptured,  //ms단위의 스트림 시작점 
DWORD dwFlag,    //임의의 정의된 플래그 
DWORD_PTR dwReserved[4],     //드리이브를 위한 예약 
} VIdEOHDR, NEAR *PVIDEOHDR , FAR *LPVIDEOHDR;

CAPSTATUS 구조체
캡처 윈도우의 현재 상태를 나타내는 구조체 
비디오 프레임의 크기와 이미지 포맷을 지정하는 대화상자 출력시 활용된다

capDlgVideoFormat 함수 
typedef struct{
UINT uiImageWidth,       // 이미지의 넓이 
UINT uiImageHeight,      // 이미지의 높이 
BOOL fLiveWindow,       //프리뷰모드를 사용하고 있다면 TRUE값을 반환 (flag변수)
BOOL fOverlayWindow,     // 오버레이모드를 사용하고 있다면 TRUE 값을 반환(flag 변수)
BOOL fScale,                  //입력 크기 조정 플래그, 클라이언트에 입력 크기 조정시
                                     // TRUE 값 설정(flag변수)
POINT ptScroll,               // 윈도우 클라이언트 영역 내 좌측 좌표 x, y 의 값
BOOL fUsingDefaultPalette,    // TRUE 이면 디폴드 팔래트를 사용한다(flag 변수)
BOOL fAudioHardware,    // WaveForm-audio 하드웨어가 설치 되어 있다면 TRUE(flag변수)
BOOL fCapFileExists,     // 유효한 캡처 파일이 생성되면 TRUE(flag 변수)
DWORD dwCurrentVideoFrame,   // 가장 최근 스트림 된 캡처 프레임의 개수(드랍된 프레임 포함)
DWORD dwCurrentVideoFramesDropped, // 드랍된 프레임의 갯수
DWORD dwCurrentWaveSamples,     // 현재 스트리밍 캡처의 파형-오디오 샘플을 갯수
DWORD dwCurrentTimeElapsedMS, // 시작한 스트리밍 캡처로 부터의 현재 시간, ms단위
HPALETTE hPalCurrent,                 // 현재 팔래트 핸들 
BOOL fCapturingNow,                    // 캡처 진행 중임을 확인하는 플래그, TRUE 면 진행중 
DWORD dwReturn,                         // 에러시 값을 리턴한다
UINT wNumVideoAllocated,           // 할당된 비디오 버퍼의 갯수 
UINT wNumAudioAllocated,           // 할당된 오디오 버퍼의 갯수 
} CAPSTATUS;


CAPTUREPARMS 구조체 
스트리밍 비디오 캡처를 하는 요소들로 구성이 되어 있다 이구조체는 캡처 비율, 버퍼 수
캡처 종료 방법 등에 영향을 주는 변수들을 설정하거나 값을 복사한다

typedef struct{
DWORD dwRequestMicroSecPerFame,   // 프레임 속도 비율,  66667은 초당 15프레임을 의미
BOOL fMakeUserHitOKToCapture,          // 사용자가 캡처를 시작했음을 대화상자를 뛰어보여주는
                                                         // 플래그
UINT wPercentDropForError,                 // 놓치는 프레임은 %비율로 제한 ( 기본값;10)
BOOL fYield,                                      // 쓰레드를 생성시켜 캡처링(TRUE 의 경우 캡처링 시작)
DWORD dwIndexSize,                         // 캡처 할 수 있는 프레임 혹은 오디오 버퍼의 갯수에 대한 
                                                        // 제한 0~34,952
UINT wChunkGranUlarity,                    // AVI파일의 논리적 블럭 (0:현재 섹터 크기가 알갱이처럼 
                                                        // 사용되고 있음을 가리킨다 
BOOL fUsingDOSMemory,                  // Win32 응용 프로그램에서는 사용하지 않음
UINT wNumVideoRequested,              // 할당된 비디오 버퍼의 최대 갯수 
BOOL fCaptureAudio,                         // 오디오 캡처 여부 (FLASE 이면 캡처 금지 )
UINT wNumAuidoRequested,             // 할당된 오디오 버퍼의 최대 개수 (최대 10)
UINT vKeyAbort,                              // 연속 캡처를 종료에 사용하는 가상 키코드 RegisterHotKey함수를
                                                     // 사용하여 변경 가능하다
                                                     // ESC 키를 누르면 캡처 종료(디폴드값)
BOOL fAbortLeftMouse,                    // 왼쪽 마우스를 눌러도 캡처종료 처리 여부
                                                     // (FALSE=>캡처 종료 불가능하게)
BOOL fAbortRightMouse,                 // 오른쪽 마우스를 눌러도 캡처 종료 처리 여부 
                                                    // (FALSE=>캡처 종료 불가능하게)
BOOL fLimitEndabled,                     //  TRUE 시간 제한 있음 FALSE 시간 제한 없음
UINT wTimeLimit,                          // 시간 제한( 초단위), fLimitEndabled이 TRUE일때만 사용가능
BOOL fMCIContol,                         // MCI 장치 스텝 캡처 플래그 FALSE 이면 MCI장치 이용한
                                                  // 스텝 갬처가 가능 해짐
DWORD dwMCIStartTime,             // 캡처시컨스를 위한 MCI장치의 ms단위 시작
DWORD dwMCIStopTime,            // 캡처시커스를 위한 MCI 장치의 ms단위 중단 
                                                    // (fMCIControl이 FALSE면 무시된)
BOOL fStepCaptureAt2x,              // 이중으로 해상도를 높이는 경우 사용 (TRUE일때 사용,RGB형식에서)
UINT wStepCaptureAverageFrames, // 한프레임을 만들때 프레임이 캡처 되는 횟수 평균치는 5
DWORD dwAudioBufferSize,             // 오디오 버퍼 사이즈
BOOL fDisableWriteCache,              // Win32 에서는 사용 안함 
UINT AVStreamMaster,                   // AVI 파일을 기록할때 오디오 스트림과 비디오 스트림의 
                                                    // 매치를 할것인지 판단하는 변수
} CAPTUREPARMS;

비트맵 인포 구조체 

typedef struct tagBITMAPINFO{
BITMAPINFOHEADER bmiHeader,      // 칼라 포맷에 대한 정보를 담은 구조체 선언
RGBQUAD bmiColors[1],                  // 칼라 테이블의 색상을 지정한다
} BITMAPINFO, *PBITMAPINFO;

typedef struct tagBITMAPINFOHEADER{
DWORD biSize,       // BITMAPINFOHEADER 구조체의 크기 
LONG biWidth,          // 이미지의 가로크기 
LONG biHeight,         // 이미지의 세로 크기 
WORD biPlanes,        // 장치의 플레인 수 91로 설정해야함
WORD biBitCount       // 각픽셀의 비트수 
DWORD biCompression,    // 압축 방법 설정 
DWORD biSizeImage,        // 비트맵 이미지 크기 
LONG biXPelsPerMeter,    // 수평 해상도 
LONG biXPelsPerMeter,    // 수직 해상도
DWORD biClrUsed,           // 사용된 컬러의 수 
DWORD biClrImportant,      // 비트맵 출력에 사용되는 컬러의 수 
}BITMAPINFOHEADER, *PBITMAPINFOHEADER;

'Win32 Api > 강의' 카테고리의 다른 글

비트맵뷰어  (0) 2010.12.28
2010년 12월 1일 수요일  (0) 2010.12.02
2010년 11월 22일 월요일 (첫 강의)  (0) 2010.11.22
Win32에서 라이브러리 vfw 로 지원한다. 등록 방법은 다음과 같이 하면 된다.

 위의 그림과 같이 메뉴창에서 Project창에서 Settings로 들어간다 단축키는 Alt+F7


 그러면 위와 같이 새창이 뜨는데 탭메뉴에서 Link를 찾아 들어간다. Link탭에 보면 Object/library modules라는 곳이 있는 이곳에 vfw32.lib를 등록 시켜주고  OK눌러주면 된다. 이 방법으로 자기가 셋팅해놓은 컴퓨터에서는 사용이되지만 다른 컴퓨터에서는 셋팅이 안되어 있기때문에 다시 똑같은 작업을 해주어야하는 번거로움이 있다. 이 해결법은 이방법말고 다른 방법이 있는데 다음과 같다.


 위와 같이 소스에 #pragma comment(lib,"vfw32.lib") 이란 명령어를 추가해주면 처음과 같은 방법으로 하지 않아도 라이브러리를 가져올수가 있다. 그리고 컴퓨터를 옮기거나 프로그램 삭제했다가 다시 설치해도 처음 방법처럼 다시 셋팅해줄 필요가 없다.

그럼, 이번에 공구한 TFT-LCD는 보통 QVGA급(320 x 240; Quarter VGA, 즉, 640 x 480 VGA의 1/4에 해당하는 크기를 말함)이므로 일단 320 x 240 사이즈의 테스트용 비트맵 파일을 가지고 실험을 해봤다.

테스트 사진 중에 가로로 긴 사진이 없어서 세로로 긴 사진을 가지고 테스트했다.

(그러나, 사진을 보면 잘했다는 생각이 들것임. *^^*) 일단, 완소 김태희 양의 사진을 가지고 분석했다.
 

1.jpg

 
완소 김태희 사진이 들어있는 BMP파일을 첨부파일을 울트라 에디터로 열어보면 바이너리 모드로 열린다. 그리고, 탐색기에서 해당 비트맵 파일의 등록정보를 열어서 파일 사이즈 및 비트맵 정보를 확인하자. 마우스로 오른쪽 클릭하고 등록정보를 확인하면 파일 크기가 230,454 바이트(=16진수로는 0x38436 바이트)이고, 비트 깊이는 24비트임을 알 수 있다. 물론 크기는 240 x 320 픽셀이다.

 자, 그 다음에 울트라 에디터로 살짝 파일을 열어보면 파일 앞 부분이 다음과 같이 되어 있음을 알 수 있다.

2.jpg

 위의 그림에서 빨간색 박스로 그려진 부분이 BMP파일의 헤더에 해당한다. 위의 헤더파일의 구조는 다음과 같다.

3.jpg

 생각보다 BMP 파일의 헤더 구조는 단순하다. 그리고, 저 헤더부분 뒤에 이미지의 RGB 데이터가 바로 이어지기 때문에 BMP파일에서 RGB 데이터를 추출하고자 할 때에는 헤더부분을 제외한 다음 부분부터 파싱을 하면 된다. 헤더 부분을 잠시 살펴보면 모든 BMP 파일은 첫 두바이트가 0x41, 0x4D로 시작한다. 이를 보통 매직코드라고 부른다. 대부분의 특정한 포맷을 가진 파일들은 위와 같은 매직코드를 가지고 있는 경우가 많다. 그 다음 필드는 BMP 파일의 사이즈인데, 아까 등록정보로 0x38436 바이트라고 했는데, 그 값이 뒤집혀서(즉, 리틀 엔디안 형식으로) 저장되어 있다. 그리고, 다섯번째 필드를 보면 실제 이미지 데이터가 시작하는 오프셋의 위치를 담고 있는데... 0x36번째 부터란다. 헤더 부분의 총크기를 계산하면 0 ~ 0x35이므로 총 0x36바이트를 가진다. 따라서, 오프셋의 위치와 정확히 일치함을 알 수 있다. 그리고, image width/height in pixels 필드는 그림 파일의 가로/세로 픽셀 수에 해당하는데 저 값을 10진수로 바꿔보면 240 x 320이다. 그 다음 가장 중요한 "size of image data in bytes"는 실제 이미지 데이터의 크기를 말한다(데이터 크기가 정말 저 값이 맞는지는 각자 확인하기 바람 *^^*). 또한, number of bits per pixel 은 한 픽셀당 비트수를 말하는데, 0x18이니까 십진수로 24비트이다. 아까 등록정보에서 확인한 값과 일치한다. 이런 식으로 헤더정보를 파싱하면 된다.


이제, 헤더의 다음부분 부터 이미지 데이터가 어떤 식으로 저장되어 있는지 살펴보면...

신기하게도... 좌측상단의 점부터 데이터를 저장하고 있는 것이 아니라, 맨 아래줄부터 데이터가 저장된다.

무슨 말인고 하면...

 

4.jpg

 

위의 그림처럼 BMP파일의 그림 데이터는 라인단위로 저장되는데... 이미지의 가장 아래 라인부터 역순으로 저장된다. 게다가, 저장되는 순서도 RGB(8비트, 8비트, 8비트) 순서로 저장되는 것이 아니라 BGR 순서로 저장된다. 정말???

5.jpg

 

위의 모서리 값은 실제 이미지 데이터의 RGB값을 그래픽 뷰어 프로그램으로 찾아 본 것인데, 헤더 부분의 그 다음 3바이트를 확인해보면...

 

EC, EF, E6

 

임을 알 수 있는데, 그 값은 정확히 위 의 그림에서 ③번 모서리의 RGB의 역순임을 알 수 있다. 이런 식으로 BMP파일에서 RGB 데이터를 추출하면 된다


'Win32 Api > 강의' 카테고리의 다른 글

CAM을 활용한 영상처리 프로그램  (0) 2011.03.03
2010년 12월 1일 수요일  (0) 2010.12.02
2010년 11월 22일 월요일 (첫 강의)  (0) 2010.11.22
//windows.h 헤더 파일은 기본적인 데이터 타입, 함수 원형 등을 정의하며 그 외 필요한 

헤더 파일을 포함
#include <windows.h> 

// 함수 선언부
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

//HINSTANCE, lpszClass 를 요구하는 경우가 많으므로 따로 전역으로 정의.
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First!");

//프로그램 시작점인 엔트리 포인트가 WinMain 함수이다.
//APIENTRY 지정자는 호출규약(__stdcall)에 관한 명시. 없다고 생각해도 무관.
(★실습해보자.) 다르게 쓸 수 있는 건?, 빼도 되나?
//__cdecl ☜ 으로 교체하니 안됨, APIPRIVATE, PASCAL은 가능 어차피 저 두개도 #define으로 __stdcall명시되어있음 되는게 당연함 ㅡㅡ;;;
//HWND와 Hinstance의 차이.
인스턴스 : 실행중인 프로그램 하나. 윈도우는 멀티태스킹 시스템이면서 하나의 프로그램 중복실행 가능. 이때 실행되는 각각의 프로그램을 프로그램인스턴스라고 함. 

두개의 메모장 프로그램 인스턴스.
인스턴스는 클래스의 상속받은 객체라고 보면 된다. 메모장 이라는 클래스가 실현된 객체라고 보면 됨.
//call STACK으로 HINSTANCE값 살펴봐라.
★ 디버깅 모드로 실행해보면 callstack(ctrl + alt + C)에 instance값 나옴

HINSTANCE 핸들은 보통 실행되고 있는 Win32 프로그램이 메모리 상에 올라가 있는 시작 주소 값을 갖고 잇습니다. 보통은 0x00400000 이런 식의 값을 가지고 있는데 저 값의 메모리 주소에 실행 모듈이 올라가 잇다는 것을 의미 하겠죠.(스크랩 툴을 WINHEX로 보여준다.) 보통 리소스들을 로드 하는 함수들에서 이 핸들 값을 많이 참조하게 되는데 이 이유는 메모리 상에 올라가 있는 실행 모듈 들 중 hInstance 가 가르키는 주소에 올라가 있는 실행 모듈에서 그 리소스를 읽어오라고 지정 해주는 것입니다. 그리고 그 변수를 글로벌로 잡는 경우가 있는데 그 이유는 그 변수를 여러 함수들에서 자주 사용하기 때문에 Win32의 구현 방식상 여러함수에서 그 값을 읽어 와야 하고 그 값은 WinMain() 에서 한번 들어오는 값이기 때문에 글로벌 변수에 넣어 두는것이죠... HINSTANCE는 프로그램의 인스턴스 식별자(핸들)을 의미합니다. 간단히 말씀 드리자면 실행파일 형태로 껍데기에 불과한 프로그램이 메모리에 실제로 구현된 실체를 뜻합니다. 따라서 만약에 프로그램을 여러 개 실행시켰을 때 이들의 각각을 프로그램 인스턴스라고 하고 실행되는 프로그램마다 고유한 값을 갖고 실행중인 프로그램들을 구분하기 위한 식별 값으로 인스턴스 핸들을 이용합니다.
 
HWND는 프로그램의 윈도우 식별자(핸들)를 의미하는데 해당 프로그램의 윈도우들을 구분하기 위한 식별 값을 말합니다. 여기서 윈도우와 프로그램과는 차이가 있습니다. 윈도우 프로그래밍에서 하나의 프로그램에는 많은 윈도우들을 가질 수 있기 때문입니다. 프로그램의 윈도우 핸들 중에서 대표적인 것이 부모 윈도우의 핸들입니다. 따라서 대부분의 윈도우 프로그래밍의 작업과정 중 대부분은 이들 HWND 값을 통해 얻은 윈도우 핸들을 이용한다고 이해하시면 빠를 것 같습니다. 창뿐만 아니라 여러가지 수많은 컨트롤 등도 모두 윈도우로 생성시에 핸들 값을 소유하고 있는 존재이기 때문입니다.
------------------------------------------------------------------------------------
HINSTANCE는 프로그램의 핸들이 아니라, 프로그램 코드를 담고 있는 모듈에 대한 핸들이다. 즉, 프로그램이 수행되려면 프로그램 코드를 담고 있는 파일을 메모리의 특정 영역에 올려서 명령을 하나씩 읽어가면서 수행할 수 있도록 준비해 놓아야 한다. 이렇게 메모리에 올려진 프로그램 코드 덩어리를 윈도우에서 관리하기 위해서 일종의 고유 식별 번호를 부여하는데, 이것이 인스턴스 핸들, HINSTANCE이다. 기본적으로 프로세스를 실행하는 실행파일의 코드를 메모리에 올려놓은 모듈이 하나 있어야 하므로, 실행 파일의 모듈에 대한 인스턴스 핸들을 OS가 어플리케이션 WindMain의 인자로 넘겨주는 것이다. 한 프로세스가 여러 개의 모듈을 로딩하여 프로그램을 실행하고 있다면 하나의 프로그램이 여러개의 인스턴스 핸들을 할당받아 쓰고 있을 수 있다.(물론 한 개의 모듈을 여러 프로세스가 공유하고 있을 수도 있다.) 대표적인 예가 바로 IE인데, 간단하게 DLL파일 한 개를 쓸 때마다 이 DLL 모듈에 대한 인스턴스 핸들이 한 개씩 생긴다고 보면 된다. 물론 DLL이 한번 로딩되면 다른 프로그램 사이에서 공유된다. 한 개의 프로그램에서 HINSTANCE가 한 개만 있는 것이 아니며, 또한 하나의 인스턴스 핸들이 한 개의 프로그램에만 종속되는 것이 아니므로 어떤 프로그램의 출력 대상을 지정하는 데에는 부적절하다는 점을 알 수 있다. 물론, 인스턴스 핸들은 애초부터 화면 출력을 고려햐여 만들어진 식별자는 아니며 단지 프로그램 코드 덩어리를 관리하기 위해 
만들어진 리소스이다. 윈도우라는 OS에서 화면 출력을 위해 관리하는 리소스가 바로 HWND이다. MSN 메신저 등을 보면 알겠지만 하나의 프로그램이 하나의 창을 사용한다는 보장은 없다. 오히려 99.99%의 프로그램은 한 개 이상의 윈도우로 구성되어 있다. 한 프로그램의 윈도우가 겉보기에는 단일한 대상 영역으로 보일지라도 실제로는 구성 요소별로 분리하여 별개의 윈도우로 만들어 각 윈도우는 자기 자신이 맡은 부분에 대한 화면 출력과 사용자 입력만을 담당한다. 자연히 하나의 프로그램에서 사용하는 HWND 타입의 개체 역시 1개 이상이 될 수 밖에 없으며, 이러한 상황에서 특정 위치에 특정한 동작을 수행하기 위해서는 HWND로 대상 영역을 구분할 수 밖에 없다. 
------------------------------------------------------------------------------------
구조체를 찾아가보면 HWND는 윈도우 자체에 대한 정보를 가지는 것이고, HINSTANCE는 현재 실행중인 인스턴스에 관한 정보를 가지고 있습니다. 스턴스는 운영체제 전체에서 유일한 번호를 가지므로(실행중인 창들이라고 보면 되죠) HWND를 가지고 인스턴스를 구할순 없지만 HINSTANCE를 가지고 있으면 HWND에 관한 내용들을 구할수 있습니다. 
//hInstance : 프로그램의 인스턴스(클래스가 메모리에 실제로 구현된 실체) 핸들
//hPrevInstance : 바로 앞에 실행된 프로그램의 인스턴스 핸들. 없을 경우는 NULL. Win32에서는 항상 NULL, 16비트 와의 호환성을 위해 존재. 안쓴다.
//lpszCmdParam : 명령행 인수.
(★실습해보자.)

C에서 main 이라는 함수를 정의하면 프로그램 진입점인데, 여기서 main 이란 함수를 선언하고 정의해서 사용하면 될까 안될까? 한번 해보슈.(★실습해보자.)

//WinMain이 호출되는 과정 설명
//파일의 구조, 파일 헤더, 컴파일러가.. 운영체제의 프로그램 로더는 PE의 헤더에 있는 IMAGE_OPTIONAL_HEADER 구조체의 AddressOfEntryPoint 필드를 참고해 그 주소의 코드(.text 섹션 내부에 위치)를 실행시킨다. AddressOfEntryPoint가 가리키는 지점에는 WinMainCRTStartup()함수가 있다. 이 함수는 WinMain()이 필요로 하는 실행환경을 만들고, 각 매개변수로 전달될 값들을 구해서 WinMain()함수를 호출한다. WinMain()함수는 WinMainCRTStartup()함수로 부터 넘겨받은 arguments를 이용해 프로그램 내부적인 동작을 실행한다. WinMain()함수의 동작이 종료되면 실행 포인터는 WinMainCRTStartup()함수로 return되고, 이 함수는 다시 WinMain()의 리턴값을 exit함수의 parameter로 넘겨준다. exit()함수는 여러가지 마무리 작업을 하고, Win32 ExitProcess() 함수를 호출하고 WinMain()의 리턴값을 운영체제로 넘기고 프로그램을 종료한다.
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdParam,int nCmdShow)
{
HWND hWnd
MSG Message

__cdeclC언어 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해 전달되며, 호출한 곳에서 스택을 정리한다. 특징적인건 호출한 쪽에서 스택을 정리하기 때문에 가변 인자를 지원한다는 것이다. __fastcall말 그대로 빠른 호출이다. 파라미터 중 일부를 레지스터를 통해서 전달하는 함수다. x86 계열에서는 일반적으로 ecx, edx로 파라미터를 전달하고 나머지는 스택으로 전달한다. __cdecl과 같이 오른쪽에서 왼쪽으로 파라미터를 전달하며, 스택 정리는 호출을 당한 곳에서 수행한다. 따라서 가변 인자를 지원하지 못한다. __stdcall윈도우 API의 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해서 전달되며, 스택 정리는 호출 당한 곳에서 이루어진다.

//윈도우 클래스. F12 또는 점찍어 확인. 10개의 맴버.
//여기서 10개를 다 지정해주므로 하나식 살펴보자.


WNDCLASS WndClass

//cbClsExtra, cbWndExtra : 예약영역. 윈도우가 내부적으로 사용.  특수한 목적으로 사용. 0으로 사용.
//hbrBackground : 윈도우 배경색깔을 채색할 브러시 지정. 색을 바로 지정해도 되고 GetStockObject라는 함수로 윈도우즈 기본 재공 브러시 사용하거나 COLOR_WINDOW 같은 색을 바로 지정해줘도 상관없다. WHITE니까 RED해봐라. 되는가? 윈도우는 흰색검은색 회색만 지원함.

//hCursor, hIcon 기본적으로 제공하는 아이콘과 커서 사용.
★ 커서에 IDC_CROSS, 아이콘에 IDI_HAND 로 바꿔보자. ☞ 커서모양이 글씨입력 모양으로 바낀다 I 요렇게 생김
//lpfnWndProc : 윈도우 메시지 처리 함수지정. 메시지 발생시 마다 함수가 호출. 함수 이름을 정할 수 있지만 거의 WndProc로 사용한다. 암묵적으로.여러분들도 사용하시길.
//lpszClassName : 윈도우 클래스의 이름을 정의. 윈도우 클래스의 이름은 보통 실행 파일의 이름과 일치시켜 작성. 이 두개의 멤버가 중요함.
//lpszMenuName: 이 프로그램이 사용할 메뉴를 지정한다. 메뉴는 프로그램 코드에서 만드는 것이 아니라 리소스 에디터에 의해 별도로 만들어진 후 링크시에 같이 합쳐진다. 메뉴를 사용하지 않을 경우 이 멤버에 NULL을 대입해 주면 된다.

//style : 윈도우가 어떤 형태를 가질 것인가를 지정하는 멤버. 이 멤버가 가질 수 있는 값은 무지하게 많지만 가장 많이 사용하는 값이 CS_HREDRAW와 CS_VREDRAW이다. 이 두 값을 OR 연산자(|)로 연결하여 사용한다. 이 값들의 의미는 윈도우의 수직(또는 수평) 크기가 변할 경우 윈도우를 다시 그린다는 뜻이다. 이밖에도 많은 값이 올 수 있다
Style : 윈도우 스타일. 
lpfnWndProc : 윈도우 메시지 처리함수 지정. 메시지 발생시 마다 이 멤버가 지정하는 함수 호출되어 메시지 처리.
이름을 마음대로 정의할 수 있지만 대부분 WndProc 으로 정하는 편.

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=(LPCWSTR)lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
//윈도우 클래스 구조체의 번지를 전달. 앞으로 사용할 윈도우를 등록하는 과정 RegisterClass(&WndClass);
★ 클래스 네임과 윈도우 네임을 같이 주었는데 변경하여 차이를 보자. 스파이 이용.

//3번째 인자는 dwStyle. 만들고자 하는 윈도우의 형태. 일종의 비트 필드값이며 거의 수십개를 헤아리는 매크로 상수들이 정의되어 있고 이 상수들을 OR연산자로 연결하여 윈도우의 다양한 형태를 지정한다. 윈도우가 경계선을 가질 것인가, 타이틀 바를 가질 것인가 또는 스크롤 바의 유무 등등을 세세하게 지정해 줄 수 있다. 가능한 스타일값에 관한 자세한 내용은 레퍼런스를 참조하되 WS_OVERLAPPEDWINDOW를 사용하면 가장 무난한 윈도우 설정 상태가 된다. 즉 시스템 메뉴, 최대 최소 버튼, 타이틀 바, 경계선을 가진 윈도우를 만들어 준다.
//X, Y, nWidth, nHeight : 인수의 이름이 의미하듯이 윈도우의 크기와 위치를 지정하며 픽셀 단위를 사용한다. x, y좌표는 메인 윈도우의 경우는 전체 화면을 기준으로 하며 차일드 윈도우는 부모 윈도우의 좌상단을 기준으로 한다. 정수값을 바로 지정해도 되며 CW_USEDEFAULT를 사용하면 윈도우즈가 알아서 적당한 크기와 위치를 설정해 준다. 예제에서는 모두 CW_USEDEFAULT를 사용하였다.

//hWndParent : 부모 윈도우가 있을 경우 부모 윈도우의 핸들을 지정해 준다. MDI 프로그램이나 팝업 윈도우는 윈도우끼리 수직적인 상하관계를 가져 부자(parent-child) 관계가 성립되는데 이 관계를 지정해 주는 인수이다. 부모 윈도우가 없을 경우는 이 값을 NULL로 지정하면 된다.

//hmenu : 윈도우 클래스에서 지정한 메뉴를 그대로 사용하려면 NULL로 주면된다. 나중에 5장에서 배운다.

//lpvParam : CREATESTRUCT라는 구조체의 번지이며 특수한 목적에 사용된다. 보통은 NULL값을 사용한다.

hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);

if (strcmp(lpszCmdParam,"mimi")==0)
{
MessageBox(NULL, L"mimi", L" ", 0);
}

// hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다. nCmdShow는 윈도우를 화면에 출력하는 방법을 지정.
//nCmdShow 출력해보자. SW_HIDE도 써보자.
//디버그 뷰 이용
TCHAR DbgStr[256];
wsprintf(DbgStr, TEXT("%d"),nCmdShow);
MessageBox(NULL, DbgStr, TEXT(""),MB_OK);


ShowWindow(hWnd,nCmdShow);
//윈도우즈를 메시지 구동 시스템(Message Driven System)이라고 하며 이 점이 도스와 가장 뚜렷한 대비를 이루는 윈도우즈의 특징이다. 도스에서는 프로그래머에 의해 미리 입력된 일련의 명령들을 순서대로 실행하는 순차적 실행방법을 사용한다. 윈도우즈는 이와 다르게 프로그램의 실행 순서가 명확하게 정해져 있지 않으며 상황에 따라 실행 순서가 달라지는 데 여기서 말하는 상황이란 바로 어떤 메시지가 주어졌는가를 말한다. 메시지란 사용자나 시스템 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보를 말한다. 예를 들어 사용자가 마우스의 버튼을 눌렀다거나 키보드를 눌렀다거나 윈도우가 최소화되었다거나 하는 변화에 대한 정보들이 메시지이다. 메시지가 발생하면 프로그램에서는 메시지가 어떤 정보를 담고 있는가를 분석하여 어떤 루틴을 호출할 것인가를 결정한다. 즉 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 실행된다. 윈도우즈 프로그램에서 메시지를 처리하는 부분을 메시지 루프라고 하며 보통 WinMain 함수의 끝에 다음과 같은 형식으로 존재한다.

BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT 

wMsgFilterMax); 

이 함수는 시스템이 유지하는 메시지 큐에서 메시지를 읽어들인다. 읽어들인 메시지는 첫번째 인수가 지정하는 MSG 구조체에 저장된다. 이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 False를 리턴하며 그 외의 메시지이면 True를 리턴한다. 따라서 WM_QUIT 메시지가 읽혀질 때까지, 즉 프로그램이 종료될 때까지 전체 while 루프가 계속 실행된다. 나머지 세 개의 인수는 읽어들일 메시지의 범위를 지정하는데 잘 사용되지 않으므로 일단 무시하기로 한다.

BOOL TranslateMessage( CONST MSG *lpMsg); 
키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 해 준다. 윈도우즈는 키보드의 어떤 키가 눌러졌다거나 떨어졌을 때 키보드 메시지를 발생시키는데 이 함수는 키보드의 눌림(WM_KEYDOWN)과 떨어짐(WM_KEYUP)이 연속적으로 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다. 예를 들어 A키를 누른 후 다시 A키를 떼면 A문자가 입력되었다는 메시지를 만들어 낸다.
// 해보자.
case WM_CHAR:
MessageBox(NULL, L"이미 있음", L"", MB_OK);
PostQuitMessage(0);
return 0;

SPY로 확인도 해보자.

LONG DispatchMessage( CONST MSG *lpmsg); 
시스템 메시지 큐에서 꺼낸 메시지를 프로그램의 메시지 처리 함수(WndProc)로 전달한다. 이 함수에 의해 메시지가 프로그램으로 전달되며 프로그램에서는 전달된 메시지를 점검하여 다음 동작을 결정하게 된다.
while(GetMessage(&Message,0,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
//메시지 루프가 종료되면 프로그램은 마지막으로 Message.wParam을 리턴하고 종료한다. 이 값은 WM_QUIT 메시지로부터 전달된 탈출 코드(exit code)이다. 도스에서 사용하는 탈출 코드와 동일한 의미를 가지며 사용되는 경우가 거의 없다.
return (int)Message.wParam;
}

//WndProc은 WinMain에서 호출하는 것이 아니라 윈도우즈에 의해 호출된다. WinMain내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 할 뿐이며 WndProc은 메시지가 입력되면 윈도우즈에 의해 호출되어 메시지를 처리한다. 이렇게 운영체제에 의해 호출되는 응용 프로그램내의 함수를 콜백(CallBack) 함수라고 한다. callback함수 프로그램에서 호출한 함수가 실행중에 실행하도록 먼저 지정해둔 함수호출한 함수의 인수로서 콜백함수에 참조 정보를 넘겨둠으로 초출한 함수에서 임의의 콜백 함수를 실행시켜 콜백 함수에 실행 제어를 옮길 수 있다. 전화를 상대에게 한번 걸어서 전화번호만 알려주고 전화를 끊은 후, 상대방이 다시 전화를 걸어주는 것과 비슷함으로 콜백이라는 이름이 붙었다. 콜백 함수는 부른 곡에서 어떤 이벤트가 발생한 경우 처리를 지정할 목적으로 쓰는 경우가 많다. 콜백 함수를 쓰면 이벤트의 발생을 감지하기 위한 처리와 그 이벤트를 일어났을 때, 실행할 각각의 처리를 나누어서 코딩할 수 있다. C/C++에서는 함수 포인터를 불러 함수에 인수를 넘겨주는 것으로 콜백 함수가 움직인다. C/C++이 아닌 다른 언어에서도 서브 루틴의 참조를 다른 서브 루틴에게 넘겨서 실행할 수 있게 되어진 언어에서는 동일하게 콜백 함수를 쓸 수 있다. 
//LRESULT : F12 따라가보자.
//long 형.그럼 반환값을 다르게 줘보자.
0 또는 1로.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM 

wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_DESTROY:
//PostQuitMessage 함수는 여러 가지 면에서 일반적인 메시지 함수와는 다르게 동작합니다. 왜냐하면 프로세스를 종료하는 특수한 명령이기 때문입니다. PostMessage(WM_QUIT)에 비해 대여섯가지 차이점이 있는데 가장 이해하기 쉬운 예를 들자면요.
PostQuitMessage(0);
명령1;
명령2;
 
이런 상황일 때 프로그램을 종료하고 난 후라도 명령1과 명령2까지 다 수행을 해야 정상적인 종료가 됩니다. WM_QUIT는 메시지 루프를 종료시키므로 PostMessage로 보내서는 이런 효과를 낼 수가 없습니다. 그래서 별도의 함수를 제공하는데 이 함수는 실제로 프로그램을 종료하는 것이 아니라 다음 아이들 타임에 프로그램을 종료하도록 특별한 플래그를 세트하는 역할만 합니다. 다소 복잡한 내용이라 저도 바로 기억을 떠 올리기 어려운데요, 좀 더 자세한 내용에 대해 알고 싶으시면 Jeff의 책을 참고하세요. 그리고 스파이는 훅을 사용하는 것이 맞습니다. MSDN에 소스도 공개되어 있으니 어렵지 않게 찾을 수 있을 겁니다. 소스내용을 보시면 보기보다 특별한 내용이 없어 쉽게 분석 됩니다.

PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}


'Win32 Api > 강의' 카테고리의 다른 글

CAM을 활용한 영상처리 프로그램  (0) 2011.03.03
비트맵뷰어  (0) 2010.12.28
2010년 11월 22일 월요일 (첫 강의)  (0) 2010.11.22
강사 : 하재민 

궁금한게 있으면 생각만하지말고 직접 해봐라!!
수업안하내 ㅡㅡ;;;; 
       윈도우 단축키
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
Win + Break = 시스템정보
Win + R = 실행
Win + E = 탐색기
Win + M, D = 바탕화면보기
Win + L = 화면잠금
Win + F = 검색
Ctrl+Shitf+Esc = 작업관리자


'Win32 Api > 강의' 카테고리의 다른 글

CAM을 활용한 영상처리 프로그램  (0) 2011.03.03
비트맵뷰어  (0) 2010.12.28
2010년 12월 1일 수요일  (0) 2010.12.02

+ Recent posts