Shell_NotifyIcon() and an invisible window - c

I'd like to use Shell_NotifyIcon() to add an icon to the system tray but I do not want to have a visible window in my application. That's why I'm passing a handle to a message-only window to Shell_NotifyIcon(). The window has been created like that:
hTrayWnd = CreateWindowEx(0, CLASSNAME, "tray_dummy", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, HINSTANCE, NULL);
CLASSNAME points to the name of my class that I've registered using RegisterClassEx() and HINSTANCE is the handle of my instance.
Now the problem is: The tray icon is added but the WindowProc() of my class is never called. If I use a normal top-level window instead, everything is working fine.
Is there no way to create a tray icon without a top-level window? I've seen apps having a tray icon without a top level window so it must be possible somehow. But why does it not work with a HWND_MESSAGE window?
Thanks!

Oops, sorry, it was my fault. The reason why it didn't work was that I called CreateWindowEx() from a different thread. When I use the main thread it is working fine.

Related

parts drawn with DrawThemeBackground on Windows 10 are not correct

Problem description
I want to create a Windows API app in C which renders the menu and the caption buttons in the same non-client area, similar to Firefox
In order to do this, I've determined the solution needs to:
be type WS_POPUP, in order for the menu to be aligned to the top
take ownership of the non-client area (where the menu is rendered)
manually render the minimize/maximize/close buttons
The solution needs to work on Windows 7, 8, and 10 (and ideally future versions too).
How it looks now
I have a test program available on GitHub.
In my app, I've overridden the appropriate events:
WM_NCCALCSIZE, WM_NCHITTEST, WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMOUSEMOVE, WM_NCPAINT
And then I repaint non-client areas on these events:
WM_NCACTIVATE, WM_SETTEXT
Here's an example of how I'm doing the rendering:
// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;
// ...
LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
RECT windowRect;
HRGN hRgn = NULL;
GetWindowRect(hWnd, &windowRect);
if (wParam == 1) {
hRgn = CreateRectRgnIndirect(&windowRect);
} else {
hRgn = (HRGN)wParam;
}
if (hRgn) {
// Carve out the area for custom content
HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
DeleteObject(captionButtonRgn);
// Force default painting for non-client area
LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
// black background covering part of menu, behind buttons
HDC hDC = GetWindowDC(hWnd);
FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));
DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);
CloseThemeData(hTheme);
}
}
The rendered result looks like this:
Unfortunately, the styles used for the parts (minimize, maximize/restore, close) look like the styles for Windows 7/8, and not the native Windows 10 controls. I've been searching for a way to do this for several days without luck. I need help understanding how to render these buttons for Windows 10 using the Windows API.
Current status (and what I've tried so far)
My first hunch was that I need to properly enable Visual Styles.
Per this article, calls checking the OS version will get Windows 8 unless you specifically target Windows 10 via a manifest. Click here to view my manifest. This does work:
Before GetVersionEx returned major=6, minor=2, build=9200
Now GetVersionEx returns major=10, minor=0, build=10586
Per the official "Enabling Visual Styles" article, I made sure to use Common Controls version 6.
Added linker input for Comctl32.lib
Call is made to InitCommonControls on program start
Added dependency for version 6 to the application manifest
Here are some screenshots of relevant project settings that I have tried:
Other ideas
I'm running low on things to try. Before throwing in the towel, there were some things I was going to try:
Idea 1: using GetThemeStream which allows you to retrieve the size/bitmap for controls.
Load aero msstyles file like so:
HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);
Get the bitmap for the part (minimize button, maximize button, etc) like so (passing the loaded theme file):
GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);
Load the bitmap; it appears to be in PNG format (I haven't gotten this far)
Draw the bitmap
Idea 2: copy the non-client area from a hidden window which has the caption area (and minimize, maximize, close buttons).
Create a window which has the caption and min/max buttons, never activating it.
In the non-client paint, get the DC for that Window and capture the pixels for the min/max/close button
Render them using bitblt
I think the issue comes from trying to use WM_NCPAINT on OS version >= Win Vista.
Since Vista all NC rendering is controlled by DWM (desktop window manager). If you still dare to handle WM_NCPAINT, DWM rendering will be turned off and you get "old-school" look:
From the Shell Revealed Blog:
The DWM doesnt have any legacy worries because applications cannot
draw inside the glass frame, since its rendered and managed by a
totally different process. If an application tries to do it, Windows
will detect it and remove the glass frame entirely (and therefore
revert to the Basic frame), so that the application can draw what it
wants to draw.
To get proper results, you have to do it the "DWM way" (specifically the sections "Removing the Standard Frame" and "Drawing in the Extended Frame Window"). This works by letting DWM render the frame within the client area, so you can paint over it. Also with this solution you don't have to draw the caption buttons on your own. This answer summarizes the required steps (under "Aero supported solution").
The caveat is that you propably have to draw the menu on your own and you can't use most of GDI API for that, because GDI ignores the alpha channel and things will look ugly if the frame is translucent (Vista and Win 7 by default, Win8+ with extensions). BitBlt() works if the source is a memory DC that contains a 32bpp bitmap with an alpha channel. GDI+ works aswell.

Win32, and the window style

So I have a window, and I have coded it so that during run-time it can enter and exit full-screen mode. Entering full-screen works, but exiting places the window tile bar in reverse order.
Exit full screen code:
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 640, 480, NULL);
InvalidateRect(hWnd, NULL, TRUE);
Picture of the result: https://www.dropbox.com/s/p15eltz7b2hxx4y/window.png?dl=0
I tried using GWL_STYLE instead of GWL_EXSTYLE but that works even worse, with the window being visible but clicking anything on the window will act like the window is not there and the click on whatever is behind it...
Thanks!
Philip
Just a thought, couldn't you get the window style (with GetWindowLongPtr), store it as a member variable in you class and then use this as the style to reset in SetWindowLongPtr?
Following is uncheck code (this is assuming you are using C++),
MainWnd::OnFullScreen(...)
{
m_oldStyle = GetWindowLongPtr(GWL_EXSTYLE, m_hwd);
/*
what ever other code is necessary
*/
}
MainWnd::OnExitFullScreen(...)
{
SetWindowLongPtr(m_hwn, GWL_EXSTYLE, m_oldStyle);
/*
and other code as needed
*/
}
I've made two assumptions here:
(1) that you will have two variables, one to contain the old style (m_oldStyle) and one to hold the handle to the window (m_hwd). Note if you are doing strict SDK style coding then the handle will be passed to you as part of WndProc. If you are using MFC there should be member function in the class you derived you main window from. In other cases you are on your own.
(2) the second assumption is that SetWindowLongPtr is called prior to any change of screen type. I believe that SetWindowLongPtr is called during window construction, but it has been several years since I've done serious windows programming using Microsoft frameworks (now I tend to used QT's framework).

flicker on tab control - WIN32

I have a WIN32 application. Its main window is hwndMain, one of its child is hwndView. There is one tab control hwndTab on hwndView.
When I resize hwndMain, hwndView is resized and so is hwndTab. It flicker a little, but not much.
I have tried to use WS_EX_COMPOSITED style ( for hwndView or hwndTab), but it just gave me blank window. I tried to use WS_EX_TRANSPARENT and it solves flicker, but when the windows is resized to be larger, the childs are updated very slow, e.g I see black region for one second, then the region is updated.
I have successfully sloved the flicker issue for TreeView by using WS_CHIPCHILDREN style. (See remark below). But using WS_CHIPCHILDREN stlye for hwndView doesn't fix the flicker issue for tab control.
I have paid attention to WM_ERASEBKGND and Not set hbrBackground also.
I want to use double buffer for tab control, but I can't find a tutorial for this purpose. All the tutorial I found is: In WM_PAINT, after creating CompatibleDC and CompatibleBitmap, draw what you want in memdc and.....; But I don't want to do any custom drawing in WM_PAINT for hwndTab. I just want to leave the tab control do this job, but shows the final result only.
Could someone show me a small example how to double buffer a tab control (if you think this will fix the flicker issue of tab control), in the language c + winapi, since I don't have the knowledge of C#, Net,..etc.
Remark: For my TreeView, it is a child of a window hwndContainer. It is created as:
win->hwndContainer = CreateWindowEx(
WS_CLIPCHILDREN,
_T("SUMATRA_PDF_TOCBOX"), NULL,
WS_CHILD,
0, 0, gGlobalPrefs.sidebarDx, 0,
win->hwndPanel, NULL,
ghinst, NULL);
Using WS_CLIPCHILDREN fix the flicker, even if I don't use double buffer. But it is strange to put
WS_CLIPCHILDREN in the first parameter position. If I put it after WS_CHILD, i.e
win->hwndContainer = CreateWindowEx(
NULL,
_T("SUMATRA_PDF_TOCBOX"), NULL,
WS_CHILD | WS_CLIPCHILDREN,
0, 0, gGlobalPrefs.sidebarDx, 0,
win->hwndPanel, NULL,
ghinst, NULL);
,then the flicker still occurs.
So I also tried to use the first way when I created hwndView, but it just gave blank white window.
I am really confused with these stuff.
Here is the blank window picture when I used WS_EX_COMPOSITED for hwndView.
There is no such problem when I used it for hwndContainer.
hwndView in fact has two child: a Tab Control hwndTab and a child which has its own double buffer and drawing. I am not sure if this cause the problem for using WS_EX_COMPOSITED.
You are using the WS_EX_COMPOSITED style. When you pass WS_CLIPCHIDREN as the first argument to the CreateWindowEx, it's interpreting the value of WS_CLIPCHILDREN as an extended window style. Since the value of WS_CLIPCHILDREN is 0x02000000L, the same as WS_EX_COMPOSITED, you've just created a composited window.
And a composited window, according to the documentation, has all of its descendants painted in a bottom-to-top painting order using double-buffering.
I'm not sure what you mean when you say:
I have tried to use WS_EX_COMPOSITED style ( for hwndView or hwndTab), but it just gave me blank window.
You'll have to post code the reproduces this problem. But your second-to-last code snippet is producing a composited window.

How to hide window from "Applications" tab in task manager?

I have question regarding the CreateWindowEx function. I have 2 windows, a main one and a popup one. I want a popup window to hide everywhere. It is currently not displayed in taskbar and it is not even visible in alt+tab menu. However it is visible on "Applications" tab in task manager. What flags do I need to use in CreateWindowEx to hide my popup window from there?
Current code:
hHistoryWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED, szAppName, L"HistoryWindow", WS_EX_TOPMOST | WS_POPUP, WIDTH, TOP, width, height, NULL, NULL, hInstance, NULL);
I also wanted to ask, whether I need to release a bitmap resource from "static" window before using DestroyWindow() function? I set image to a "static" window this way:
SendMessage (hStatic, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hWhiteStone);
Is it enough to release hWhiteStone or do I need to release also handle returned by SendMessage() (- or is it done automatically by DestroyWindow)?
Thank you for any info.
Kra
Make it a child of your main window. Do this by changing the fourth last parameter to the HWND of your main window. This SHOULD make windows treat your popup window as part of the same application as your main window.

How do you place sub controls inside a group box?

When I enable common control visual style support (InitCommonControls()) and I am using any theme other then Windows Classic Theme, buttons inside a group box appear with a black border with square corners.
Windows Classic Theme appears normal, as well as when I turn off visual styling.
I am using the following code:
group_box = CreateWindow(TEXT("BUTTON"), TEXT("BS_GROUPBOX"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX | WS_GROUP,
10, 10, 200, 300,
hwnd, NULL, hInstance, 0);
push_button = CreateWindow(TEXT("BUTTON"), TEXT("BS_PUSHBUTTON"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
40, 40, 100, 22,
group_box, NULL, hInstance, 0);
EDIT: The issue occurs with radio buttons as well
EDIT: I am not using any dialogs/resources, only CreateWindow/Ex.
I am compiling under Visual C++ 2008 Express SP1, with a generic manifest file
Screenshot http://img.ispankcode.com/black_border_issue.png
The problem is having the groupbox as the controls' parent. Groupboxes are not supposed to have any children and using them as parents will cause all kinds of errors (including painting, keyboard navigation and message propagation). Just change the parent in the buttons' CreateWindow call from group_box to hwnd (i.e. the dialog).
I'm guessing you used the groupbox as the parent in order to position the other controls easily inside it. The proper way to do this is to get the position of the groupbox client area and map it to the client area of the dialog. Everything placed in the resulting RECT will then appear inside the groupbox. Since groupboxes don't actually have a client area, it can be calculated with something like this:
// Calculate the client area of a dialog that corresponds to the perceived
// client area of a groupbox control. An extra padding in dialog units can
// be specified (preferably in multiples of 4).
//
RECT getClientAreaInGroupBox(HWND dlg, int id, int padding = 0) {
HWND group = GetDlgItem(dlg, id);
RECT rc;
GetWindowRect(group, &rc);
MapWindowPoints(0, dlg, (POINT*)&rc, 2);
// Note that the top DUs should be 9 to completely avoid overlapping the
// groupbox label, but 8 is used instead for better alignment on a 4x4
// design grid.
RECT border = { 4, 8, 4, 4 };
OffsetRect(&border, padding, padding);
MapDialogRect(dlg, &border);
rc.left += border.left;
rc.right -= border.right;
rc.top += border.top;
rc.bottom -= border.bottom;
return rc;
}
Note that the same applies to Tab controls. They too are not designed to be parents and will exhibit similar behavior.
Just a guess here, but it looks like you are inheriting either the Static Edge or Client Edge style from you theme. I create most of my dialogs from the resource editor and set these properties there.
In your case, you can replace your CreateWindow with a CreateWindowEx to set these extended styles, which are probably being defaulted in CreateWindow. Specifically check out WS_EX_STATICEDGE, WS_EX_WINDOWEDGE and WS_EX_CLIENTEDGE
Edit: I'm assuming that this is not happening because you button is the default control in the dialog, which would also give a black edge.
Apparently group boxes are not meant to group controls (be a parent hwnd)
So in order to get rid of the black borders/painting issues I would have to subclass group box and implement WM_PAINT and WM_PRINTCLIENT
Ahh yes the black background with radio buttons and group boxes. Although I'm not sure if this will work for VC++ 2008, but back-in-the-day the solution for VB6 themed apps was to put the radio controls on a PictureBox (a generic container really) first and then add that to the group box.
Its worth a shot!

Resources