Changing taskbar icon programmatically (Win32,C++) [duplicate] - c++

This question already has an answer here:
Is there a Windows API for adding badges to taskbar icons?
(1 answer)
Closed 9 years ago.
I have a C++ win32 program, and I'd like to edit the taskbar icon at runtime to display alerts, etc about the program, however I'm not too experienced with the win32 api, and I haven't been able to find anything online. The closest I've found is http://www.windows-tech.info/17/52a5bfc45dac0ade.php which tells how to load the icon off the disc at runtime and change it.
I would like to do what they do in this question: Create an icon in memory with win32 in python but in C++ and without an external library.

I would like to do what they do in this question: Create an icon in memory with win32 in python but in C++ and without an external library
Since the accepted answer uses the wxWidgets library, which is just a wrapper of the Win32 API, the solution translates pretty nicely.
All you need to do is create a bitmap in memory using the CreateCompatibleBitmap function. Then you can draw into that bitmap using the standard GDI drawing functions. Finally, you create the icon using the CreateIconIndirect function.
The hardest part is keeping track of your resources and making sure that you free them all when you're finished to prevent memory leaks. It's way better if it's all wrapped up in a library that makes use of RAII to ensure the objects are properly freed, but if you're writing C code in C++, it would look like this:
HICON CreateSolidColorIcon(COLORREF iconColor, int width, int height)
{
// Obtain a handle to the screen device context.
HDC hdcScreen = GetDC(NULL);
// Create a memory device context, which we will draw into.
HDC hdcMem = CreateCompatibleDC(hdcScreen);
// Create the bitmap, and select it into the device context for drawing.
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, width, height);
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);
// Draw your icon.
//
// For this simple example, we're just drawing a solid color rectangle
// in the specified color with the specified dimensions.
HPEN hpen = CreatePen(PS_SOLID, 1, iconColor);
HPEN hpenOld = (HPEN)SelectObject(hdcMem, hpen);
HBRUSH hbrush = CreateSolidBrush(iconColor);
HBRUSH hbrushOld = (HBRUSH)SelectObject(hdcMem, hbrush);
Rectangle(hdcMem, 0, 0, width, height);
SelectObject(hdcMem, hbrushOld);
SelectObject(hdcMem, hpenOld);
DeleteObject(hbrush);
DeleteObject(hpen);
// Create an icon from the bitmap.
//
// Icons require masks to indicate transparent and opaque areas. Since this
// simple example has no transparent areas, we use a fully opaque mask.
HBITMAP hbmpMask = CreateCompatibleBitmap(hdcScreen, width, height);
ICONINFO ii;
ii.fIcon = TRUE;
ii.hbmMask = hbmpMask;
ii.hbmColor = hbmp;
HICON hIcon = CreateIconIndirect(&ii);
DeleteObject(hbmpMask);
// Clean-up.
SelectObject(hdcMem, hbmpOld);
DeleteObject(hbmp);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
// Return the icon.
return hIcon;
}
Adding the error checking and the code to draw something interesting onto the bitmap is left as an exercise for the reader.
As I said in a comment above, once you have created the icon, you can set the icon for a window by sending it a WM_SETICON message and passing the HICON as the LPARAM:
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
You can also specify ICON_SMALL in order to set the window's small icon. If you set only a big icon, it will be scaled down to create the small icon automatically. However, if you set only the small icon, the window will continue to use the default icon as its big icon. Big icons typically have a dimension of 32x32, while small icons typically have a dimension of 16x16. This is not, however, guaranteed, so do not hardcode these values. If you need to determine them, call the GetSystemMetrics function with SM_CXICON and SM_CYICON to retrieve the width and height of big icons, or SM_CXSMICON and SM_CYSMICON to retrieve the width and height of small icons.
A fairly good tutorial on drawing in Windows using GDI is available here. I recommend reading it thoroughly if this is your first time doing this and have no prior experience with GDI.

Related

Capturing invisible window with win32 API

Trying to capture contents of windows using BitBlt. In this particular case, which is probably important, i am speaking of invisible windows/invisible areas on windows - invisible because of z-order, they are visible but covered with other windows. All i get is a black box.
HDC winDC = GetWindowDC(hwnd);
HDC hdc_offscreen = CreateCompatibleDC(winDC);
HBITMAP bmp = CreateCompatibleBitmap(winDC, areaWidth, areaHeight);
HGDIOBJ origHandle = SelectObject(hdc_offscreen, bmp);
BitBlt(hdc_offscreen, rect.left, rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
winDC, copy_from.left, copy_from.top, SRCCOPY);
// at this point i expect pixels to be there, but nah!
ReleaseDC(hwnd, winDC);
SelectObject(hdc_offscreen, origHandle);
DeleteDC(hdc_offscreen);
ReleaseDC(hWnd_main, winDC);
hwnd is the window i am trying to capture. Same things works like a charm with Aero theme enabled. I am using Windows 7 Professional.
Any idea what i am missing?
Your best bet (though it's not guaranteed to work) is to ask the other window to paint itself to your DC by sending it WM_PRINT.
HDC returned by GetWindowDC just doesn't contain a window's image. I can imagine that this HDC refers to the same "bitmap" as other windows. When Aero enabled, they apparently doesn't share the same "bitmap" and that's why you get a good result.
I would rather try to force window to paint to your HDC instead of reading HDC returned by GetWindowDC. To get client area you can try to call BeginPaint with your HDC, but who knows what if a window will check whether it (or it's part) is visible or not?
To get non client area you could pass WM_NCPAINT with your HDC.
Standard controls support HDC passed with WM_PAINT. Other windows may support it, but it's not required. If I remember correctly PrintWindow relies on this behavior.
In general I would say that no a 100% way to get a window's image but BeginPaint and WM_NCPAINT should be a good point of start.

PrintWindow causes flicker, strange artifacts in titlebar

I'm trying to take a screenshot of a Chrome window. It looks like this:
When I use PrintWindow to get the screenshot, I can see a flicker on the window titlebar/Chrome tab area. The captured screenshot contains a strange rendering of a titlebar in Windows Basic style (even though my machine runs the Aero theme):
I've noticed that some other apps also exhibit a similar behavior where they flicker on-screen but the titlebar artifact is not visible in the captured screenshot. Apps that do this include Office 2010, IE 10, and the Trillian tabbed chat window — in other words, windows that extend the non-client area seem to have this issue.
The code that reproduces this is simple:
void Screenshot(HWND hWnd) {
RECT rc;
GetClientRect(hWnd, &rc);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
//Print to memory hdc
PrintWindow(hWnd, hdc, PW_CLIENTONLY);
}
Why am I seeing flickering and strange visual artifacts? What can I do to stop it?
For those, who have the same problem, do this:
const uint PW_RENDERFULLCONTENT = 0x00000002;
PrintWindow(hWnd, hDC, PW_RENDERFULLCONTENT);
If Aero is enabled, use BitBlt instead.
This comment in the chromium desktop capture source code was especially helpful:
// When desktop composition (Aero) is enabled each window is rendered to a
// private buffer allowing BitBlt() to get the window content even if the
// window is occluded. PrintWindow() is slower but lets rendering the window
// contents to an off-screen device context when Aero is not available.

Transparency in C++

I'm trying to create transparency in my application.
For instance the window of my app is square and i want to make it round, by hiding parts of the window.
My code looks something like this:
HDC hdcMask = nullptr;
HBITMAP hBMP = (HBITMAP)LoadImageW(nullptr, L"C:\\mask.bmp", IMAGE_BITMAP, 150, 160, LR_LOADFROMFILE);
SelectObject(hdcMask, hBMP);
HWND hWnd = GetActiveWindow();
HDC hdcWindow = GetDC(hWnd);
TransparentBlt(hdcWindow, 0, 0, 150, 160, hdcWindow, 0, 0, 150, 160, RGB(0,0,0));
where mask.bmp is a bitmap where white is what i want to be transparent and black is what I want to be visible.
After applying this code, nothings happens. What am I doing wrong ? Is there another method to obtain the desired result?
Note: I need this code to work on Windows XP OS or later.
There are several ways of making a window transparent and/or translucent.
SetWindowRgn will make parts of a window transparent.
SetLayeredWindowAttributes can make parts of a window transparent, and can also apply translucency to the whole of the rest of the window.
UpdateLayeredWindow can give individual windows different amounts of translucency.
Why don't you use a different format than bmp where you can also include alpha data?
You can use a PixelFormat32bppPARGB Bitmap, use Bitmap::LockBits an Bitmap::Unlockbits
to keep the file's format and avoid having the alpha setting overwritten.

Freehand Drawing on Entire Screen

I'm trying to make a simple little tool that would allow a user to switch from normal operation to a mode where all application messages are disabled and they can use the mouse to do some freehand drawing, then switch modes again to keep their drawing on the screen while they do whatever other normal stuff they want. This could, if I decide, evolve into a nice thing you could use to use a decorated screen by saving the decorations you do and loading them later.
When I started this (which was over half a year ago, soon after discovering the Windows API) I just did global mouse tracking and painted a circle wherever it was to a GetDC(NULL) hdc. The problems, of course, were that it would disappear when anything under it updated and would still have the mouse messages put through, so if I held down the button on the desktop, for example, it would put resizing rectangle things throughout the paintings.
Today, after finally having some spare time since the last major work on this most of that 6 months ago, I decided to remake it and see if I could achieve what I wanted. I made a transparent, topmost, WS_CHILD, layered, maximized window (basically the screen doesn't change, but there's a window on top of everything letting messages through). Next, I made it so that when it was in painting mode, it would set the alpha value to 1 and let the user paint. The thing I didn't realize until I did it was that since the alpha value of the window was 1, none of the painting would be visible.
Next, I tried using GetDC(NULL), but remembered that gets erased when something updates.
Now I just thought of using bitmaps and dcs to repeatedly store the screen into a dc, paint on another dc, and then copy it back to the one with the stored screen with transparency for the parts that aren't drawn on, and copy that back to the screen, but I'm losing a bit of heart. Here's my source code for that (the mask function is taken from this tutorial). Please tell me if some of this is unnecessary. I've used bitmaps for double buffering sure, but I'm not all that sure on where I need them.
//Global mask since it takes longer to make
HBITMAP mask;
//Window Procedure Start
HDC screenDC; //hdc for entire screen
screenDC = GetDC (NULL); //get DC for screen
HDC memDC = CreateCompatibleDC (screenDC); //create DC for holding the screen+paint
HBITMAP bm = CreateCompatibleBitmap (screenDC, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN)); //create bitmap for memDC
HDC paintDC = CreateCompatibleDC (screenDC); //create DC to paint on
HBITMAP paintBM = CreateCompatibleBitmap (screenDC, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN)); //create bitmap for paintDC
SelectObject (memDC, bm); //select bitmap into memDC
SelectObject (paintDC, paintBM); //select painting bitmap into paintDC
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), screenDC, 0, 0, SRCCOPY); //copy screen to memDC
SetBkColor (paintDC, RGB(0,0,0)); //set background of paintDC to black so it's all transparent to start
//WM_CREATE
mask = CreateBitmapMask (bm, RGB(0,0,0)); //create black mask (paint colours are limited 1-255 now)
//painting is done into paintDC
//at end of Window Procedure
SelectObject (paintDC, mask); //select mask into paintDC
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCAND); //this in combination with the next should make it bitblt with all of the black taken out I thought
SelectObject (paintDC, paintBM); //select bitmaps into DCs
SelectObject (memDC, bm);
BitBlt (memDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCPAINT); //second part of transparent bitblt
BitBlt (screenDC, 0, 0, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), paintDC, 0, 0, SRCCOPY); //copy memDC back to screen
DeleteObject (paintBM); //delete stuff
DeleteObject (mask);
DeleteDC (memDC);
DeleteDC (paintDC);
ReleaseDC (hwnd, screenDC);
//CreateBitmapMask() (taken directly from http://www.winprog.org/tutorial/transparency.html
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask;
BITMAP bm;
// Create monochrome (1 bit) mask bitmap.
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
// Get some HDCs that are compatible with the display driver
hdcMem = CreateCompatibleDC(0);
hdcMem2 = CreateCompatibleDC(0);
SelectObject(hdcMem, hbmColour);
SelectObject(hdcMem2, hbmMask);
// Set the background colour of the colour image to the colour
// you want to be transparent.
SetBkColor(hdcMem, crTransparent);
// Copy the bits from the colour image to the B+W mask... everything
// with the background colour ends up white while everythig else ends up
// black...Just what we wanted.
BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
// Take our new mask and use it to turn the transparent colour in our
// original colour image to black so the transparency effect will
// work right.
BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);
// Clean up.
DeleteDC(hdcMem);
DeleteDC(hdcMem2);
return hbmMask;
}
I know that code may very well be horrible. I'm taking all suggestions into account, it's just that I'm not too clear with this subject, and don't get everything that's happening, which makes it hard to fix. This code runs for me by pretty much putting a fullscreen black rectangle every so often.
My main question is: Is there any way I can paint onto the screen without it getting erased when windows underneath update? The only real thing I can think of now would be to store all of the locations of the tiny line segments the user draws and keep redrawing them on top of the screen. At first glance it seems very inefficient and wasteful of memory.
Also, I was pretty sure while writing this that I didn't need any code examples for the theoretical stuff before the supplied code segments. Most of it is gone now, but this really is more of a theory issue.
EDIT:
I just found out about the TransparentBlt function which seemed perfect for the situation, so I tried using that instead of the SRCPAINT and SRCAND BitBlts and it produced the same result: a black rectangle covering the screen, sometimes having parts disappear when my mouse moves over things.
Simplest way, perhaps:
When in non-drawing mode, use SetLayeredWindowAttributes to set a 'transparency key' color for the transparent window. Make the window's alpha fully opaque, but fill the window (FillRect or similar) with that key color, and it will all appear transparent. Then anything you draw in the non-key color will appear as solid, on top of all the windows beneath the transparent layered window.
To go into drawing mode, one approach is to create a new window with a captured bitmap of the desktop immediately under your transparent layer. Or avoid the bitmap, and make it slightly non-transparent and all a solid color - eg so it looks like the desktop is "greyed out". The key thing is that this window is not completely transparent, so it will be able to receive mouse input that you can then use to draw on the actual transparent layer.
I think you would be best of by creating a snapshot of the screen and save that in a bitmap (in the form of a memory DC) BEFORE you show a window which displays the contents of the memory DC in fullscreen. That way you actually fetch the messages caused by clicks etc on your own window and process them as usual.
Capture screen contents
Create window (full screen) and use captured contents
Do some drawing
Save the content (as bmp or anything fancy)
Close the window and return to regular desktop
Good idea?

screenshots of covered/minimized windows

I have the following code to take screenshots of a window:
HDC WinDC;
HDC CopyDC;
HBITMAP hBitmap;
RECT rt;
GetClientRect (hwnd, &rt);
WinDC = GetDC (hwnd);
CopyDC = CreateCompatibleDC (WinDC);
hBitmap = CreateCompatibleBitmap (WinDC,
rt.right - rt.left, //width
rt.bottom - rt.top);//height
SelectObject (CopyDC, hBitmap);
//Copy the window DC to the compatible DC
BitBlt (CopyDC, //destination
0,0,
rt.right - rt.left, //width
rt.bottom - rt.top, //height
WinDC, //source
0, 0,
SRCCOPY);
ReleaseDC(hwnd, WinDC);
ReleaseDC(hwnd, CopyDC);
It is someone elses code, slightly modified, as I am not really familiar with DC and how windows draws stuff to screen.
When I have one window slightly covering another, the covering window appears on screenshots of the covered one, which is kind of inconvenient. Also, when the window is minimized, this code produces nothing much interesting.
Is there any way around this? I would imagine that taking screenshots of a minimized application would be quite difficult, but I hope that getting screenshots of covered windows is possible. Perhaps there is a different method of implementing this to get around these problems?
No, a screenshot is exactly what it sounds like. You'll read the pixels out of the video adapter, what you get is what you see. You'll have to restore the window and bring it to the foreground to get the full view. WM_SYSCOMMAND+SC_RESTORE and SetForegroundWindow() respectively. Plus some time to allow the app to repaint its window if necessary.
The WM_PRINT message is available to ask a window to draw itself into a memory context. That could handle the overlapped window problem. But that can only work if is your window. And rarely has the expected result, programmers don't often implement WM_PRINT correctly.