상세 컨텐츠

본문 제목

프로그래밍 화면캡쳐 관련 (대부분 win32 프로그래밍)

프로그래밍 관련

by AlrepondTech 2016. 12. 9. 11:47

본문

반응형
728x170

 

 

 

 

 

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

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

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

 

 

 

 

 

 

 

출처: http://blog.naver.com/PostView.nhn?blogId=tipsware&logNo=220367065310&parentCategoryNo=&categoryNo=44&viewDate=&isShowPopularPosts=false&from=postView

 

프로그램으로 화면을 캡쳐할 수 있는 방법은 참 많습니다. 하지만 초보 프로그래머들에게 코드로 작성해보라고 하면 어려워하는 것 같아서 초보자들이 쉽게 사용할 수 있는 방법을 소개하려고 합니다. 이 글에서는 CImage 클래스를 사용하여 화면을 캡쳐하는 방법에 대해서 소개할 것인데, 이 방법은 캡쳐된 화면을 원하는 이미지 파일(*.bmp, *.png, *.jpeg, ...)로 저장하기도 편하기 때문에 프로그래머들이 많이 사용합니다.

 

 

// 화면 전체를 캡쳐하기 위해서 Window DC 를 사용합니다. ::GetDC(NULL) 이라고 해도 결과는 동일합니다.

HDC h_dc = ::GetWindowDC(NULL);

// 캡쳐에 사용할 CImage 객체를 선언한다.

CImage tips_image;

// 수평 해상도를 얻는다.

int cx = ::GetSystemMetrics(SM_CXSCREEN);

// 수직 해상도를 얻는다.

int cy = ::GetSystemMetrics(SM_CYSCREEN);

// 화면의 색상 수를 얻는다.

int color_depth = ::GetDeviceCaps(h_dc, BITSPIXEL);

// 캡쳐에 사용할 이미지를 생성한다.

tips_image.Create(cx, cy, color_depth, 0);

  

// 화면 전체 이미지를 m_tips_image 객체에 복사한다. 

::BitBlt(tips_image.GetDC(), 0, 0, cx, cy, h_dc, 0, 0, SRCCOPY);

// 캡쳐한 이미지를 "test.png" 라는 이름으로 저장한다.
tips_image.Save(L"test.png", Gdiplus::ImageFormatPNG);

 

// 화면 캡쳐에 사용했던 DC를 해제한다.

::ReleaseDC(NULL, h_dc); 

tips_image.ReleaseDC();

  

 이 코드를 수행하고 나면 작업경로에 "test.png" 라는 파일이 생길 것 입니다. Save 함수는 파일의 확장자로써 파일을 구성하는 것이 아니라 그 뒤에오는 Gdiplus::ImageFormatPNG 라는 인자를 가지고 이미지 파일 형식을 결정하게 됩니다. 따라서 Jpeg 와 같은 형식의 이미지로 저장하고 싶다면 아래와 같이 코드를 구성하시면 됩니다.

 

tips_image.Save(L"test.jpeg", Gdiplus::ImageFormatJPEG); 

 

 

 

 

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

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

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

 

 

 

 

 

출처: http://mingy.tistory.com/entry/%ED%99%94%EB%A9%B4-%EC%BA%A1%EC%B3%90

 

 

CClientDC dc( GetDesktopWindow() );         

// 바탕화면으로부터 DC 생성

     CDC memDC;
    memDC.CreateCompatibleDC( 

&

dc );           

// dc에 호환되는 메모리 DC 생성

     

int 

w = 200;   //1024;
    

int 

h = 200;    //768;

     CBitmap bitmap;

    bitmap.CreateCompatibleBitmap( 

&

dc, w, h );   

// dc와 호환성이 있는 비트맵 생성


    memDC.SelectObject( 

&

bitmap );                    

// MemDC에 bitmap 설정

    memDC.BitBlt( 0, 0, 200, 200, 

&

dc, 0, 0, SRCCOPY );

     BITMAP bm;
    bitmap.GetBitmap( 

&

bm );

 

    // 32비트 컬러에서는 한 점당 4바이트가 사용된다. 4바이트 * point(가로해상도) * 높이    // 그리고 16비트 컬러는 한 점당 2바이트가 사용된다.

    DWORD dwCount = bm.bmWidthBytes * bm.bmHeight;  

// 총 필요 버퍼


    BYTE

*

 pBuff   = 

new 

BYTE[dwCount];

 

    

// pBuff에는 데이터가 RGB0순으로 읽혀진다. RGB는 컬러값이고 나머지 1바이트는 사용되지 않는다.


    DWORD dwRead  = bitmap.GetBitmapBits( dwCount, pBuff ); 

 // pBuff에 bitmap 데이터 읽기

    

delete 

[] pBuff;

     pDC->BitBlt( 0, 0, 200, 200, 

&

memDC, 0, 0, SRCCOPY );        

// 화면에 배경 화면 출력

}

 

 

 

 

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

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

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

 

 

 

 

 

출처: http://monsterwave.tistory.com/38

 

 

이 소스 찾는다고 3일 액션스크립트 붙잡고 삽질한거 생각하면 그저 눈물이 ㅠㅠ
역시 사람은 똑똑해야되 ㄷㄷㄷ

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

//컨트롤 캡쳐관련 클래스
public class win32
 {
  [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]

  private static extern bool BitBlt(

   IntPtr hdcDest, // handle to destination DC

   int nXDest,  // x-coord of destination upper-left corner

   int nYDest,  // y-coord of destination upper-left corner

   int nWidth,  // width of destination rectangle

   int nHeight, // height of destination rectangle

   IntPtr hdcSrc,  // handle to source DC

   int nXSrc,   // x-coordinate of source upper-left corner

   int nYSrc,   // y-coordinate of source upper-left corner

   System.Int32 dwRop  // raster operation code

   );
 

  [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]

  private static extern IntPtr  CreateDC(

   string lpszDriver,        // driver name

   string lpszDevice,        // device name

   string lpszOutput,        // not used; should be NULL

   IntPtr lpInitData   // optional printer data

   );
 

  public static bool CaptureWindow(System.Windows.Forms.Control control, ref System.Drawing.Bitmap bitmap)
  {

   Graphics g2 = Graphics.FromImage(bitmap);

   IntPtr dc1 = CreateDC("DISPLAY", null, null, (IntPtr)null);

   IntPtr dc2 = g2.GetHdc();


   Point APoint = control.PointToScreen(new Point(0, 0));

   BitBlt(dc2, 0, 0, control.Width, control.Height, dc1, APoint.X, APoint.Y, 13369376);
 

   g2.ReleaseHdc(dc1);

   g2.Dispose();

   return true;

  }

 }

//컨트롤 캡쳐 매서드
private void swf2jpg()
{
   Bitmap bitmap = new Bitmap(flash.Width, flash.Height);  //플래쉬의 가로,새로

   win32.CaptureWindow(flash, ref bitmap);

   bitmap.Save(Application.StartupPath + @"\work\report.jpg");    //저장경로
}


출처 : http://www.hoons.kr/board.aspx?Name=pds&board_idx=444468&page=1&Mode=2&BoardIdx=887 
화면캡쳐프로그램 만들기 : http://www.hoons.kr/Lecture/LectureView.aspx?BoardIdx=34&kind=26

 

 

 

 

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

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

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

 

 

 

 

 

출처: http://shaeod.tistory.com/197

 

 

OpenGL로 구현한 프로그램에 화면 캡쳐 기능이 필요해서 오동님 블로그를 참조해서 사용하다가 리눅스(Qt)에서도 사용하게 될 일이 있어서 수정한 거 올립니다. 확인 결과 MFC, Win32 API 에서 문제 없이 잘 동작하며 리눅스에의 확인은 제 친구가 했는 데 리눅스에서도 문제 없이 잘 동작한다고 하네요.(비트맵 관련 구조체가 없어서 구조체는 직접 추가했다고 함)

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
void ScreenCapture( const char *strFilePath )
{
    //비트맵 파일 처리를 위한 헤더 구조체
    BITMAPFILEHEADER    BMFH;
    BITMAPINFOHEADER    BMIH;
 
    int nWidth = 0;
    int nHeight = 0;
    unsigned long dwQuadrupleWidth = 0;     //LJH 추가, 가로 사이즈가 4의 배수가 아니라면 4의 배수로 만들어서 저장
 
    GLbyte *pPixelData = NULL;              //front buffer의 픽셀 값들을 얻어 오기 위한 버퍼의 포인터
 
#ifdef WIN32
    //윈도우의 클라이언트 영역 좌표
    RECT ImageRect;
    GetClientRect( *m_hWndCopy, &ImageRect );          
 
    //이미지 영역 좌표를 이용하여 실제 이미지의 사이즈를 계산
    nWidth  = ImageRect.right - ImageRect.left;     //윈도우 버전의 경우 사이즈 변경이 되므로 그때그때 조사
    nHeight = ImageRect.bottom - ImageRect.top;
 
#else
    nWidth  = 1024;     //(나의 경우)리눅스에서의 경우 해상도 고정이므로 그 값을 입력
    nHeight = 768;
 
#endif
 
    //4의 배수인지 아닌지 확인해서 4의 배수가 아니라면 4의 배수로 맞춰준다.
    dwQuadrupleWidth = ( nWidth % 4 ) ? ( ( nWidth ) + ( 4 - ( nWidth % 4 ) ) ) : ( nWidth );
 
    //비트맵 파일 헤더 처리
    BMFH.bfType  = 0x4D42;      //B(42)와 M(4D)에 해당하는 ASCII 값을 넣어준다.
    //바이트 단위로 전체파일 크기
    BMFH.bfSize  = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + ( dwQuadrupleWidth * 3 * nHeight );
    //영상 데이터 위치까지의 거리
    BMFH.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
 
    //비트맵 인포 헤더 처리
    BMIH.biSize             = sizeof( BITMAPINFOHEADER );       //이 구조체의 크기
    BMIH.biWidth            = nWidth;                           //픽셀 단위로 영상의 폭
    BMIH.biHeight           = nHeight;                          //영상의 높이
    BMIH.biPlanes           = 1;                                //비트 플레인 수(항상 1)
    BMIH.biBitCount         = 24;                               //픽셀당 비트수(컬러, 흑백 구별)
    BMIH.biCompression      = BI_RGB;                           //압축 유무
    BMIH.biSizeImage        = dwQuadrupleWidth * 3 * nHeight;   //영상의 크기
    BMIH.biXPelsPerMeter    = 0;                                //가로 해상도
    BMIH.biYPelsPerMeter    = 0;                                //세로 해상도
    BMIH.biClrUsed          = 0;                                //실제 사용 색상수
    BMIH.biClrImportant     = 0;                                //중요한 색상 인덱스
 
    pPixelData = new GLbyte[ dwQuadrupleWidth * 3 * nHeight ];  //LJH 수정
 
    //프런트 버퍼로 부터 픽셀 정보들을 얻어온다.
    glReadPixels(
        0, 0,                   //캡처할 영역의 좌측상단 좌표
        nWidth, nHeight,        //캡처할 영역의 크기
        GL_BGR,                 //캡처한 이미지의 픽셀 포맷
        GL_UNSIGNED_BYTE,       //캡처한 이미지의 데이터 포맷
        pPixelData              //캡처한 이미지의 정보를 담아둘 버퍼 포인터
        );
 
    {//저장 부분
        FILE *outFile = fopen( strFilePath, "wb" );
        if( outFile == NULL )
        {
            //에러 처리
            //printf( "에러" );
            //fclose( outFile );
        }
 
        fwrite( &BMFH, sizeof( char ), sizeof(BITMAPFILEHEADER), outFile );         //파일 헤더 쓰기
        fwrite( &BMIH, sizeof( char ), sizeof(BITMAPINFOHEADER), outFile );         //인포 헤더 쓰기
        fwrite( pPixelData, sizeof( unsigned char ), BMIH.biSizeImage, outFile );   //glReadPixels로 읽은 데이터 쓰기
 
        fclose( outFile );  //파일 닫기
    }
 
    if ( pPixelData != NULL )
    {
        delete pPixelData;
    }

 

아래는 위의 함수로 저장한 bmp파일들입니다.

 

 

 

 

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

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

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

 

 

 

 

 

 

출처: http://blog.secmem.org/381

 

 

안녕하세요.

신촌멤버십 22-2기 김형진입니다.

이번에는 지난 글에 이어서 Windows API를 이용하여 다른 윈도우를 캡쳐하는 두 가지 방법을 소개하겠습니다.

C++ 기준으로 작성하였으며, C# 등 Windows API를 사용 가능한 언어에서도 같은 방법으로 적용 가능합니다.

 

1. 첫 번째 방법 : DC

먼저 다른 윈도우의 DC(Device Context)를 통해 얻어오는 방법입니다.

DC를 얻는 방법은 간단합니다내가 만든 프로그램의 경우는 WM_PAINT 메시지에서 BeginPaint 함수로 얻을 수도 있으며, WM_PAINT가 아니거나 다른 프로그램의 경우 HWND값을 이용하여 GetDC만 호출하면 얻어올 수 있습니다.

사용법은 간단합니다.

 

HWND hwndCalc = FindWindow(NULL, "계산기");
HDC hdcCalc = GetDC(hwndCalc);

 

이렇게 하면 hdcCalc 변수에는 계산기의 DC값이 저장됩니다.

하지만여기서 얻은 DC는 Client Area만의 DC입니다화면에 무엇인가를 그릴 수 있는 영역뿐타이틀바경계선메뉴바가 제외된 영역을 얻어오게 됩니다.

따라서윈도우의 DC를 얻기 위해서는 GetDCEx 함수나 GetWindowDC 함수를 이용해야 합니다. GetDCEx는 윈도우 외에도 DC를 얻는 여러 다양한 방법을 제시하고 있지만 사용이 복잡합니다하지만 GetWindowDC는 GetDC와 용법이 같아 다음과 같이 사용하기만 하면 됩니다.

 

HWND hwndCalc = FindWindow(NULL, "계산기");
HDC hdcCalc = GetWindowDC(hwndCalc);

 

DC를 얻었다면 이를 캡쳐하는 방법은 간단합니다.

바로 DC간에 고속으로 복사하는 BitBlt, StretchBlt 함수를 이용하면 됩니다이 중 StretchBlt 함수는 그림을 확대/축소하는 기능이 포함되어 있으므로일반적으로는 BitBlt 함수를 이용합니다.

또한 BitBlt, StretchBlt 함수는 비트 연산 기능이 포함되어 있지만단순 복사를 위해서는 마지막 인자인 Flag를 SRCCOPY로 넘겨 주면 됩니다.

 

하지만 복사하기 위해 알아야 하는 또 다른 함수가 있는데바로 GetWindowRect 함수입니다이유는 바로 계산기의 창 크기를 알기 위해서입니다. RECT 구조체는 left/right/top/bottom으로 구성되어 사각형으로 된 어떤 객체의 좌표를 담을 수 있는데이 구조체에 내가 원하는 윈도우의 크기를 얻어오는 함수가 GetWindowRect입니다. HWND 값을 이용하여 다음과 같이 사용할 수 있습니다.

RECT rectCalc;
GetWindowRect(hWndCalc, &rectCalc);

 

따라서 WM_PAINT 메시지 내에서 계산기를 캡쳐하여 화면에 그리기 위해서는 다음과 같이 구현할 수 있습니다.

 

HDC hDC;

PAINTSTRUCT ps;

HWND hWndCalc;

HDC hDCCalc;

RECT rectCalc;

(중략)

case WM_PAINT:

hDC = BeginPaint(hWnd, &ps);

hWndCalc = FindWindow(NULL, "계산기");

if(hWndCalc!=NULL) {

GetWindowRect(hWndCalc, &rectCalc);

hDCCalc = GetWindowDC(hWndCalc);

BitBlt(hDC, 0, 0, rectCalc.right, rectCalc.bottom, hDCCalc, 0, 0, SRCCOPY);

ReleaseDC(hWndCalc, hDCCalc);

}

EndPaint(hWnd, &ps);

break;

 

결과는 다음과 같습니다.

 

 

 

 

만약 FindWindow 대신 GetDesktopWindow() 라는 인자 없는 함수를 호출하여 그 리턴값을 똑같이 동작시킨다면이는 바탕 화면 전체를 캡쳐하는 것입니다즉 이는 다음과 같이 구현됩니다.

case WM_PAINT:

hDC = BeginPaint(hWnd, &ps);

hWndDesktop = GetDesktopWindow();

if(hWndDesktop!=NULL) {

GetWindowRect(hWndDesktop, &rectDesktop);

hDCDesktop = GetWindowDC(hWndDesktop);

BitBlt(hDC, 0, 0, rectDesktop.right, rectDesktop.bottom, hDCDesktop, 0, 0, SRCCOPY);

ReleaseDC(hWndDesktop, hDCDesktop);

}

EndPaint(hWnd, &ps);

break;

 

참고로 여기서 얻은 다른 윈도우의 DC를 이용해 다른 윈도우에 그림을 그리는 행동도 가능합니다즉 위의 hDCCalc 변수를 이용해 GetWindowDC와 ReleaseDC 사이에서 다음과 같이 호출해준다면 계산기 위에 동그라미가 그려지게(!) 됩니다물론 다음 화면에서 알 수 있듯이 타이틀바 및 경계선 영역에는 그려지지 않습니다.

Ellipse(hDCCalc, 10, 10, 300, 300);

 

 

반응형

 

728x90

 

 

 

2. 두 번째 방법 : PrintWindow

다른 윈도우의 DC를 얻어올 필요 없이단 하나의 함수 호출만으로 윈도우의 내용을 그대로 DC에 그릴 수 있습니다바로 PrintWindow라고 하는 함수입니다. PrintWindow 함수의 원형은 다음과 같습니다.

 

BOOL PrintWindow(

HWND hwnd,

HDC hdcBlt,

UINT nFlags

);

 

hwnd는 핸들, hdcBlt는 그릴 대상 DC, 즉 BeginPaint나 GetDC로 얻은 핸들을 말하며세 번째 인자는 NULL 또는 PW_CLIENTONLY를 넘길 수 있는데 PW_CLIENTONLY를 선택하면 클라이언트 영역만 그려지게 됩니다또한 이 함수를 이용하면 크기를 얻어오거나 하는 작업 없이 손쉽게 창을 얻어올 수 있다는 장점이 있습니다.

PrintWindow를 쓰는 경우 위의 코드가 단 몇 줄로 끝나게 됩니다.

 

hWndCalc = FindWindow(NULL, "계산기");

if(hWndCalc!=NULL) {

PrintWindow(hWndCalc, hDC, 0);

}

 

즉 위와 같은 동작이 이 함수 하나로 끝납니다.

만약 원하는 좌표에 캡쳐하기 위해서는 Bitmap을 저장하기 위한 MemDC를 만들고 여기를 타겟으로 하여 PrintWindow 함수를 호출합니다.

PrintWindow 함수는 데스크톱 화면(GetDesktopWindow로 얻은 핸들)에 대해서는 캡쳐되지 않습니다.

 

PrintWindow 함수가 간편한 만큼 몇 가지 장단점이 존재합니다.

첫째, PrintWindow 역시 DC에서 가져오는 원리는 비슷하나내부적으로 저장되어 있는 메모리 내용을 가져오기 때문에 화면에 가려진 영역이 있어도 정상적으로 출력됩니다이는 DC를 이용한 방법이 화면에 보이지 않는 영역을 정상적으로 출력하지 못하는 것과 대비됩니다화면에 보이지 않는다 하더라도 단지 다른 창에 가려진 경우에는 정상적으로 보이지만화면 가장자리로 가져가서 숨기거나 한 경우 DC에서 가져오는 방식으로는 정상적으로 출력되지 않습니다.

둘째, PrintWindow 함수는 다소 느린 단점이 있습니다예를 들어 동영상을 재생하는 창을 가져와 타이머를 이용하여 수시로 캡쳐하는 경우, DC를 이용하는 경우에는 잘 출력되지만, PrintWindow 함수를 이용하면 시스템 자원을 상당히 많이 사용하며 또한 원본 창에 무리를 주어 원본 창을 드래그할 수 없는 경우가 발생하기도 합니다.

 

 

 

3. 캡쳐된 이미지와 실제 창 모양이 왜 다를까?

다음 두 화면을 보면 캡쳐된 그림과 실제 화면이 다른 것을 알 수 있습니다실질적인 창 내용은 같지만테마가 적용되어 있는지 여부만 서로 다릅니다.이 차이가 발생하는 이유는바로 여기서 하는 캡쳐는 OS상에서 자체적으로 저장한 메모리 장치 컨텍스트의 이미지를 가져오는 것이지엄밀히 말해 화면에 보여지는 실제 창 모양을 캡쳐하는 것이 아니기 때문에, Windows의 자체 효과가 지정된 화면이 출력되지 않는 것입니다

 

 

 

 

비스타 이상에서 작업 표시줄에 마우스를 가져다 댔을 때 나오는 작은 이미지에 타이틀바가 나오지 않도록 되어 있는 이유도효과가 적용된 채 캡쳐되지 않기 때문에일부러 클라이언트 영역만 보이도록 저장된 것입니다.

 

 

 

 

하지만 GetDesktopWindow 함수를 이용하여 화면 전체를 캡쳐하는 경우에는 효과가 적용된 채로 출력됩니다.

따라서 만약 구현 후 캡쳐된 화면이 이질적이라서 거슬린다면, Windows 효과(Aero )를 프로그램 실행시에 끄도록 하는 방법을 통해 캡쳐화면과 실제화면을 일치시킬 수 있습니다.

 

 

 

4. 최소화된 창은 어떻게 캡쳐할까?

기본적으로는 최소화된 창은 캡쳐할 수 없습니다하지만 다음과 같이최소화 애니메이션을 없애고 윈도우를 임시로 투명하게 처리한 뒤캡쳐하는 순간에만 창을 원래 크기로 돌리는 방법이 있습니다다음 게시물 및 소스를 참조하면 자세하게 확인하실 수 있습니다.

http://www.codeproject.com/Articles/20651/Capturing-Minimized-Window-A-Kid-s-Trick

 

 

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

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

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

 

 

 

 

 

출처: http://dsnight.tistory.com/49

 

 

CDC memDC; CBitmap bitmap; 	 int cx, cy;	// 화면의 크기를 구하기 위해서 // 주 모니터 화면 cx = GetSystemMetrics(SM_CXSCREEN); cy = GetSystemMetrics(SM_CYSCREEN); // 듀얼 모니터 사용시 전체화면 //cx = GetSystemMetrics(SM_CXVIRTUALSCREEN); //cx = GetSystemMetrics(SM_CYVIRTUALSCREEN);  CClientDC dc(this);		// dc선언해주고 (this)에 화면을 출력하기 위해선언 CClientDC ScreenDC(GetDesktopWindow());	// 스크린DC 선언 memDC.CreateCompatibleDC(&ScreenDC);	// 스크린DC와 호환되는 DC생성 bitmap.CreateCompatibleBitmap(&ScreenDC, cx, cy);	// 스크린DC와 호환되는 비트맵 생성 CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);	// Bitmap 포인터를 넘겨줌 memDC.StretchBlt(0, 0, cx, cy, &ScreenDC, 0, 0, cx, cy, SRCCOPY);	// 스크린DC에 저장된화면을 memDC에 copy, bitmap에도 기록됨  // 출력 dc.BitBlt(0, 0, cx, cy, &memDC, 0, 0, SRCCOPY);	// 0,0 부터 해상도크기까지 memDC가 가리키는 참조값 0, 0부터 복사해서 출력함 	 memDC.SelectObject(pOldBitmap);	// 본래의 비트맵 복구?? 


위 소스를 이용하여 컴퓨터 화면을 캡쳐할 수 있다.

화면을 동영상처럼 캡쳐를 원할경우 Timer를 사용해서 1초에 약 30번 주기로 캡쳐를 하면 된다.

 

주석이 많기때문에 설명은 생략, 에뮬로 실행한 스마트폰 화면을 찍고싶어서 만들어봤다.스마트폰 화면을 캡쳐해서 영상처리를 해봐야겠다.

 

 

 

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

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

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

 

 

 

 

 

출처: http://jjjryu.tistory.com/entry/%EC%BB%B4%ED%93%A8%ED%84%B0-%ED%99%94%EB%A9%B4-%EC%BA%A1%EC%B3%90-Win32

 

 

void SetCapture(CRect &rc)
{
        CDC ScreenDC;
        ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);
        CDC memDC;
        CSize s = CSize(rc.Size());
        CBitmap bm;

        memDC.CreateCompatibleDC(&ScreenDC);
        bm.CreateCompatibleBitmap(&ScreenDC, s.cx, s.cy);
        memDC.SelectObject(&bm);
        memDC.BitBlt(0,0,s.cx, s.cy,&ScreenDC, rc.left, rc.top,SRCCOPY);

        HBITMAP hbit = (HBITMAP)bm;

        if (hbit == NULL)
            ::MessageBox(NULL, "bmp파일 생성이 실패하였습니다.", "CapAndShow", MB_ICONSTOP);
        else
        {
            BITMAPFILEHEADER fh;
            BITMAPINFOHEADER ih;
            BITMAP bit;
            BITMAPINFO *pih;
            int PalSize;
            HANDLE hFile;
            DWORD dwWritten, Size;
            HDC hdc;

            // 전체화면에 대한 DC를 구한다.
            hdc = ::GetDC(NULL);

            // 비트맵 정보로 부터 정보 구조체를 초기화한다.
            GetObject(hbit, sizeof(BITMAP), &bit);
            ih.biSize            = sizeof(BITMAPINFOHEADER);
            ih.biWidth            = bit.bmWidth;
            ih.biHeight            = bit.bmHeight;
            ih.biPlanes            = 1;
            ih.biBitCount        = bit.bmPlanes * bit.bmBitsPixel;
            if(ih.biBitCount > 8) ih.biBitCount = 24;
            ih.biCompression    = BI_RGB;
            ih.biSizeImage        = 0;
            ih.biXPelsPerMeter    = 0;
            ih.biYPelsPerMeter    = 0;
            ih.biClrUsed        = 0;
            ih.biClrImportant    = 0;

            // 정보 구조체 + 팔레트 크기만큼 메모리를 할당하고 이 버퍼에 정보 구조체를 복사한다.
            PalSize = (ih.biBitCount==24 ? 0:1 << ih.biBitCount) * sizeof(RGBQUAD);
            pih = (BITMAPINFO*)malloc(ih.biSize + PalSize);
            pih->bmiHeader = ih;

            // 비트맵의 크기를 구한다.
            GetDIBits(hdc, hbit, 0, bit.bmHeight, NULL, pih, DIB_RGB_COLORS);
            ih = pih->bmiHeader;

            // 비트맵의 크기가 구해지지 않았을 경우 수작업으로 직접 계산한다.
            if(ih.biSizeImage==0) {
                ih.biSizeImage = ((((ih.biWidth*ih.biBitCount)+31) & ~31) >> 3) * ih.biHeight;
            }

            // 래스터 데이터를 읽기위해 메모리를 재할당한다.
            Size = ih.biSize + PalSize + ih.biSizeImage;
            pih = (BITMAPINFO *) realloc(pih, Size);

            // 래스터 데이터를 읽어들인다.
            GetDIBits(hdc, hbit, 0, bit.bmHeight, (PBYTE)pih+ih.biSize+PalSize, pih, DIB_RGB_COLORS);

            // 파일 헤더를 만든다.
            fh.bfOffBits    = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PalSize;
            fh.bfReserved1    = 0;
            fh.bfReserved2    = 0;
            fh.bfSize        = Size+sizeof(BITMAPFILEHEADER);
            fh.bfType        = 0x4d42;

            // 파일을 생성하고 파일 헤더와 정보 구조체, 팔레트, 래스터 데이터를 출력한다.
            char path[200];
            sprintf(path,"d:\\test%d.bmp", m_iSeq);

            hFile = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            WriteFile(hFile, &fh, sizeof(fh), &dwWritten, NULL);
            WriteFile(hFile, pih, Size, &dwWritten, NULL);

            ::ReleaseDC(NULL, hdc);
            CloseHandle(hFile);

            // 메모리 반환
            //free(pih);
            DeleteObject(hbit);
        }

        bm.DeleteObject();
}


참조 사이트:

 

 

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

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

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

 

 

 

 

 

출처: http://miffyzzang.tistory.com/467

 

 

활성화된 화면 캡쳐 ScreenCapture

 

 

HBITMAP ScreenCapture(HWND hWnd)

{

 

HDC hScrDC, hMemDC;

HBITMAP hBitmap;

RECT rt;

 

GetWindowRect(GetForegroundWindow(), &rt);

// GetWindowRect(GetActiveWindow(), &rt);

 

rt.left = max(0, rt.left);

rt.right = min(rt.right, GetSystemMetrics(SM_CXSCREEN));

rt.top = max(0, rt.top);

rt.bottom = min(rt.bottom, GetSystemMetrics(SM_CYSCREEN));

 

hScrDC=CreateDC("DISPLAY",NULL,NULL,NULL);

 

hMemDC = CreateCompatibleDC(hScrDC);

 

hBitmap = CreateCompatibleBitmap(hScrDC, rt.right-rt.left, rt.bottom-rt.top);


SelectObject(hMemDC, hBitmap);

 

BitBlt(hMemDC, 0, 0, rt.right-rt.left, rt.bottom-rt.top, hScrDC, rt.left, rt.top, SRCCOPY);

 

DeleteDC(hMemDC);

DeleteDC(hScrDC);

InvalidateRect(hWnd, NULL, TRUE);

 

return hBitmap;

}

 

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

위의 함수를 호출하여 사용한다

핫키를 등록하여 Ctrl + F4가 눌리면 화면 캡쳐!

WM_PAINT에서 캡쳐된 화면을 그려줌 

 

 

case WM_CREATE:

RegisterHotKey(hWnd, 0, MOD_CONTROL, VK_F4);

break;

 

 

case WM_HOTKEY:

hBit = ScreenCapture(hWnd); // CTRL +F4

return 0;

 

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

if(hBit != NULL) 

{

hMemDC = CreateCompatibleDC(hdc);

SelectObject(hMemDC, hBit);

GetObject(hBit, sizeof(BITMAP), &bmp);

BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hMemDC, 0, 0, SRCCOPY);

DeleteDC(hMemDC);

}

EndPaint(hWnd, &ps);

 

return 0;

 

 

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

 

 

 

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

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

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

 

 

 

 

 

출처: http://www.hoons.net/board/qacshap/content/25881

 

 

[RE] 윈도우의 활성화된창( 지금 사용중인창 )을 캡쳐

 

1. Win32 api이용, Active Window의 handle을 찾음.
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

2. Win32 api이용, window handle값으로, 창 정보를 읽어옴.
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);

3. screen에서 capture
      Graphics 의  CopyFromScreen 메서드 이용하여 Bitmap으로 capture
     Image or Bitmap 의 Save메서드를 이용하여, 파일로 저장
     Bimap b = new Bitmap(rect.Width, rect.Height);
     Graphics g = Graphics.FromImage(b);
     g.CopyFromScreen(startPoint, Point.Empty, rect.Size);
 
    b.Save(sFile)....
 
1,2,3순으로 하면 간단히 캡쳐 저장됩니다. 글에 오타 있을테니 차근차근 해보세요.
CopyFormScreen의 desctop전체 크기넣으면 전체화면 캡쳐가 됩니다.
 

 

 

 

 

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

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

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

 

 

 

 

 

 

 

출처: http://lesomnus.tistory.com/10

 

Intro

템 파밍을 위해 잠수를 하는데, 젠이 됐는지 안됐는지 계속 확인하는게 비효율적이었다. 화면을 주기적으로 캡처하고 몹이 인식되면 알림을 띄우기 위해 이러한 삽질을 하게 되었다. 사실 캡처 자체는 문제가 아니었는데, 캡처할 대상이 '비활성 window'에다가 게임은 DiretX로 렌더링 되는 화면이었고, 이는 캡처 시 검은 화면만을 표시한다.

Try#1

사실 WinAPI를 이용한 프로그래밍 조차 처음이었고, 당연히 구글링부터 시작했다. 일단 이 글을 참고했다. 프로그래머라면 일단 Copy&Paste가 기본이 아닌가.1

 

public static Bitmap PrintWindow(IntPtr hwnd) {
    RECT rc;
    WinUserApi.GetWindowRect(hwnd, out rc);
 
    Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
    Graphics gfxBmp = Graphics.FromImage(bmp);
    IntPtr hdcBitmap = gfxBmp.GetHdc();
    bool succeeded = WinUserApi.PrintWindow(hwnd, hdcBitmap, 0);
    gfxBmp.ReleaseHdc(hdcBitmap);
    if(!succeeded) {
        gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
    }
    IntPtr hRgn = WinGdiApi.CreateRectRgn(0, 0, 0, 0);
    WinUserApi.GetWindowRgn(hwnd, hRgn);
    Region region = Region.FromHrgn(hRgn);
    if(!region.IsEmpty(gfxBmp)) {
        gfxBmp.ExcludeClip(region);
        gfxBmp.Clear(Color.Transparent);
    }
    gfxBmp.Dispose();
    return bmp;
}



출처: http://lesomnus.tistory.com/10 [Dev. Somnus]

 

 

 

되지 않으리라 예상은 했지만, 컴파일조차 되지 않을 줄은 정말 몰랐다. RECT라는 구조체부터

 빨간 밑줄이 표시되었다. 도대체 WinUserApi란 무엇인가. 어쩔 수 없이 본문을 읽어보았고,

 본문 첫 줄에 PrintWindow라는 단서를 얻을 수 있었다. MSDN으로 연결되었고, 예제는 C++였다. 

작성자는 왜 C#으로 답변을 해주었는가?

Try#2

여러 의문을 남겨둔 채, PrintWindow함수에 대해 검색을 하였다. C#으로 작성된 코드를

 이용하여야 하므로 관련된 검색을 했고 여기에서 해답을 찾을 수 있었다.

 

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);



출처: http://lesomnus.tistory.com/10 [Dev. Somnus]

 

<Try#1>의 WinUserApi는 user32.dll을 말하는 것임을 알 수 있게 되었다. 똑같이 gdi32.dll이 그러하다. 

이제 RECT와 PixelFormat을 찾아주면 된다. RECT의 이름이나 쓰임새로 보아선 Rectangle의 약자인 것 같다. 

구글에 검색하자, MSDN Rectangle 구조체 문서가 최상단에 노출되었다. PixelFormat 문서도 쉽게 찾을 수 있었다.

자, 빨간 밑줄을 모두 없애 주었고, 두근거리는 마음으로 컴파일하고 실행을 했지만 런타임 에러가 발생하였다. GetWindowRect라는 놈이 문제였는데, 이 함수의 용도는 window 크기 정보를 알아내는게 전부였다. 단순 구조체라고 생각했던 RECT가 생각만큼 단순한 녀석이 아니었나 보다. 크기 정보를 얻을 수 있는 다른 방법이 없는가 알아보던 중, Graphics 클래스 문서에서 HWND로 정보를 가져올 수 있는 메소드를 찾았다.

Try#3

public static void PrintWindow(IntPtr hwnd) {
    Rectangle rc = Rectangle.Empty;
    Graphics gfxWin = Graphics.FromHwnd(hwnd);
    rc = Rectangle.Round(gfxWin.VisibleClipBounds);
 
    Bitmap bmp = new Bitmap(
        rc.Width, rc.Height,
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
    Graphics gfxBmp = Graphics.FromImage(bmp);
    IntPtr hdcBitmap = gfxBmp.GetHdc();
    bool succeeded = WinAPIs.User32.PrintWindow(hwnd, hdcBitmap, 1);
    gfxBmp.ReleaseHdc(hdcBitmap);
    if(!succeeded) {
        gfxBmp.FillRectangle(
            new SolidBrush(Color.Gray),
            new Rectangle(Point.Empty, bmp.Size));
    }
    IntPtr hRgn = WinAPIs.Gdi32.CreateRectRgn(0, 0, 0, 0);
    WinAPIs.User32.GetWindowRgn(hwnd, hRgn);
    Region region = Region.FromHrgn(hRgn);
    if(!region.IsEmpty(gfxBmp)) {
        gfxBmp.ExcludeClip(region);
        gfxBmp.Clear(Color.Transparent);
    }
    gfxBmp.Dispose();
    bmp.Save("c:\\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
}



출처: http://lesomnus.tistory.com/10 [Dev. Somnus]

 

 

 

 

아주 좋다. Window 메뉴바도 함께 찍히는데, 필요한 정보가 아니므로, MSDN 문서에 의하면, 12번 줄의 LParam값으로 PW_CLIENTONLY주면 클라이언트 화면만 캡쳐한다. 현재는 정의된 값이 아니므로, 0x1로 하였다.

하지만 StackOverflow글의 본문에도 적혀있듯이, DirectX 렌더링 화면은 캡쳐하지 못하는것을 확인하였다. 하지만 답글에 달린 댓글에 놀라운 정보가 있었다.

Solved

"it works with pretty much every window except for Direct X / WPF windows." - New flag may help. According to Dave Anderson's comment (msdn.microsoft.com/en-us/library/dd162869.aspx), the PW_RENDERFULLCONTENT flag on Win8.1+ works with "windows that use DirectX or DirectComposition".

Dwayne Robinson Jun 19 '15 at 7:41

링크를 따라가봤지만, PW_RENDERFULLCONTENT같은 플래그는 없었다. 혹시나 싶어 플래그로 0x20x3을 넣어봤는데, DirectX 렌더링 화면이 매우 잘 캡쳐되었다!

 

 

행인2016.04.27 01:36 신고   

해당플래그는 윈도우7에서는 작동안하는거지요?

Somnus2016.06.21 20:24 신고   

윈도우 10에서 테스트 해봤는데 7은 잘모르겠군요...

jonyon2016.07.31 22:16 신고   

아주 훌륭한 삽질이군여 이건 캡쳐를 했다가 나중에 써먹어야할듯

대박~2016.12.31 16:19 신고   

감사합니다. 캡쳐가 정말 되네요!!!!

yjngh2017.03.20 09:29 신고   

감사합니다.
정말 좋은 정보네요

yjngh2017.03.20 09:30 신고  

회사컴퓨터에 테스트 해봤는데 윈도우7은 안되더군요..아마 다이렉트 x 버전때문인듯 합니다.

크흠2017.04.13 16:00 신고   

저기 마지막 스택오버플로우 답변에 "...PW_RENDERFULLCONTENT flag on Win8.1+ works..." 

라는걸 보니 윈도우 8이상만 되는것 같네요

wjdwjd2017.04.21 10:51 신고   

현재 c#으로 가려진 윈도우 창을 캡처하고싶어 공부하고있는데 
궁금한점이 있습니다. 
WinAPIs.User32.PrintWindow(hwnd, hdcBitmap, 1);

WinAPIs.Gdi32.CreateRectRgn(0, 0, 0, 0);
이 코드에서 WinAPIs.User32 와 WinAPIs.Gdi32. 부분에서 정의가 포함되어 있지 않다고 오류가 납니다. 

따로 선언해줄것이 필요한건지 혹은 추가해야하는 부분인지 궁금합니다. 

ko9ma2017.04.26 15:18 신고   

감사합니다 ^__^



출처: http://lesomnus.tistory.com/10 [Dev. Somnus]

 

 

 

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

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

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

 

 

 

 

 

반응형
그리드형


관련글 더보기

댓글 영역