The confusion about windows GDI. Novice Programmer - winapi

I'm a chinese student and here is my first question I've asked in a foreign forum.
I have written two programs, one can run normally, but the other one failed.
Here is the normal one:
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
if(fIsTime)
ShowTime(hdc, &st);
else
ShowDate(hdc, &st);
EndPaint (hwnd, &ps) ;
return 0 ;
Here is the failed one:
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
hdcMem = ::CreateCompatibleDC(hdc);
hBitmap = ::CreateCompatibleBitmap(hdc, cxClient, cyClient);
::SelectObject(hdcMem, hBitmap);
if(fIsTime)
ShowTime(hdcMem, &st);
else
ShowDate(hdcMem, &st);
::BitBlt(hdcMem, 0, 0, cxClient, cyClient, hdc, 0, 0, SRCCOPY);
::DeleteObject(hBitmap);
::DeleteDC(hdcMem);
EndPaint (hwnd, &ps) ;
return 0 ;
The only difference between two codes is the WM_Paint code block, the latter one can not display anything. I am confused about where the error is in the latter code?

Your biggest problem is you have the source and destination DCs swapped around for the BitBlt call. The first parameter should be the destination, not the source.
Also, when you set a bitmap to a DC, you must remember the old value that is returned to you and restore it when you are finished.
Try the following:
hdc = BeginPaint (hwnd, &ps) ;
hdcMem = ::CreateCompatibleDC(hdc);
hBitmap = ::CreateCompatibleBitmap(hdc, cxClient, cyClient);
hbmpOld = ::SelectObject(hdcMem, hBitmap);
if(fIsTime)
ShowTime(hdcMem, &st);
else
ShowDate(hdcMem, &st);
::BitBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);
::SelectObject(hdcMem, hbmpOld);
::DeleteObject(hBitmap);
::DeleteDC(hdcMem);
EndPaint (hwnd, &ps) ;
return 0 ;

Related

HP printer & WINGDI - page size

I have to use GDI to print documents with printers in general.I can get the device no problem. I also communicate it desired page size like:
HANDLE hPrinter;
OpenPrinterW( printerName, &hPrinter, 0 );
DocumentPropertiesW( 0, hPrinter, printerName,
pDevMode, NULL, DM_OUT_BUFFER);
if( settings.paperLength > 0 && settings.paperWidth > 0 )
{
if( pDevMode->dmFields & DM_PAPERSIZE )
{
pDevMode->dmPaperSize = 0;
}
if( pDevMode->dmFields & DM_PAPERLENGTH )
{
pDevMode->dmPaperLength = settings.paperLength;
}
if( pDevMode->dmFields & DM_PAPERWIDTH )
{
pDevMode->dmPaperWidth = settings.paperWidth;
}
}
DocumentPropertiesW( 0, hPrinter, printerName, 0, pDevMode,
DM_IN_BUFFER | DM_OUT_BUFFER );
ClosePrinter( hPrinter );
Then I StretchBlt my document and print it.
DOCINFOW di;
memset ((void *) &di, 0, sizeof (di));
di.cbSize = sizeof (DOCINFOW);
di.lpszDocName = utf_to_widechar("Document").data();
HDC hdc = this->deviceContext;
auto cxpage = GetDeviceCaps( hdc, HORZRES );
auto cypage = GetDeviceCaps( hdc, VERTRES );
auto hdcMem = CreateCompatibleDC( hdc );
BITMAPINFO bi;
bi.bmiHeader = {sizeof(bi.bmiHeader), settings.width, settings.height,
1, 24, BI_RGB, 0, 0x0ec4, 0x0ec4, 0, 0};
LPVOID ppvBits;
auto hBitmap = CreateDIBSection( 0, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0 );
auto hbmOld = SelectObject( hdcMem, hBitmap );
StartDocW( hdc, &di);
for(int i = 0; i < this->colorTables.size(); ++i)
{
int size = settings.width * settings.height * 3;
StartPage( hdc );
SetDIBits( 0, hBitmap, 0, settings.height,
(PVOID)&(colorTables[i][0]), &bi, DIB_RGB_COLORS );
SetMapMode( hdc, MM_ISOTROPIC )
SetWindowExtEx( hdc, cxpage, cypage, 0 );
SetViewportExtEx( hdc, cxpage, cypage, 0 );
SetViewportOrgEx( hdc, 0, 0, 0 );
StretchBlt( hdc, 0, 0, cxpage, cypage, hdcMem, 0, 0,
settings.width, settings.height, SRCCOPY );
EndPage( hdc );
}
EndDoc( hdc );
SelectObject( hdcMem, hbmOld );
DeleteDC( hdcMem );
Now, my document is 210mmx99mm. Most printers more or less properly handle this: the print size is proper, and either is it located left top corner, or top in the middle, if a printer supports smaller paper sizes. The issue is, I am working with HP printer now and it just doesn't work this way. The print is stretched all over A4 [which means that it is resized to cover whole A4 page, which is both wrong size and wrong proportions]. I know the printer can actually do it. I can use other software to print such documents properly. It just seems HP doesn't fully support some GDI functions. Is there some way to sort it out?

C++ Win32: HDC's, HWND's, SelectObject, and GetObject

I'm new to win32 and there are some concepts I haven't fully grasped. For one, the difference between HDC and HWND. I understand (or think I understand) that they're handles to a objects and that hdc's can be derived from hwnd's. Like in the case of BeginPaint:
hdc = BeginPaint(hWnd, &ps);
But i don't fully understand what seperates each as certain methods have hdc as parameters and some use hwnd
Secondly, I'm not sure what SelectObject and GetObject do. I think that they associate handles with various objects, like in the case of this bitmap drawing function:
BOOL DrawBitmap (HDC hDC, INT x, INT y, INT width, INT height, HBITMAP hBitmap,
DWORD dwROP)
{
HDC hDCBits;
BITMAP Bitmap;
BOOL bResult;
hDCBits = CreateCompatibleDC(hDC);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
SelectObject(hDCBits, hBitmap);
bResult = StretchBlt(hDC, x, y, width, height,hDCBits, 0, 0, Bitmap.bmWidth,
Bitmap.bmHeight, dwROP);
DeleteDC(hDCBits);
return bResult;
}
But despite all of this I don't understand exactly what they do or how they work. Thanks, in advance.

HBITMAP or HDC to uint8[]

I have obtained a screenshot by doing the following:
GetDesktopWindow
GetDC
GetClientRect
CreateCompatibleBitmap
This gives me a HBITMAP, I can optionally take it to HDC with:
CreateCompatibleDC
My goal was to end up with a uint8 byte array from either step 4 (CreateCompatibleBitmap) or step 5 (CreateCompatibleDC) is this possible?
Thanks
You need to create a new DC with CreateCompatibleDC(), create a DIB (device-independent bitmap) for this DC with CreateDIBSection(), select the DIB in the new DC with SelectObject(), then copy from your original DC to the new DC with BitBlt(). The pointer retrieved by the CreateDIBSection will point to the raw data. This data is allocated by the system, which means you don't need to allocate it yourself, but it will be freed when you call DeleteObject() for the DIB.
Here is an example in C :
HDC hdcMemoryDC = CreateCompatibleDC(yourDC);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HBITMAP hbmp;
COLORREF *pixelBuffer;
hbmp = CreateDIBSection( hdcMemoryDC, &bmi, DIB_RGB_COLORS, (VOID**)&pixelBuffer, NULL, 0 );
SelectObject( hdcMemoryDC, hbmp );
BitBlt( hdcMemoryDC, 0, 0, width, height, yourDC, 0, 0, SRCCOPY );

Capture bitmap of entire software window with winapi

I'm trying to capture several software window and print them to my software. The goal is to be able to view two different software side to side even if they overlap.
void MyWindowFinder::drawTo(HWND hWnd){
MyWindow* currentWindow = anchorWindow;
HDC hdcRendering;
HDC hdcMemRendering;
PAINTSTRUCT ps;
hdcRendering = BeginPaint(hWnd, &ps);
hdcMemRendering = CreateCompatibleDC(hdcRendering);
int widthToRender = 0;
for(int i = 0; i < this->anchorWindow->winID ; i++){
HDC hdcCapture;
HDC hdcMemCapture;
HBITMAP hBitmap;
HGDIOBJ oldCaptureBitmap;
HGDIOBJ oldRenderingBitmap;
RECT clientRect;
GetClientRect(currentWindow->hwnd,&clientRect);
int width = clientRect.right-clientRect.left;
int height = clientRect.bottom-clientRect.top ;
if (!(hdcCapture = GetDC (currentWindow->hwnd))){
return;
}
hdcMemCapture = CreateCompatibleDC(hdcCapture);
hBitmap = CreateCompatibleBitmap(hdcCapture,clientRect.right-clientRect.left,clientRect.bottom-clientRect.top);
oldCaptureBitmap = SelectObject(hdcMemCapture, hBitmap);
BitBlt(hdcMemCapture, 0, 0, width, height, hdcCapture, 0,0, SRCCOPY|CAPTUREBLT);
oldRenderingBitmap = SelectObject(hdcMemRendering, hBitmap);
BitBlt(hdcRendering, widthToRender, 0, width, height, hdcMemCapture, 0, 0, SRCCOPY);
widthToRender+= width;
SelectObject(hdcMemRendering, oldRenderingBitmap);
SelectObject(hdcMemCapture, oldCaptureBitmap);
DeleteDC(hdcMemCapture);
DeleteDC(hdcCapture);
DeleteObject(hBitmap);
currentWindow = currentWindow->previousWindow;
}
DeleteDC(hdcMemRendering);
DeleteDC(hdcRendering);
EndPaint(hWnd, &ps);
}
This works pretty well and I can print two overlapping window side to side in my software :
However I'm facing a tricky problem. When I click on any element of one of my children software, they are not drawn in my software.
Do you have any idea why, and how I could solve this problem ?

how to use DrawText() to write text in a given window whose handle is known?

I want to know how to write text on a particular window starting at a given location in the window using the Windows API.
For example if the coordinates within the window where the text is to be written are (x,y) = (40,10) then what do I need to do to write a line of text to a particular window at that location in the window?
Suppose your window name is "hwnd" and the text which u want to write on that window at x,y coordinate is say stored in "message" where
LPCWSTR message=L"My First Window"; then
RECT rect;
HDC wdc = GetWindowDC(hwnd);
GetClientRect (bgHandle, &rect) ;
SetTextColor(wdc, 0x00000000);
SetBkMode(wdc,TRANSPARENT);
rect.left=40;
rect.top=10;
DrawText( wdc, message, -1, &rect, DT_SINGLELINE | DT_NOCLIP ) ;
DeleteDC(wdc);
Thats it.. remember this is just one example.
I am hoping this a more complete answer...
void OnPaint(HWND hWnd)
{
RECT rect;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
SetTextColor(hdc, RGB(0xFF, 0x00, 0x00));
SetBkMode(hdc, TRANSPARENT);
rect.left = 40;
rect.top = 10;
DrawText(hdc, L"Hello World!", -1, &rect, DT_SINGLELINE | DT_NOCLIP);
SelectObject(hdc, oldPen);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
}
This would then be called from the WM_PAINT message in the WndProc.

Resources