Estoy tratando de encontrar una (un poco) de manera fácil tomar una captura de pantalla en la ventana de pruebas y guarda el resultado HBITMAP como un archivo JPEG. El truco aquí es que, dado que el código está en C que no puedo usar GDI+ y puesto que el código es un módulo para una mayor programa yo no puedo ni usar una externa lib (como libjpeg).

Este código toma una captura de pantalla y devuelve un HBITMAP. El ahorro de mapa de bits en un archivo es fácil. el problema es que el mapa de bits es de 2 o 3 mb.

HDC hDCMem = CreateCompatibleDC(NULL);
HBITMAP hBmp;
RECT rect;
HDC hDC;
HGDIOBJ hOld;    

GetWindowRect(hWnd, & rect);

hBmp = NULL;

{
    hDC = GetDC(hWnd);
    hBmp = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
    ReleaseDC(hWnd, hDC);
}

hOld = SelectObject(hDCMem, hBmp);
SendMessage(hWnd, WM_PRINT, (WPARAM) hDCMem, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);

SelectObject(hDCMem, hOld);
DeleteObject(hDCMem);

return hBmp;

ideas sobre cómo hacer esto?
muchas gracias, cualquier ayuda es muy apreciada

EDITAR:
Desde que entró en la dirección de GDI+ yo pensé que había puesto el código iv C++ que puede tomar la captura de pantalla y convertirlo a JPEG utilizando GDI+. Si alguien sabe cómo conseguir esto utilizando el PLANO de GDI+ agradecería la ayuda.
Código:

    #include <windows.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
int GetEncoderClsid(WCHAR *format, CLSID *pClsid)
{
unsigned int num = 0,  size = 0;
GetImageEncodersSize(&num, &size);
if(size == 0) return -1;
ImageCodecInfo *pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
if(pImageCodecInfo == NULL) return -1;
GetImageEncoders(num, size, pImageCodecInfo);
for(unsigned int j = 0; j < num; ++j)
{
if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0){
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}    
}
free(pImageCodecInfo);
return -1;
}
int GetScreeny(LPWSTR lpszFilename, ULONG uQuality) //by Napalm
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hMyWnd = GetForegroundWindow(); //get my own window
RECT  r;                             //the area we are going to capture 
int w, h;                            //the width and height of the area
HDC dc;                              //the container for the area
int nBPP;
HDC hdcCapture;
LPBYTE lpCapture;
int nCapture;
int iRes;
CLSID imageCLSID;
Bitmap *pScreenShot;
HGLOBAL hMem;
int result;
//get the area of my application's window  
//GetClientRect(hMyWnd, &r);
GetWindowRect(hMyWnd, &r);
dc = GetWindowDC(hMyWnd);//  GetDC(hMyWnd) ;
w = r.right - r.left;
h = r.bottom - r.top;
nBPP = GetDeviceCaps(dc, BITSPIXEL);
hdcCapture = CreateCompatibleDC(dc);
//create the buffer for the screenshot
BITMAPINFO bmiCapture = {
sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0,
};
//create a container and take the screenshot
HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture,
DIB_PAL_COLORS, (LPVOID *)&lpCapture, NULL, 0);
//failed to take it
if(!hbmCapture)
{
DeleteDC(hdcCapture);
DeleteDC(dc);
GdiplusShutdown(gdiplusToken);
printf("failed to take the screenshot. err: %d\n", GetLastError());
return 0;
}
//copy the screenshot buffer
nCapture = SaveDC(hdcCapture);
SelectObject(hdcCapture, hbmCapture);
BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY);
RestoreDC(hdcCapture, nCapture);
DeleteDC(hdcCapture);
DeleteDC(dc);
//save the buffer to a file    
pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Guid  = EncoderQuality;
encoderParams.Parameter[0].Type  = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].Value = &uQuality;
GetEncoderClsid(L"image/jpeg", &imageCLSID);
iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);
delete pScreenShot;
DeleteObject(hbmCapture);
GdiplusShutdown(gdiplusToken);
return iRes;
}
¿realmente tiene que ser un JPG? si un PNG es aceptar que usted podría utilizar libpng..
Sí, tiene que ser JPG. PNG no es lo suficientemente pequeña para lo que yo necesito. Además no puedo usar externo o la 3ª parte de libs. libpng es grande y añade un montón de código extra que no necesito (he probado esta ya) Gracias por la respuesta tho.
JPG no es buena opción para capturas de pantalla. Es mucho mejor para las fotos y como, pero en las imágenes PNG proporciona resultados mucho mejores. Usted puede hacer que sea más pequeña escala hacia abajo o de reducción de profundidad de bits de color.

OriginalEl autor wonderer | 2009-06-15

11 Comentarios

  1. 17

    ACEPTAR, después de mucho esfuerzo he aquí la respuesta:

    int SaveJpeg(HBITMAP hBmp, LPCWSTR lpszFilename, ULONG uQuality)
    {
    ULONG *pBitmap = NULL;
    CLSID imageCLSID;
    EncoderParameters encoderParams;
    int iRes = 0;
    typedef Status (WINAPI *pGdipCreateBitmapFromHBITMAP)(HBITMAP, HPALETTE, ULONG**);
    pGdipCreateBitmapFromHBITMAP lGdipCreateBitmapFromHBITMAP;
    typedef Status (WINAPI *pGdipSaveImageToFile)(ULONG *, const WCHAR*, const CLSID*, const EncoderParameters*);
    pGdipSaveImageToFile lGdipSaveImageToFile;
    //load GdipCreateBitmapFromHBITMAP
    lGdipCreateBitmapFromHBITMAP = (pGdipCreateBitmapFromHBITMAP)GetProcAddress(hModuleThread, "GdipCreateBitmapFromHBITMAP");
    if(lGdipCreateBitmapFromHBITMAP == NULL)
    {
    //error
    return 0;
    }
    //load GdipSaveImageToFile
    lGdipSaveImageToFile = (pGdipSaveImageToFile)GetProcAddress(hModuleThread, "GdipSaveImageToFile");
    if(lGdipSaveImageToFile == NULL)
    {
    //error
    return 0;
    }
    lGdipCreateBitmapFromHBITMAP(hBmp, NULL, &pBitmap);
    iRes = GetEncoderClsid(L"image/jpeg", &imageCLSID);
    if(iRes == -1)
    {
    //error
    return 0;
    }
    encoderParams.Count = 1;
    encoderParams.Parameter[0].NumberOfValues = 1;
    encoderParams.Parameter[0].Guid  = EncoderQuality;
    encoderParams.Parameter[0].Type  = EncoderParameterValueTypeLong;
    encoderParams.Parameter[0].Value = &uQuality;
    lGdipSaveImageToFile(pBitmap, lpszFilename, &imageCLSID, &encoderParams);
    return 1;
    }
    • lo que es hModuleThread? Buscar en aquí. Puede reemplazar con GetModuleHandle()

    • lo que es GetEncoderClsid? Buscar aquí.

    Ahora la pregunta es, ¿cómo puedo guardar los datos codificados pBitmap (como jpeg) en un BYTE buffer?

    OriginalEl autor wonderer

  2. 5

    Que se traduce en el plano de GDI+ API es bastante sencillo:

    void SaveJpeg(HBITMAP hBmp, LPCWSTR lpszFilename, ULONG uQuality)
    {
    GpBitmap* pBitmap;
    GdipCreateBitmapFromHBITMAP(hBmp, NULL, &pBitmap);
    CLSID imageCLSID;
    GetEncoderClsid(L"image/jpeg", &imageCLSID);
    EncoderParameters encoderParams;
    encoderParams.Count = 1;
    encoderParams.Parameter[0].NumberOfValues = 1;
    encoderParams.Parameter[0].Guid  = EncoderQuality;
    encoderParams.Parameter[0].Type  = EncoderParameterValueTypeLong;
    encoderParams.Parameter[0].Value = &uQuality;
    GdipSaveImageToFile(pBitmap, lpszFilename, &imageCLSID, &encoderParams);
    }

    La única cosa que no era evidente fue la limpieza de la GpBitmap creado por GdipCreateBitmapFromHBITMAP(). El Gdiplus::clase de mapa de bits no parece tener un destructor, y no hacer nada con ella interno de GpBitmap. Además, no hay ninguna GdipDeleteBitmap(), como los hay para otros GDI+ objetos. Así, es claro para mí lo que se necesita hacer para evitar fugas.

    Edición:
    Este código no abordar el hecho de que el suministrado por Microsoft GDI+ archivos de encabezado de declarar todas las funciones necesarias en C++ espacios de nombres. Una solución es copiar las declaraciones necesarias (y convertir como sea necesario para código C) a su propio encabezado. Otra posibilidad es usar los conectores suministrados por el Vino o Mono proyectos. Ambos parecen ser mucho mejor comportamiento para la compilación de código C.

    Gracias, voy a tratar de ella. Los encabezados están incluidos a ser capaz de compilar esto?
    Sí, yo no puedo compilar esto, ya que todos los basados en GDI función no puede ser encontrado. por ejemplo: GpBitmap. La adición de #include <Gdiplusflat.h> lo hará peor. Lo que me estoy perdiendo?
    La compilación es bastante extraño. Tuve que #include <GdiPlus.h>, entonces #include <GdiPlusFlat.h>. Para colmo, el Gdiplus tipos y funciones son nombres de espacios, así que tuve que agregar el uso de instrucciones para el Gdiplus y Gdiplus::DllExports espacios de nombres. Obviamente, esto te obligará a compilar el código C++, aunque en realidad es sólo recta C código. Si esto es un problema, usted puede probablemente, sólo tire de las declaraciones necesarias para su propio encabezado.
    Ese es el problema, para empezar. Yo no puedo compilar nada en C++ porque no puedo llevar el runtime de C++ en mi módulo.
    He añadido mención de esto. He probado la crea-tu-propia-encabezado como un truco rápido, y funcionó bien para mí. No me atrevo a publicar el resultado para evitar ir en contra de Microsoft los derechos de autor.

    OriginalEl autor Chris Ostler

  3. 4

    Una posibilidad: Si usted no puede modificar este programa para guardar como Jpeg, escribir un segundo programa, el uso de C# /GDI+ /otras fantasía tecnologías para vigilar el directorio para guardar y proceso de guardado Bmp a jpeg.

    Si usted no puede hacer eso, el Independent Jpeg group ha hecho pura-C Jpeg código disponible desde finales del siglo 20: Una muy mínima de la página web está disponible aquí.

    Gracias. El problema con la lib es que es ENORME. En cualquier caso, yo no puedo usar un externo lib.

    OriginalEl autor Aric TenEyck

  4. 1

    Intente esto en el comienzo de su código

    #include "windows.h"
    #include "gdiplus.h"
    using namespace Gdiplus;
    using namespace Gdiplus::DllExports;

    Y GdipSaveImageToFile() puede compilar en c++ creo.

    En C puro, probablemente, la mejor es intentar hacer que solo incluye. Busque las funciones declaraciones en «gdiplus.h» y agregar mínimo incluye para cada una de las funciones que no incluyen espacios de nombres, tales como #include "Gdiplusflat.h" para la GdipSaveImageToFile()

    y no definir WIN32_LEAN_AND_MEAN 😉

    OriginalEl autor Luis

  5. 0

    Usted probablemente tendrá que usar una librería externa para crear un archivo JPEG en el código C–no habrá una biblioteca estándar de la función para hacerlo. Por supuesto, también se podría estudiar el formato de archivo y escribir una función para generar su propio, basado en los criterios. Usted podría comenzar en la wikipedia. Si usted realmente no puede simplemente utilizar una biblioteca externa, se podía leer en las funciones de la biblioteca para aprender acerca de cómo generar sus propios archivos Jpeg. Pero que suena como un trabajo mucho más que sólo el uso de la biblioteca.

    También, no creo que el tamaño de una biblioteca realmente entra en juego con C-creo que sólo se obtiene el tamaño de las funciones de llamada, que la biblioteca (creo que, de todos modos). Por supuesto, si todas las funciones están estrechamente unida que sería enorme de todos modos.

    La 1ª cosa que probé fue el uso de libjpg y otros 3 ª parte de libs. No importa lo que yo uso de esas bibliotecas. Importa la cosa entera. Hace que mi código ENORME.
    Estaba leyendo acerca de un C versión de GDI+ (llamado plano de GDI+), alguien tiene experiencia con eso? Si eso es un sí, tengo todo el código para tomar la captura de pantalla, lo codifica como JPEG y guardarlo en un archivo o a un búfer

    OriginalEl autor Carson Myers

  6. 0

    Buena compresión de la imagen es duro. Hay un motivo por el código de la biblioteca existe para los formatos más comunes. Se requiere una gran cantidad de código para hacer. Sus restricciones en el problema de no hacer que suene como que no hay una solución práctica.

    Hay un par de cosas para probar.

    1. El uso de 8 bits por píxel BMP en vez de un color verdadero BMP. Esto supone que la pérdida de información en la asignación de colores es aceptable, por supuesto. Se va a comprar un factor de tres en el tamaño del archivo en un tiempo de 24-bits BMP.

    2. Trate de usar ejecutar la codificación de longitud en el BMP de escribir. RLE es un formato documentado para BMP, y debe haber un montón de código de ejemplo que hay para hacer. Funciona mejor en imágenes que tienen una gran cantidad de carreras largas de idéntico color. Una pantalla típica sin demasiados degradados y rellenos se ajusta a esa descripción.

    3. Si estas no son suficientes, entonces usted puede ser que necesite recurrir a una fuerte compresión. El código fuente para libjpeg es fácilmente disponible, y es posible enlazar estáticamente en lugar de utilizar la DLL. Se requerirá un poco de esfuerzo para configurar en sólo los bits que necesita, pero a la compresión y descompresión de código son casi totalmente independientes el uno del otro y que se divide la biblioteca casi en la mitad.

    gracias. Voy a tratar de la RLE. Estaba leyendo acerca de un C versión de GDI+ (llamado plano de GDI+), alguien tiene experiencia con eso? Si eso es un sí, tengo todo el código para tomar la captura de pantalla, lo codifica como JPEG y guardarlo en un archivo o a un búfer.
    El plano de gdi+ es msdn.microsoft.com/en-us/library/ms533969(VS.85).aspx

    OriginalEl autor RBerteig

  7. 0

    Han echado un vistazo a la FreeImage Proyecto? Sí, es una biblioteca externa, pero es bastante pequeña (~1Mb). Hace apenas sobre cualquier cosa que usted quiere, con imágenes, demasiado. Definitivamente vale la pena conocer, aunque no para este proyecto.

    Lo siento, como ya he dicho que no se puede usar externo libs. Mi módulo es de 200, añadiendo que la lib se hacen enormes. gracias
    Entendido. Yo vi ese comentario, pero pensé que yo también vi un poco de la apertura a ella más tarde. Ah, bueno, tal vez otro proyecto…

    OriginalEl autor Karl E. Peterson

  8. 0

    libjpeg es libre, de código abierto, el código en C. Usted puede copiar su código en el tuyo.

    http://sourceforge.net/project/showfiles.php?group_id=159521

    Gracias. lamentablemente yo no puedo utilizar el código GPL en la mía, porque la mía no es GPL. Dicho esto, me echó un vistazo a su código. Sería prácticamente imposible no incluir a la totalidad de lib ya que es tan desordenado que no se puede obtener una función sin tener que traer un montón de código. gracias m por el comentario, realmente lo aprecio.
    Es BSD NO GPL!!!

    OriginalEl autor Windows programmer

  9. 0

    Yo tenía el mismo problema y resuelto con este código.
    También deberá incluir gdiplus.lib en las Entradas para el Vinculador.

    struct GdiplusStartupInput
    {
    UINT32 GdiplusVersion;              //Must be 1  (or 2 for the Ex version)
    UINT_PTR    DebugEventCallback;         //Ignored on free builds
    BOOL SuppressBackgroundThread;      //FALSE unless you're prepared to call the hook/unhook functions properly
    BOOL SuppressExternalCodecs;        //FALSE unless you want GDI+ only to use its internal image codecs. 
    };
    int __stdcall GdiplusStartup(ULONG_PTR *token, struct GdiplusStartupInput *gstart, struct GdiplusStartupInput *gstartout);
    int __stdcall GdiplusShutdown(ULONG_PTR token);
    int __stdcall GdipCreateBitmapFromFile(WCHAR *filename, void **GpBitmap);
    int __stdcall GdipSaveImageToFile(void *GpBitmap, WCHAR *filename, CLSID *encoder, void *params);
    int __stdcall GdipCreateBitmapFromStream(IStream *pstm, void **GpBitmap);
    int __stdcall GdipCreateHBITMAPFromBitmap(void *GpBitmap, HBITMAP *bm, COLORREF col);
    int __stdcall GdipCreateBitmapFromHBITMAP(HBITMAP bm, HPALETTE hpal, void *GpBitmap);
    int __stdcall GdipDisposeImage(void *GpBitmap);
    int __stdcall GdipImageGetFrameDimensionsCount(void *GpBitmap, int *count);
    int __stdcall GdipImageGetFrameDimensionsList(void *GpBitmap, GUID *guid, int count);
    int __stdcall GdipImageGetFrameCount(void *GpBitmap, GUID *guid, int *count);
    int __stdcall GdipImageSelectActiveFrame(void *GpBitmap, GUID *guid, int num);
    int __stdcall GdipImageRotateFlip(void *GpBitmap, int num);
    /*
    Save the image memory bitmap to a file (.bmp, .jpg, .png or .tif)
    */
    int save_bitmap_to_file(char *filename, HBITMAP hbitmap)
    {
    wchar_t wstr[MAX_FILENAME];
    int     sts;
    size_t  len;
    void    *imageptr;          /* actually an 'image' object pointer */
    CLSID   encoder;
    sts = FAILURE;
    _strlwr(filename);
    if(strstr(filename, ".png"))
    sts = CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* png encoder classid */
    else if(strstr(filename, ".jpg"))
    sts = CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* jpg encoder classid */
    else if(strstr(filename, ".jpeg"))
    sts = CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* jpg encoder classid */
    else if(strstr(filename, ".tif"))
    sts = CLSIDFromString(L"{557cf405-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* tif encoder classid */
    else if(strstr(filename, ".bmp"))
    sts = CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* bmp encoder classid */
    else if(strstr(filename, ".gif"))
    sts = CLSIDFromString(L"{557cf402-1a04-11d3-9a73-0000f81ef32e}", &encoder);     /* gif encoder classid */
    if(sts != 0) return(FAILURE);
    mbstowcs_s(&len, wstr, MAX_FILENAME, filename, MAX_FILENAME-1);     /* get filename in multi-byte format */
    sts = GdipCreateBitmapFromHBITMAP(hbitmap, NULL, &imageptr);
    if(sts != 0) return(FAILURE);
    sts = GdipSaveImageToFile(imageptr, wstr, &encoder, NULL);
    GdipDisposeImage(imageptr);                 /* destroy the 'Image' object */
    return(sts);
    }

    Este código podría ser mejorada mediante la adición de un valor de calidad de los jpg archivo de salida, el código para hacer que sea más alta en este hilo.

    OriginalEl autor Mike Fletcher

  10. 0

    Si usted está realmente preocupado por el espacio, usted probablemente puede echar por la borda el tiempo de ejecución de C de las bibliotecas, así. Si sólo está utilizando un par de funciones, sólo tiene que escribir su propia versión (por ejemplo, strcpy). Es posible escribir aplicaciones de Windows que son sólo un par de K bytes. Puedo enviar una pequeña muestra de la aplicación que hace esto.

    Si llevo a mi C de imágenes de código y tira hacia abajo de sólo el codificador JPEG, probablemente producirá alrededor de 20K de código (menos si usted no necesita soporte de imágenes en escala de grises).

    Gracias, pero como puedes ver ya he respondido a la pregunta a mí mismo.
    No veo cómo respondió su pregunta a sí mismo. ¿Tiene una solución? ¿Tienes un presupuesto? La respuesta a esas 2 preguntas determina si usted obtiene una solución óptima.
    Tal vez incomprendido y pensé que me estaba refiriendo a la GPL de código.

    OriginalEl autor BitBank

  11. -1

    No reinventar la rueda.

    Si lo que necesitas es un programa que toma una captura de pantalla y la guarda como un archivo jpeg, sólo se puede utilizar ImageMagick‘s importación.

    El comando de shell sería simplemente:

    import screen.jpg

    Por supuesto, usted podría ahorrar la de arriba como takeScreenshot.bat y te gustaría tener un programa que coincide con sus requisitos.

    Gracias. No estoy tratando de reinventar la rueda. Este es el módulo que es parte de un programa que necesita para tomar una captura de pantalla cada vez que una transacción se ha terminado. Que la captura de pantalla se guarda en una base de datos de todo tipo. Es por eso que debe ser pequeño, un jpg y no puedo usar la 3ª parte de libs porque tengo que mantener mi código tan pequeño como yo, posiblemente, puede
    ImageMagick de importación es lento.

    OriginalEl autor scvalex

Dejar respuesta

Please enter your comment!
Please enter your name here