I have a Window (win32 API) Application in visual c++. I am not using MFC. I have to add a picutre box to my application and Change the image of this picture box periodically. Can any one help me out in achieving the above task? Thanks in advance.
This is quite a complex task to post full code here, but I will try to give a few guidelines on how to do it:
First method is to load the image and paint it
Load your image (unfortunately the plain Win32 API has support for quite a few image formats BMP, ICO ...).
HBITMAP hImage = (HBITMAP)LoadImage(NULL, (LPCSTR)file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_LOADTRANSPARENT);
Store the handle above somewhere in your application where you can access it from your WindowProcedure
In the WinProc on the WM_PAINT message you will need to paint the image. The code is something like:
HDC hdcMem = CreateCompatibleDC(hDC); // hDC is a DC structure supplied by Win32API
SelectObject(hdcMem, hImage);
StretchBlt(
hDC, // destination DC
left, // x upper left
top, // y upper left
width, // destination width
height, // destination height
hdcMem, // you just created this above
0,
0, // x and y upper left
w, // source bitmap width
h, // source bitmap height
SRCCOPY); // raster operation
Should work.
Now, the second way of doing it is to create a static control, with type being SS_BITMAP and set its image as:
hImage = LoadImage(NULL, file, IMAGE_BITMAP, w, h, LR_LOADFROMFILE);
SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
where hwnd is the handle of your static control.
Related
I have created a second desktop using CreateDesktop and im not switching to it. Also i have created some processes in it like Explorer.exe and Winrar.exe. Next i have a code which takes Screenshot of current desktop to clipboard. Both CreateDesktop and Screenshot works, But Screenshot of that new desktop or window returns a black bitmap:
This is the screenshot for a window in a desktop which returns current desktop:
// hwnd is handle to winrar or ... created in a new desktop retrieved by EnumDesktopWindow
RECT rc;
GetClientRect(hwnd, &rc);
const HDC hScreenDC = GetDC(nullptr);
const HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
const int width = GetDeviceCaps(hScreenDC, HORZRES);
const int height = GetDeviceCaps(hScreenDC, VERTRES);
const HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
HBITMAP(SelectObject(hMemoryDC, hBitmap));
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
OpenClipboard(nullptr);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
I have implemented both this methods in c# but same thing happens there.
There are great resources like:
Capture screenshot of hidden desktop
take a screenshot of a desktop created using createdesktop api
C# – SCREEN CAPTURE WITH VISTA DWM (SHARED DIRECT3D SURFACE)
Window Contents Capturing using WM_PRINT Message
how to capture screen from another desktop?(CreateDesktop)
Also this is like a dead topic, No new article, Explanation or solution to it.
I have read most of them but no luck, This was my closest try i think. Also language doesnt matter for me: C#, C++, Python or ... .
I found the solution, It is interesting but no perfect, Just resolves my needs.
After CreateDesktop by calling OpenDesktop then SetThreadDesktop then using the screenshot code you get the screenshot of the window which is created inside CreateDesktop, Also no need for Creating Explorer.exe inside it if you just want the window:
CreateDesktopW(L"NewDesktop"); // CreateDesktop code here. This is my function
const HDESK Handle = OpenDesktopW(L"NewDesktop", 0, 0, GENERIC_ALL);
SetThreadDesktop(Handle);
// Above ScreenShot code here ...
The screenshot code needs a PrintWindow:
RECT rc;
GetClientRect(hwnd, &rc);
const HDC hScreenDC = GetDC(nullptr);
const HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
const int width = GetDeviceCaps(hScreenDC, HORZRES);
const int height = GetDeviceCaps(hScreenDC, VERTRES);
const HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
HBITMAP(SelectObject(hMemoryDC, hBitmap));
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
/// ADDED CODE
PrintWindow(hWnd, hMemoryDC, PW_CLIENTONLY);
///
OpenClipboard(nullptr);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
Mine worked with a winrar.exe window inside a inactive desktop. You can try this then paste it to paint to see the result.
There is just one thing, The whole area of the screenshot bitmap is black except the window handle that i want which is fine by me. I think i should get handle of every window from bottom to top in order then mix them up.
All additions to this are appreciated.
Does anyone know how to get a the bitmap of a Mac and Windwos browser window? I want to capture images from Chrome or FireFox as they are rendered and pass them into a C++ based plug-in.
I've looked into using a Chrome extension, but it's not practical.
Willing to do a Mac and Windows native app.
With the windows API you can grab screenshots fairly easy.
// 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, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
// now your image is held in hBitmap. You can save it or do whatever with it
Im not sure about the exact area of the browser tho, or how to solve it for Mac. If nothing else, its a start.
First, I have a picture control with a bitmap1 loaded in a dialog box:
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)LoadImage(NULL, sbitmap1.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
Second, at some point I draw a bitmap2 in the picture control using StretchBlt.
hdcImage = GetDC(hWnd)
hMemDC = CreateCompatibleDC( hdcImage );
hOldbm = (HBITMAP)SelectObject( hMemDC, hbitmap2 );
SetStretchBltMode( hdcImage, COLORONCOLOR);
StretchBlt( hdcImage, left, top, width, height, hMemDC, 0, 0, w, h, SRCCOPY );
SelectObject( hMemDC, hOldbm );
The bitmap2 is successfully painted but in certain occasions (for example when I minimize the dialog) the picture control no longer shows bitmap2 but bitmap1 instead.
I think the problem is the repaint event. Is there a way to stop the repaint event or change the bitmap that this event is going to paint?
Edit:
Thanks #Mark and #Edward for your answers.
The problem was this:
after using StretchBlt you need to do SendMessage STM_SETIMAGE
for SendMessage STM_SETIMAGE use a global HBITMAP (preferably)
Something like this:
hbitmapglobal = (HBITMAP)CopyImage(hbitmap2, IMAGE_BITMAP, abs(width), abs(height), LR_COPYRETURNORG);
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmapglobal);
CopyImage is the real simple way to make a copy of a HBITMAP. Take in consideration that this is a sample and hbitmapglobal must be freed at some point.
Normally all the painting occurs in the WM_PAINT handler.
I would suggest setting up (global) variables when you need to paint the second bitmap and invalidate the rectangle of the picture control.
Check for the variable in the WM_PAINT handler and do the painting there.
Quick background: I have a TreeView which I have created using Windows API calls in C++ (Visual Studio 2008, though that shouldn't make a difference):
hTreeview = CreateWindowEx(0, WC_TREEVIEW, L"My Treeview", WS_CHILD | WS_VISIBLE | TVS_INFOTIP | TVS_NOHSCROLL | TVS_SHOWSELALWAYS | TVS_EDITLABELS | TVS_SINGLEEXPAND, m_tx, m_ty, m_tw, m_th, hWindow, (HMENU)2, hInstance, NULL);
I have successfully assigned an ImageList to it using:
m_hImageList = ImageList_Create(cx, cy, ILC_COLOR24, n, n);
TreeView_SetImageList(hTreeview, m_hImageList, TVSIL_NORMAL);
where cx, cy, and n are all specified (in this case, 18, 18, and 5, respectively). This all works fine, as I can see because now there is space set aside next to my items for the image.
What I am trying to accomplish is to then copy a subsection of another bitmap (from a file). The code that I have tried (but does not work) is this:
HBITMAP hSkin = (HBITMAP)LoadImage(NULL, szPathBmp, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
for (long i = 0; i < n; i++)
{
HDC dcDest = CreateCompatibleDC(NULL);
HBITMAP hIcon = CreateCompatibleBitmap(dcDest, cx, cy);
HDC dcSrc = CreateCompatibleDC(NULL);
SelectObject(dcSrc, hSkin);
BitBlt(dcDest, 0, 0, cx, cy, dcSrc, x, y, SRCCOPY);
*pIcon = ImageList_Add(m_hImageList, hIcon, NULL);
DeleteObject(hIcon);
DeleteDC(dcSrc);
DeleteDC(dcDest);
}
DeleteObject(hSkin);
I have left out the error checking code for brevity, and it can be assumed that all of the listed variables have been set somewhere else in the program (forgive me for not providing a working source file, but this is a very large project and I have tried to include only the parts that are relevant).
All I get in the Treeview is black squares (which happens to be the background color of the Treeview), so I am assuming that something is going wrong with the last block of code--the one that loads the skin and tries to BitBlt a portion of it into a new bitmap to save to the ImageList. Can anyone either tell me what I'm doing wrong, or tell me a better way of accomplishing what I'm trying to do?
I am using C++ and the Windows API exclusively, no .NET, MFC, or Windows Form Designer.
Thanks in advance for your help, and if I've left anything out, I apologize; this is one of my first posts.
There are a few problems with your code.
Firstly, when you create a new DC it starts out with a monochrome bitmap in it, so your CreateCompatibleBitmap call will also produce a monochrome one. Instead, you probably want to create the bitmap based on the window or screen DC.
Secondly, you never actually select the bitmap into dcDest, so nothing will be drawn into hIcon anyway.
Thirdly, ImageList_Add will fail if the bitmap is currently selected into a device context, so you have to deselect hIcon from dcDest before you add the icon to the image list.
Lastly, you are also neglecting to save the original bitmaps and restore them, so this will also cause a GDI leak.
Try something like this:
HDC hdcWindow = GetDC(hWnd);
HDC dcDest = CreateCompatibleDC(hDCWindow);
HBITMAP hIcon = CreateCompatibleBitmap(hDCWindow, cx, cy);
HDC dcSrc = CreateCompatibleDC(NULL);
HGDIOBJ hOldSourceBmp = SelectObject(dcSrc, hSkin);
HGDIOBJ hOldDestBmp = SelectObject(dcDest, hIcon);
BitBlt(dcDest, 0, 0, cx, cy, dcSrc, x, y, SRCCOPY);
SelectObject(dcDest, hOldDestBmp);
SelectObject(dcSrc, hOldSourceBmp);
*pIcon = ImageList_Add(m_hImageList, hIcon, NULL);
DeleteObject(hIcon);
DeleteDC(dcSrc);
DeleteDC(dcDest);
ReleaseDC(hWnd, hDCWindow);
I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.
My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.
Here's the line where I setup the new window:
hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);
For the call to RegisterClassEx, I have this set for the background:
wcex.hbrBackground = (HBRUSH)0;
Here is my handler for WM_PAINT message:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
graphics.DrawImage(*m_pBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
}
One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.
When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).
I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.
What can be done to get the window to repaint when the contents under it changes?
I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:
Displaying a Splash Screen with C++
Part 1: Creating a HBITMAP archive
Part 2: Displaying the window archive
Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = 0;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
Use the SetLayeredWindowAttributesarchive function, this allows you to set a mask color that will become transparent, thus allowing the background to show through.
You will also need to configure your window with the layered flag, e.g.:
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
After that it's fairly simple:
// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);
When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:
Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above