PDA

Ver la Versión Completa : [Sintaxis] DLL que crea un pantallazo


Kuk
9 de marzo de 2017, 13:48
Adrianeloy, Dasije, aquí está el fuente:


#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;

#define DllExport __declspec( dllexport )

extern "C" { // Undecorate

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes

ImageCodecInfo* pImageCodecInfo = NULL;

GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure

pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure

GetImageEncoders(num, size, pImageCodecInfo);

for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}

DllExport int HazPantallazo(const char* SavePath)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);

// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);

// get a new bitmap
HBITMAP hOldBitmap = SelectObject(hMemoryDC, (HGDIOBJ)hBitmap);

BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = SelectObject(hMemoryDC, (HGDIOBJ)hOldBitmap);

Bitmap *image = new Bitmap(hBitmap, NULL);

CLSID myClsId;
int retVal = GetEncoderClsid(L"image/jpg", &myClsId);

wchar_t PATH[512];
const wchar_t* WSavePath = &PATH[0];

mbstowcs(PATH, SavePath, 512);

image->Save(WSavePath, &myClsId, NULL);
delete image;

GdiplusShutdown(gdiplusToken);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);

return 0;
}

} // Undecorate



Según he leído, se puede indicar la calidad (nivel de compresión) en el 3-er parámetro en la invocación del método save (donde va el NULL) :


image->Save(WSavePath, &myClsId, NULL);


Pero no sé por qué, no me compila en DevC++ con la estructura EncoderParameter.

Adrianeloy, el formato depende de lo que tú indiques. puede ser:

int retVal = GetEncoderClsid(L"image/jpg", &myClsId);
int retVal = GetEncoderClsid(L"image/bmp", &myClsId);
int retVal = GetEncoderClsid(L"image/png", &myClsId);


Lo he modificado a JPG porque el BMP por defecto pesa 6 megas cada pantallazo :D

Dasije
9 de marzo de 2017, 14:18
En las rutinas de C++ que tengo realizadas, en su momento leí en los manuales de NetCOBOL de como llamar funciones, y así debes hacerlo para que te envíe o reciba parámetros con un CALL directamente:

extern "C" _declspec(dllexport) void WINAPI nombre_rutina(long int parametro1, char parametro2[256], char parametro3[256])

Todos los parámetros pueden ser para enviar como para recoger entre ambos ambientes.

Recuerda que el type char, es una string sin fin, y debe convertirse en array limitado el número de ocurrencias haciendo una copia del valor de la variable para que reciba el valor desde NetCOBOL, en caso contrario no llegaría ninguno.

Kuk
9 de marzo de 2017, 14:46
Dasije, ¿qué es lo que no te gusta exactamente del fuente que he publicado? No entiendo qué tiene que ver tu post. Yo sólo paso un puntero, nada más.
Si simplemente quieres mostrar un ejemplo que no tiene nada que ver con este código publicado por mi, deberías crear un tema nuevo. Si tienes modificaciones a aportar al código publicado, precisa más, dónde exactamente y por qué. ;)

Lo de "directamente", no sé a qué te refieres. Si es por la *.LIB, la que genera el Dev C++ no funciona, no sé por qué y no tengo tiempo de mirarlo. Pero como se puede evitar con LoadLibrary y GetProcAddress (que se hacen automáticamente via fichero COBOL85.CBR) no me he comido mucho el coco con ello.

El extern "C" lo que hace es el famoso "undecorate" que básicamente hace más visibles los nombres de las funciones de acuerdo a las normas del C.

En cuanto a pasar un char en parámetros, aunque la declaración sea así, por detrás se transforma en puntero y se pasa la dirección del array, porque no podemos pasar por parámetro algo que no sea tipo binario. Ah, y por cierto:

Recuerda que el type char, es una string sin fin

Aquí te has equivocado, el tipo char es de 1 byte, y no infinito. Si lo definimos como array de char y sin especificar la longitud, ahí sí. Es decir:


char MyCharArray[];

Dasije
9 de marzo de 2017, 15:59
Quería dar entender que se puede mejorar para poder usar la LIB, así tratas la rutina como si fuera una API de Windows dentro del procedure donde se ejecute.

Sino te funciona, debe ser la compatibilidad del compilador, en mi caso funciona con MS C++ 6.0.

Kuk
9 de marzo de 2017, 16:40
Dasije, entonces no es el fuente el que no te gusta sino que no haya facilitado la LIB correspondiente en el tema [Componente] Como generar un PrintScreen - COBOL Foro (http://cobolforo.es/showthread.php?t=515). No sé, he probado con la que genera el Dev C++ (que es lib*.a) y extrayendo la LIB desde la DLL con la utilidad IMPLIB. Ambas van en el ZIP, probadlas a ver, pero a mi no me han funcionado.

Hrmcobol
11 de marzo de 2017, 23:29
Amigo Kuk, sera posible que la rutina Pantallazo capture solo la pantalla Vigente?
Saludos

Kuk
12 de marzo de 2017, 14:15
Hrmcobol, aquí va el código y la DLL compilada:


#include <Windows.h>
#include <Gdiplus.h>

#define DllExport __declspec( dllexport )

extern "C" {

DllExport void __stdcall ScreenShot(char*BmpName, HWND WndHwnd)
{
RECT DesktopParams;
HDC DevC = GetDC(WndHwnd);
GetWindowRect(WndHwnd,&DesktopParams);
DWORD Width = DesktopParams.right - DesktopParams.left;
DWORD Height = DesktopParams.bottom - DesktopParams.top;

DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+ (sizeof(RGBTRIPLE)+1*(Width*Height*4));
char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);

PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];

BFileHeader->bfType = 0x4D42; // BM
BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
BInfoHeader->biPlanes = 1;
BInfoHeader->biBitCount = 24;
BInfoHeader->biCompression = BI_RGB;
BInfoHeader->biHeight = Height;
BInfoHeader->biWidth = Width;

RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
RGBTRIPLE color;

HDC CaptureDC = CreateCompatibleDC(DevC);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
SelectObject(CaptureDC,CaptureBitmap);
BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY |CAPTUREBLT);
GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,( LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);

DWORD Junk;
HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE ,0,CREATE_ALWAYS,0,0);
WriteFile(FH,BmpFileData,FileSize,&Junk,0);
CloseHandle(FH);
GlobalFree(BmpFileData);
}

} //undecorate


Para llamar la función desde PowerCOBOL, en el fichero COBOL85.CBR ponemos:


[MainForm.Entry]
ScreenShot=PrintScreen.dll


En el código:


ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 PrinScreen PIC X(50) VALUE "ScreenShot".
01 WsPath PIC X(250).
01 FrmHWND PIC S9(9) COMP-5.
PROCEDURE DIVISION.

MOVE "hwnd" OF POW-SELF TO FrmHWND
MOVE "C:\001.BMP" TO WsPath

CALL PrinScreen WITH STDCALL USING BY REFERENCE WsPath
BY VALUE FrmHWND