以前一直用屏幕DC来拷贝屏幕图象数据,然后构造位图,这种方法系统资源占用高,速度也比较慢。
请教一种高效屏幕截取(比如DirectX或者其他)的实现方法。
加500分!!!
谢谢各位大侠!

解决方案 »

  1.   

    我看到过似乎用GetCurrentImage函数的,不知道可不可以
      

  2.   

    谢谢老兄先
    大致每秒截取2次,同时CPU占用率不超过15%。
    如能解决,兄弟别无长物,奉上1000分
      

  3.   

    转贴,希望可以给你点帮助屏幕截取是令人比较感兴趣的事情.虽然现在有不少应用程序如HYPERSNAP等可以用来截取你所喜欢的屏幕画面,但是如果能把这个功能加到自己的程序中,就更能利用它强大的作用. ---- 下面用VC来逐步介绍在Windows95下的实现过程.首先我们要确定屏幕截取的区域,用LPRECT结构来定义.可以截取一个窗口,或整个屏幕.以下代码把选定的屏幕区域拷贝到位图中. HBITMAP CopyScreenToBitmap(LPRECT lpRect)
    //lpRect 代表选定区域
    {
    HDC       hScrDC, hMemDC;      
    // 屏幕和内存设备描述表
    HBITMAP    hBitmap, hOldBitmap;   
    // 位图句柄
    int       nX, nY, nX2, nY2;      
    // 选定区域坐标
    int       nWidth, nHeight;      
    // 位图宽度和高度
    int       xScrn, yScrn;         
    // 屏幕分辨率
    // 确保选定区域不为空矩形
    if (IsRectEmpty(lpRect))
    return NULL;
    //为屏幕创建设备描述表
    hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);
    //为屏幕设备描述表创建兼容的内存设备描述表
    hMemDC = CreateCompatibleDC(hScrDC);
    // 获得选定区域坐标
    nX = lpRect- >left;
    nY = lpRect- >top;
    nX2 = lpRect- >right;
    nY2 = lpRect- >bottom;
    // 获得屏幕分辨率
    xScrn = GetDeviceCaps(hScrDC, HORZRES);
    yScrn = GetDeviceCaps(hScrDC, VERTRES);
    //确保选定区域是可见的
    if (nX 〈0)
    nX = 0;
    if (nY 〈  0)
    nY = 0;
    if (nX2 > xScrn)
    nX2 = xScrn;
    if (nY2 > yScrn)
    nY2 = yScrn;
    nWidth = nX2 - nX;
    nHeight = nY2 - nY;
    // 创建一个与屏幕设备描述表兼容的位图
    hBitmap = CreateCompatibleBitmap
    (hScrDC, nWidth, nHeight);
    // 把新位图选到内存设备描述表中
    hOldBitmap = SelectObject(hMemDC, hBitmap);
    // 把屏幕设备描述表拷贝到内存设备描述表中
    BitBlt(hMemDC, 0, 0, nWidth, nHeight,
    hScrDC, nX, nY, SRCCOPY);
    //得到屏幕位图的句柄
    hBitmap = SelectObject(hMemDC, hOldBitmap);
    //清除 
    DeleteDC(hScrDC);
    DeleteDC(hMemDC);
    // 返回位图句柄
    return hBitmap;
    }
    得到屏幕位图句柄以后,我们
    可以把屏幕内容粘贴到剪贴板上.
    if (OpenClipboard(hWnd)) 
    //hWnd为程序窗口句柄
    {
    //清空剪贴板
    EmptyClipboard();
    //把屏幕内容粘贴到剪贴板上,
    hBitmap 为刚才的屏幕位图句柄
    SetClipboardData(CF_BITMAP, hBitmap);
    //关闭剪贴板
    CloseClipboard();
    }
    我们也可以把屏幕内容以位图格式存到磁盘文件上.
    int SaveBitmapToFile(HBITMAP hBitmap , 
    LPSTR lpFileName) //hBitmap 为刚才的屏幕位图句柄
    {      //lpFileName 为位图文件名
    HDC            hDC;         
    //设备描述表
    int            iBits;      
    //当前显示分辨率下每个像素所占字节数
    WORD            wBitCount;   
    //位图中每个像素所占字节数
    //定义调色板大小, 位图中像素字节大小 ,
    位图文件大小 , 写入文件字节数
    DWORD           dwPaletteSize=0,
    dwBmBitsSize,
    dwDIBSize, dwWritten;
    BITMAP          Bitmap;        
    //位图属性结构
    BITMAPFILEHEADER   bmfHdr;        
    //位图文件头结构
    BITMAPINFOHEADER   bi;            
    //位图信息头结构 
    LPBITMAPINFOHEADER lpbi;          
    //指向位图信息头结构
    HANDLE          fh, hDib, hPal,hOldPal=NULL;
    //定义文件,分配内存句柄,调色板句柄
    //计算位图文件每个像素所占字节数
    hDC = CreateDC("DISPLAY",NULL,NULL,NULL);
    iBits = GetDeviceCaps(hDC, BITSPIXEL) * 
    GetDeviceCaps(hDC, PLANES);
    DeleteDC(hDC);
    if (iBits 〈 = 1)
    wBitCount = 1;
    else if (iBits 〈 = 4)
    wBitCount = 4;
    else if (iBits 〈  = 8)
    wBitCount = 8;
    else if (iBits 〈 = 24)
    wBitCount = 24;
    //计算调色板大小
    if (wBitCount 〈 = 8)
    dwPaletteSize = (1 〈 〈   wBitCount) *
    sizeof(RGBQUAD);
    //设置位图信息头结构
    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
    bi.biSize            = sizeof(BITMAPINFOHEADER);
    bi.biWidth           = Bitmap.bmWidth;
    bi.biHeight          = Bitmap.bmHeight;
    bi.biPlanes          = 1;
    bi.biBitCount         = wBitCount;
    bi.biCompression      = BI_RGB;
    bi.biSizeImage        = 0;
    bi.biXPelsPerMeter     = 0;
    bi.biYPelsPerMeter     = 0;
    bi.biClrUsed         = 0;
    bi.biClrImportant      = 0;
    dwBmBitsSize = ((Bitmap.bmWidth *
    wBitCount+31)/32)* 4
    *Bitmap.bmHeight ;
    //为位图内容分配内存
    hDib  = GlobalAlloc(GHND,dwBmBitsSize+
    dwPaletteSize+sizeof(BITMAPINFOHEADER));
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
    *lpbi = bi;
    // 处理调色板   
    hPal = GetStockObject(DEFAULT_PALETTE);
    if (hPal)
    {
    hDC  = GetDC(NULL);
    hOldPal = SelectPalette(hDC, hPal, FALSE);
    RealizePalette(hDC);
    }
    // 获取该调色板下新的像素值
    GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,
    (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
    +dwPaletteSize,
    (BITMAPINFOHEADER *)
    lpbi, DIB_RGB_COLORS);
    //恢复调色板   
    if (hOldPal)
    {
    SelectPalette(hDC, hOldPal, TRUE);
    RealizePalette(hDC);
    ReleaseDC(NULL, hDC);
    }
    //创建位图文件    
    fh = CreateFile(lpFileName, GENERIC_WRITE, 
    0, NULL, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL | FILE_
    FLAG_SEQUENTIAL_SCAN, NULL);
    if (fh == INVALID_HANDLE_VALUE)
    return FALSE;
    // 设置位图文件头
    bmfHdr.bfType = 0x4D42;  // "BM"
    dwDIBSize    = sizeof(BITMAPFILEHEADER) 
    + sizeof(BITMAPINFOHEADER)
    + dwPaletteSize + dwBmBitsSize;  
    bmfHdr.bfSize = dwDIBSize;
    bmfHdr.bfReserved1 = 0;
    bmfHdr.bfReserved2 = 0;
    bmfHdr.bfOffBits = (DWORD)sizeof
    (BITMAPFILEHEADER) 
    + (DWORD)sizeof(BITMAPINFOHEADER)
    + dwPaletteSize;
    // 写入位图文件头
    WriteFile(fh, (LPSTR)&bmfHdr, sizeof
    (BITMAPFILEHEADER), &dwWritten, NULL);
    // 写入位图文件其余内容
    WriteFile(fh, (LPSTR)lpbi, dwDIBSize, 
    &dwWritten, NULL);
    //清除   
    GlobalUnlock(hDib);
    GlobalFree(hDib);
    CloseHandle(fh);
    }
      

  4.   

    拜托,这不是我要的。
    我需要一种高效的方法,你的方法就是我希望改进的方法,我原来就是用的这种方法
    我看到可以用DirectX的GetCurrentImage函数来实现,但是不知道这种方法有什么局限性。
      

  5.   

    呵呵,你买个1000G的cpu,包你每秒2次,而且cpu资源不会在15%以上,呵呵
      

  6.   


    这种需求通过DC应该是做不到的,要考虑直接从显卡上捕获点阵信息,然后用mpeg进行
    流媒体编码,一般的远程控制软件都是这样做的。我还看过一款远程控制软件,专门还编了一个虚拟显卡,道理应该是一样的。
      

  7.   

    一秒两次写800*600,%15的要求,写位图应该是达不到的(这种方式最多也就是%30),也许你可以尝试一下写pcx或者jpg,在0.5秒中进行适当的Sleep也许可以?
      

  8.   

    用flinming(flinming)贴的代码的时候,这句应该怎么写那?
    if (OpenClipboard(hWnd)) 
    //hWnd为程序窗口句柄
      

  9.   

    感激,最近公司搬家,刚刚才可以上网!
    老大们
    我用了directx9的一个接口,实现了我的需求,但是这样的程序必须要求系统安装Directx9,这似乎太那个了,不知道directx7中是如何实现的?
    如果再过1周不能解决,我只有把这些分散给兄弟们了。
      

  10.   

    GetCurrentImage仅用于Video Renderer,不能用于Video Source
    你的问题,我想可以用全局钩子捕获非关键桢解决。
    捕获WM_PAINT之类的消息,然后标记目标窗口/区域是需要更新的,然后捕获需要更新的区域,与上一次捕获的结果比较,只复制变化增量。如果使用DirectX捕获到显存中的离屏表面,那么可能可以减少捕获时的CPU占用率参见www.uk.research.att.com/vnc/
      

  11.   

    使用虚拟显示驱动(mirror driver)捕获屏幕的话,的确可以大大减少CPU占用,但是这类程序必须为每个Windows版本编写一个驱动,开发的工作量大大增加。
      

  12.   

    谢谢各位,十分感激
    我很希望大家可以继续这个话题
    我可以保证发言的人至少50分,同时尤其感谢jiangsheng(蒋晟.Net)等大哥,非常感谢.
    大家不用为我担心,我有7000多的可用分.