Click to See Complete Forum and Search --> : Test Run: Moving DC data (IE hbitmap/DDB) onto Form Components (IE a Canvas)


mauigb
October 18th, 2005, 09:39 PM
My problem is that I cannot get screen capture data from user32.dll function PrintWindow() to display on a Form Component, such as a Paintbox->Canvas. This sounds like it would be easy... I have tried three methods, and my only success came either from: 1) Using the clipboard and pasting it into ANOTHER application. I don't know how to paste BITMAP data from within my program, nor do I want to have to do it this way. Or, 2) Painstakingly setting the value of EACH Pixel on my canvas... INCLUDING reversing the color bit position from lpBits, AND setting up my own row and column handling. More on that in the code comments...

This is where I declare, and set the value of lpBits:


int w = rc.right-rc.left;
int h = rc.bottom-rc.top;
int size = 4 * w * h;
int *lpBits = new int[size];

::BitBlt(hdcMem, 0, 0, w, h , hbitmap, rc.left, rc.top , SRCCOPY );

::GetBitmapBits(hbitmap, size, lpBits );



This is where I set the value of each Pixel on my canvas:



int Xpixel=0;
int Ypixel=0;
int PixelColor,r,g,b; //<---'b' is ultimately not used

//The 'lpBits' (see code box above) bits come out with backwards color!

for(int bits = 0; bits<size; bits++){
PixelColor=lpBits[bits]; //I had to write these lines to get proper color.
r=PixelColor/0x010000; //(The Red and Blue Channels were reversed)
g=PixelColor/0x000100; //Forgive the sloppy code, I was frustrated! lol
g-=r*0x0100; //Besides, it works. Feel free to
PixelColor-=r*0x010000; //show me the better way...
PixelColor-=g*0x000100; //Yes, there are still MORE lines in my
PixelColor*=0x010000; //already slow FOR Loop...
PixelColor+=g*0x0100;
PixelColor+=r;
FrameBox->Canvas->Pixels[Xpixel][Ypixel]=PixelColor;

//This is where I ultimately want my
//captured BITMAP bits to go.
//And this is Code to start a new row, as lpBits
//merely a long series of backwards pixels.
if(Xpixel==w){ //lpBits is Not multi-variable array!
Xpixel=0; //Is there a way I don't have to manage this???
Ypixel++; //Truthfully, this FOR Loop is SOOOOO SLOW!!!
} //NOTE: there is no need to check the height/row
//progress, as the FOR loop automatically finishes
Xpixel++; //at the end of the data
}



The FOR Loop is incredibly slow. For the record, it was just as slow before I had all the math functions in there to fix the colors. And the clipboard not only doesn't appeal to me, but I don't know very much about it! So, my third and final method of attempt is to get the hbitmap DC and put its raw data directly onto my 'FrameBox->Canvas' with a call to SetDIBitsToDevice(). This code ALSO came from a post wherein Golanshahar had replied. By the way, this is the only method that doesn't work AT ALL:


//First step: Create BITMAP Header info:
BITMAPINFOHEADER bitmapInfo;
::ZeroMemory(&bitmapInfo,sizeof(BITMAPINFOHEADER));
bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.biWidth = w;/*enter Width*/
bitmapInfo.biHeight = h;/*enter Height*/
bitmapInfo.biPlanes = 1;
bitmapInfo.biBitCount = 32/*bit per pixel 24,32 */ ;
bitmapInfo.biCompression = 0; //RGB
bitmapInfo.biSizeImage = bitmapInfo.biWidth*bitmapInfo.biHeight*bitmapInfo.biBitCount/8;

//Then I try to use 'SetDIBitsToDevice()' to put the data onto my canvas

//HWND hDC; I once tried using this handle in the following call,
// and a SelectObject() afterwards to transfer into the canvas
::SetDIBitsToDevice(FrameBox->Canvas,//hDC, <--- the dc you want to set the data,
0,0, // lets say of the TCanvas
bitmapInfo.biWidth, //bitmap.bmWidth, <---these were provided
bitmapInfo.biHeight,//bitmap.bmHeight, <---by Golanshahar, but
0, //were undefined. I assumed he meant to use the
0, //above declared 'bitmapinfo.bi*' variables instead
0,
bitmapInfo.biHeight,//bitmap.bmHeight,
hbitmap, //hdcMem, //lpBits, //<----NUMEROUS things I tried to
(LPBITMAPINFO)&bitmapInfo, //cram into this call.
DIB_RGB_COLORS);
//SelectObject(FrameImage->Canvas->Handle , hDC); //See notes above the
//call to SetDIBitsToDevice()





Now, just in case you haven't given up on the longest post of all time, here is the complete function that brings it all together (the whole snippet is a FormMain::ButtonOnClick Event, and I used php tags to color the code, cuz it was SOOO long). This is my first post, so let me know if I have bad etiquette! Here it is:



//---------------------------------------------------------------------------

void __fastcall TFrmMain::Button4Click(TObject *Sender)
{

//Get a handle to the desired Window
hWndDesired = FindWindow(NULL, TEXT("http://www.test.com - Test Window - Microsoft Internet Explorer"));
if(hWndDesired == NULL)
{
Button3->Caption="Not Running"; //Couldn't find desired window
}
else
{
//Desired Windows' handle has been captured by hWndDesired via FindWindow(NULL, TEXT());
typedef BOOL (_stdcall *tPrintWindow)( HWND, HDC,UINT);

tPrintWindow pPrintWindow =0;

HINSTANCE handle = ::GetModuleHandle("user32.dll");//Golanshahar might
if ( handle == 0 ) //recognize this code...
handle = ::LoadLibrary("User32.dll");
if (handle)
pPrintWindow = (tPrintWindow)::GetProcAddress(handle,"PrintWindow");

if( pPrintWindow)
hdc = GetWindowDC(hWndDesired);

if (hdc)
{
HDC hdcMem = CreateCompatibleDC(hdc);

if (hdcMem)
{
RECT rc;

GetWindowRect(hWndDesired, &rc);

HBITMAP hbitmap = CreateCompatibleBitmap(hdc, (rc.right-rc.left), (rc.bottom-rc.top));

if (hbitmap)
{
SelectObject(hdcMem, hbitmap); //Okay, what EXACTLY happens on this line???

pPrintWindow(hWndDesired, hdcMem, 0); //YES!!! this truly works! (SO FAR)
//Attempt #1

if (OpenClipboard(hWndDesired) != 0) { //I HATE to have to resort to the clipboard!
EmptyClipboard(); //This code was just ONE attempt of many to get my BITMAP data
SetClipboardData(CF_BITMAP, hbitmap);//hdcMem); Would this work here instead???
CloseClipboard();

//Attempt #2 The following code, until Attempt #3, actually works, but VERY slow!

int w = rc.right-rc.left;
int h = rc.bottom-rc.top;

int size = 4 * w * h;//three as a multiplier led the display to show 3/4 of the image!
int *lpBits = new int[size];
::BitBlt(hdcMem, 0, 0, w, h , hbitmap, rc.left, rc.top , SRCCOPY ); //Is this OKAY???

::GetBitmapBits(hbitmap, size, lpBits );
//Golanshahar instructed the use of this call
//He ALSO said I could access either hdcMem, or hbitmap
//I can't remember which!


//I reluctantly proceed to loop through 'lpBits'
//The 'lpBits' bits come out with backwards color!
//Meaning: 0xFFBBAD SHOULD be: 0xADBBFF

int Xpixel=0;
int Ypixel=0;
int PixelColor,r,g,b; //<---'b' is ultimately not used

for(int bits = 0; bits<size; bits++){
PixelColor=lpBits[bits]; //I had to write these lines to get proper color.
r=PixelColor/0x010000; //(The Red and Blue Channels were reversed)
g=PixelColor/0x000100; //Forgive the sloppy code, I was frustrated! lol
g-=r*0x0100; //Besides, it works. Feel free to
PixelColor-=r*0x010000; //show me the better way...
PixelColor-=g*0x000100; //Yes, there are still MORE lines in my
PixelColor*=0x010000; //already slow FOR Loop...
PixelColor+=g*0x0100;
PixelColor+=r;
FrameBox->Canvas->Pixels[Xpixel][Ypixel]=PixelColor;

//This is where I ultimately want my
//captured BITMAP bits to go.
//And this is Code to start a new row, as lpBits
//merely a long series of backwards pixels.
if(Xpixel==w){ //lpBits is Not multi-variable array!
Xpixel=0; //Is there a way I don't have to manage this???
Ypixel++; //Truthfully, this FOR Loop is SOOOOO SLOW!!!
} //NOTE: there is no need to check the height/row
//progress, as the FOR loop automatically finishes
Xpixel++; //at the end of the data
}


//Attempt #3

//OKAY, here I make another attempt to get the hbitmap
//data onto my screen via my Paintbox->Canvas.
//This code also originated from Golanshahar, THANKS AGAIN!
//First step: Create BITMAP Header info:
BITMAPINFOHEADER bitmapInfo;
::ZeroMemory(&bitmapInfo,sizeof(BITMAPINFOHEADER));
bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.biWidth = w;/*enter Width*/
bitmapInfo.biHeight = h;/*enter Height*/
bitmapInfo.biPlanes = 1;
bitmapInfo.biBitCount = 32/*bit per pixel 24,32 */ ;
bitmapInfo.biCompression = 0; //RGB
bitmapInfo.biSizeImage = bitmapInfo.biWidth*bitmapInfo.biHeight*bitmapInfo.biBitCount/8;

//Then I try to use 'SetDIBitsToDevice()' to put the data onto my canvas

//HWND hDC; I once tried using this handle in the following call,
// and a SelectObject() afterwards to transfer into the canvas
::SetDIBitsToDevice(FrameBox->Canvas,//hDC, <--- the dc you want to set the data,
0,0, // lets say of the TCanvas
bitmapInfo.biWidth, //bitmap.bmWidth, <---these were provided
bitmapInfo.biHeight,//bitmap.bmHeight, <---by Golanshahar, but
0, //were undefined. I assumed he meant to use the
0, //above declared 'bitmapinfo.bi*' variables instead
0,
bitmapInfo.biHeight,//bitmap.bmHeight,
hbitmap, //hdcMem, //lpBits, //<----NUMEROUS things I tried to
(LPBITMAPINFO)&bitmapInfo, //cram into this call.
DIB_RGB_COLORS);
//SelectObject(FrameImage->Canvas->Handle , hDC); //See notes above the
//call to SetDIBitsToDevice()


/* >>This block includes the descriptions from MSDN, after I tried to insert
>>my own variables, and is here for reference only!
SetDIBitsToDevice(
FrameImage->Canvas,//<---I put this here // handle to DC
0, // x-coord of destination upper-left corner
0, // y-coord of destination upper-left corner
(rc.right-rc.left),//<---I put this here // source rectangle width
(rc.bottom-rc.top),//<---I put this here // source rectangle height
0, // x-coord of source lower-left corner
rc.bottom,//<---I put this here // y-coord of source lower-left corner
0, // first scan line in array
rc.bottom,//<---I put this here // number of scan lines
CONST VOID *lpvBits, // array of DIB bits
CONST BITMAPINFO *lpbmi, // bitmap information
UINT fuColorUse // RGB or palette indexes
); //*/


//I can't even remember what I was trying to do with this.... But it
//demonstrates the level of cluelessness I have!

//BitBlt(FrameImage->Picture->Bitmap ,0,0,(rc.right-rc.left),(rc.bottom-rc.top),hdcMem,0,0,SRCCOPY);//hbitmap
}

DeleteObject(hbitmap);
}
DeleteObject(hdcMem);
}
ReleaseDC(hWndDesired, hdc);
}

Button3->Caption="Running"; //This is just to show me that the Desired
//Window is Running, It happens to be a
//Button for testting purposes only
}

}
//---------------------------------------------------------------------------



A final note: I REALLY want to be able to get the hbitmap DC data directly into any form component that I can select a rectangle from, and compare it to a selected rectangle from an image on disk. If you know how to do this with either a TPictureBox, TImage, TCanvas, or ANYTHING else, I will change my code to accomodate. And peoples, I have spent HOURS (No, wait, DAYS...) reading posts, serching MSDN, and honestly, my beginner knowledge isn't enough that I can find the answer to this particular problem on my own. If you can help, PLEASE DO!!! References to other posts, or other pages with details on the subject ARE WELCOME, but make sure they actually apply. MSDN, for example, can be very vague, and assume you have a high level of base knowledge. Thanks in advance!
~Maui