How can i capture screenshot and assign to a static control? Winapi - winapi

I am trying to capture a screen and assign it to some sort of control so I can click the picture and retrieve the coordinates at which the picture was clicked.
How would I go about doing this? Im trying to assign a bitmap to a static control, and when the user clicks on the image, i retrieve mouse coordinates and subtract the offsetted borders. Im using a static control since it has SS_Notify.
But here the picture isnt even to be able to be displayed on the frame?
What would i do to fix this code?
static HDC handle_MemoryDC;
static HDC handle_ScreenDC;
//BITMAP bitmap;
static HBITMAP handle_Bitmap;
static int x, y;
case WM_CREATE:{
CreateWindowEx(NULL, "STATIC", "", SS_BITMAP|WS_VISIBLE|WS_CHILD, 500,300, 640 ,360 , hwnd, HMENU(IDCSTATIC_BITMAP), GetModuleHandle(NULL), NULL);
handle_ScreenDC = GetDC(NULL);
handle_MemoryDC = CreateCompatibleDC(handle_ScreenDC);
x = GetDeviceCaps(handle_ScreenDC, HORZRES);
y = GetDeviceCaps(handle_ScreenDC, VERTRES);
handle_Bitmap = CreateCompatibleBitmap(handle_ScreenDC, 640, 360);
SelectObject(handle_MemoryDC, handle_Bitmap);
StretchBlt(handle_MemoryDC, 0, 0, 640, 360, handle_ScreenDC, 0, 0, x, y, SRCCOPY);
SendDlgItemMessage(hwnd, IDCSTATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)handle_Bitmap);

It looks like you are trying to send message to menu (IDCSTATIC_BITMAP), instead of static control. Try to use
SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)handle_Bitmap);

Related

Drawing peformance with Win32

I am getting a very poor peformance drawing with Win32. It takes too much time and needs improving. Please advise.
Here is what I do.
HDC dc = GetDC(wnd);
HDC memoryDc = CreateCompatibleDC(dc);
HBITMAP memoryMapBitmap = CreateCompatibleBitmap(dc, 400, 400);
HGDIOBJ originalBitmap = SelectObject(memoryDc, memoryMapBitmap);
Then, I draw in a for-loop as follows.
HBRUSH brush = (HBRUSH)GetStockObject(DC_BRUSH);
SetDCBrushColor(memoryDc, colorRef);
FillRect(memoryDc, &rect, brush);
And finally, I do a cleanup
SelectObject(memoryDc, originalBitmap);
DeleteDC(memoryDc);
ReleaseDC(wnd, dc);
Drawing takes a lot of time (several seconds). Is there a way to draw faster with Win32?
Thanks in advance!
It looks like I have solved it. Below is the solution with some comments.
I have a dialog defined in RC-file. There is a control to display a bitmap image in the dialog.
CONTROL "", IDC_MEMORY_MAP, WC_STATIC, SS_BITMAP | SS_CENTERIMAGE | SS_SUNKEN, 9, 21, 271, 338, WS_EX_LEFT
In the run-time I need to create, draw and display a bitmap:
HWND map = GetDlgItem(dlg, IDC_MEMORY_MAP);
HBITMAP bitmap = createMemoryMapBitmap(map);
bitmap = (HBITMAP)SendMessage(map, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmap);
DeleteObject(bitmap); // (!) this is a very important line, otherwise old bitmap leaks
Code that finds out the size of the bitmap to create:
HBITMAP createMemoryMapBitmap(HWND map) {
RECT rect = {0, 0, 0, 0};
GetClientRect(map, &rect);
SIZE size = {rect.right - rect.left, rect.bottom - rect.top};
HDC dc = GetDC(map);
HBITMAP bitmap = doCreateMemoryMapBitmap(dc, &size);
ReleaseDC(map, dc);
return bitmap;
}
Finally, we actually create the bitmap and draw on it:
HBITMAP doCreateMemoryMapBitmap(HDC dc, LPSIZE bitmapSize) {
// create 24bpp bitmap in memory in order to draw fast
BITMAPINFO info;
memset(&info, 0, sizeof(info));
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = bitmapSize->cx;
info.bmiHeader.biHeight = bitmapSize->cy;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 24;
info.bmiHeader.biCompression = BI_RGB;
void *pixels = NULL;
HBITMAP memoryBitmap = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &pixels, NULL, 0);
HDC memoryDc = CreateCompatibleDC(dc); // (!) memoryDc is attached to current thread
HGDIOBJ originalDcBitmap = SelectObject(memoryDc, memoryBitmap);
// drawing code here
// perform windows gdi cleanup
SelectObject(memoryDc, originalDcBitmap); // restore original bitmap in memoryDC (optional step)
DeleteDC(memoryDc); // this releases memoryBitmap from memoryDC
return memoryBitmap;
}
The idea above is to create a 24bpp bitmap in the memory and draw on it. This way drawing is fast, as #IInspectable pointed out.
If display is in the indexed color mode, e.g. 16 or 256 colors, it seems Windows native control is smart enough to convert the color depth automatically displaying the bitmap.

Win32 can't get SetBkMode Transparency to work

I am writing a simple program, that prints some text on the screen, overlaying the other windows.
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
HWND hwnd = GetDesktopWindow();
HDC hdc;
RECT rect;
//LPRECT rect = new RECT;
wchar_t text[] = L"test";
GetClientRect(hwnd, &rect);
do{
hdc = GetWindowDC(hwnd);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(100, 100, 100));
DrawText(hdc, text, -1, &rect, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
Sleep(15);
} while (1);
return 0;
}
The problem is that I would like the background of the printed text to be transparent, but SetBkMode does not seem to work (it actually makes no difference if I set it to OPAQUE or TRANSPARENT) so I get a solid background. Any ideas? What am I missing?
edit: Changed LPRECT to RECT, as suggested.
edit: using transparent window:
creating window:
CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, // extended style
(LPCWSTR)WINDOW_CLASS_NAME, // class
L"test", // title
NULL,
0, 0, // initial x,y
400, 300, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL)
globals:
wchar_t tst_Str[] = L"TEST";
WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetTextColor(hdc, RGB(255, 0, 0));
DrawText(hdc, tst_Str, -1, &rc,NULL);
EndPaint(hwnd, &ps);
return 0;
Windows are responsible for painting themselves. The system is not designed to allow external parties to draw on other windows in the manner you are attempting. You will have to recalibrate your expectations.
If you wish to draw something you should create a window and draw on it. You are at liberty to make the window a transparent layered window and position your drawing so that it appears on top of another window. And you also gain the benefit that you don't need to run a busy loop. You can paint in the standard way, in response to WM_PAINT messages.
To make a layered window appear on the desktop, you have to call SetLayeredWindowAttributes function, for example
SetLayeredWindowAttributes(hWnd,
RGB(255, 255, 255), // The transparent color
128, // Opacity of the window
LWA_ALPHA | LWA_COLORKEY); // Enable both features
This example will make portions of the window with white color completely transparent. Parameter bAlpha specifies the opacity of the window (the pixels not affected with crKey).
Here's a screen shot of such window with Test text floating over browser window with this question:
If you create your window without border, using these window styles: WS_POPUP | WS_SYSMENU, then it will look like on the screen shot. I included WS_SYSMENU to have the standard window menu in the taskbar. To force the taskbar to display a button for popup window, include WS_EX_APPWINDOW extended style.

Get the specified part of an hbitmap.

There is an HBITMAP which can be displayed successfully. I want get a portion (specified by a rect) of the bitmap, so is the following code but the returned bitmap is always black. Is there something wrong with the code? Thanks.
HBITMAP GetSelectedBitmap(HBITMAP p_bitmap, int x, int y, int width, int height){
HDC l_srcDc = ::CreateCompatibleDC(NULL);
::SelectObject(l_srcDc, p_bitmap);
HDC l_dstDc = ::CreateCompatibleDC(l_srcDc);
HBITMAP l_newBitmap = CreateCompatibleBitmap(l_dstDc, width, height);
HBITMAP l_oldBitmap = (HBITMAP)::SelectObject(l_dstDc, l_newBitmap);
ASSERT(0 != ::BitBlt(l_dstDc, 0, 0, width, height, l_srcDc, x, y, SRCCOPY));
HBITMAP l_clippedBitmap = (HBITMAP)::SelectObject(l_dstDc, l_oldBitmap);
::DeleteDC(l_srcDc);
::DeleteDC(l_dstDc);
return l_clippedBitmap;}
I found the cause, change
HBITMAP l_newBitmap = CreateCompatibleBitmap(l_dstDc, width, height);
to
HBITMAP l_newBitmap = CreateCompatibleBitmap(l_srcDc, width, height);
fix the issue. that means CreateCompatibleBitmap should use the source DC.

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 ?

Capture screen shot with mouse cursor

I have used the following code to get screen shot on Windows.
hdcMem = CreateCompatibleDC (hdc) ;
int cx = GetDeviceCaps (hdc, HORZRES);
int cy = GetDeviceCaps (hdc, VERTRES);
HBITMAP hBitmap(NULL);
hBitmap = CreateCompatibleBitmap (hdc, cx, cy) ;
SelectObject (hdcMem, hBitmap) ;
BitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
However, the mouse cursor doesn't show up.
How could I get the cursor? or Is there a library can do that?
Thanks in advance.
After your BitBlt and before you select the bitmap back out of hdcMem, you can do this:
CURSORINFO cursor = { sizeof(cursor) };
::GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING) {
RECT rcWnd;
::GetWindowRect(hwnd, &rcWnd);
ICONINFOEXW info = { sizeof(info) };
::GetIconInfoExW(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - rcWnd.left - rc.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - rcWnd.top - rc.top - info.yHotspot;
BITMAP bmpCursor = {0};
::GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
::DrawIconEx(hdcMem, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
}
The code above figures out if the cursor is showing, using the global cursor state since you're probably taking a screenshot of a window (or windows) in another process. It then gets the target window coordinates for adjusting from screen. It gets specific info about the cursor, including its hotspot. It computes the drawing position of the icon. Finally, it gets the actual size of the cursor icon so that it can draw it without any stretching.
The only limitations to this approach that I know of are:
You don't get cursor shadows if you have them enabled.
If it's an animated cursor, this just shows the first frame. As far as I know, there's no way to determine the current frame.

Resources