Enabling Aero Glass in custom-drawn window title (WM_NCPAINT, DwmSetWindowAttribute) - vb6

Some years ago we developed an ActiveX component called CaptionX (CaptionX Home Page). It allows the developers to place custom clickable graphical buttons into the window title bar.
All works fine in any Windows except Vista and Windows 7 when Aero Glass is turned on - the title bar with our custom icons is drawn as if we used the Windows Basic theme without the transparency effect for the window borders.
People would like to use CaptionX in the latest versions of the OS, but we cannot make it working.
All searches in the Internet tell us we need to enable Aero Glass when we use custom drawing in the window title using the DwmSetWindowAttribute API call with the DWMWA_NCRENDERING_POLICY attribute, but we have not managed to make it working.
Our code that draws on the window's non-client surface looks like this (sorry - it's the old VB6 :):
Friend Function WindowProc(ByVal lPrevWndProc As Long, ByVal hwnd As Long, ByVal iMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Select Case iMsg
Case WM_NCPAINT
DoNCPaint lPrevWndProc, wParam
WindowProc = 0
Exit Function
Case WM_...
' Other message handlers
End Select
WindowProc = CallWindowProc(lPrevWndProc, hwnd, iMsg, wParam, lParam)
End Function
We added the following call
DwmSetWindowAttribute m_hwnd, DWMWA_NCRENDERING_POLICY, DWMNCRP_ENABLED, 4
to many places in our code, but it does not have any effect. We can disable the Aero Glass effect if it is used by default in a window using DwmSetWindowAttribute, but cannot enable it.
What are we doing wrong? Do we need to add more API calls to our code, and if so where?

There may be a mistake on your calling, you should not pass a plain DWMNCRP_ENABLED value into the API, instead, you should pass a ref of a DWMNCRENDERINGPOLICY structure.

This old approach does not work in latest versions of Windows starting from Vista because of the new Desktop Window Manager (DWM) responsible for drawing the window frame. Here is also one interesting blog found in the web.archive.org that explains the nature of the problem:
Frequently asked questions about the Aero Basic window frame
The pertinent excerpt is the following:
The DWM doesn't have any legacy worries because applications cannot
draw inside the glass frame, since it's 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 modify the window title bar in Windows Vista, 7 and so on, we need to use the new DWM API.

Related

How to create a usual non-resizible window with thick border (MFC or WinAPI)

I want to create a window with the following properties:
Thick border ("double border" as described in MSDN)
Not resizable
Icon in the title
Close button in the title
It sounds so simple and we can find this kind of windows everywhere in Windows. But as soon as I disable ability to resize my window I also lose the thick border. Here is the illustration of my problem:
The height of my test window's caption bar is slightly less than that of a Shell property dialog, but I would like it to be the same height.
I have tried to use WinSpy and copy all styles from Explorer's window to mine, but this did not help.
I don't know what's going on under the hood, but I found two solutions:
Switch to the newer Platform Toolset (which means drop support of Windows XP)
Disable resizing by writing custom WM_NCHITTEST handler
The second option is quite simple and I do not want drop Windows XP support for a silly reason. Job is done:
LRESULT CListEditorDlg::OnNcHitTest(CPoint point)
{
LRESULT retVal = CDialog::OnNcHitTest(point);
switch (retVal)
{
case HTBOTTOM:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
case HTLEFT:
case HTRIGHT:
case HTTOP:
case HTTOPLEFT:
case HTTOPRIGHT:
retVal = HTBORDER;
}
return retVal;
}

Difference between NOTIFYICON_VERSION and NOTIFYICON_VERSION_4 used in NOTIFYICONDATA structure?

When adding a system tray icon from in Windows there are two versions of API that we can pass to Shell_NotifyIcon() via NOTIFYICONDATA structure. There are subtle differences between the two API, and these are not listed anywhere on MSDN. It took me some effort to figure out some of the differences, which I am going to share now. Improvements/additions to the answer are always welcome.
PS: This question is purely for sharing what I have learnt over last few days experimenting with windows DPI scaling.
uVersion member of the NOTIFYICONDATA structure can have 3 possible values, representing the version of the API being used to create the taskbar icon.
0 Use this value for applications designed for Windows versions prior to Windows 2000.
NOTIFYICON_VERSION Use the Windows 2000 behavior. Use this value for applications designed for Windows 2000 and later.
NOTIFYICON_VERSION_4 Use the current behavior. Use this value for applications designed for Windows Vista and later.
When it comes to message handler for the tray icon, the wParam, and uParam have the differences as illustrated in the following image.
Notice that in NOTIFYICON_VERSION_4 the wParam gives X, and Y coordinates of various events, but there is no provision for getting the coordinates in NOTIFYICON_VERSION. This gives rise to an interesting behaviour (which was a cause of a BUG I was trying to solve). If you use NOTIFYICON_VERSION, and then invoke the context menu of the tray icon, then the mouse cursor, wherever it may be while you are invoking the menu, gets placed right at the center of the tray icon. Even if you use keyboard (WINDOWS+B) for invoking context menu of the icon, the mouse cursor still moves to the icon.
This may not be of particular interest to you until you look at this particular BUG I am trying to solve in Pico torrent application.
Here is the scenario.
OS : Windows 10
Application isn't per-monitor DPI aware, but is system level DPI aware.
There is an initial value of Desktop scaling set, say 150%, when the user logs in.
Pico torrent is running.
DPI scaling value is changed to, say 125%
Pico torrent's context menu is invoked
The context menu will not be displayed at its proper place, and will be displaced a little, showing a deviance.
See the following images to understand what's happening.
The problem is that although MSDN says that GET_X_LPARAM(wParam), and GET_Y_LPARAM(wParam) should give correct values in the handler of tray icon, but it doesn't, in the presence of DPI scaling (i.e. for a change in DPI scaling without doing a sign out and sign in). On the other hand the API GetCursorPos() returns the correct value of mouse cursor coordinates. Note that NOTIFYICON_VERSION_4 along with GetCursorPos() will not work, since the context menu can be invoked using keyboard, at which the mouse cursor can be anywhere on the screen(s).
So, how do you combine all the knowledge just learnt to display the tray icon's context menu correctly when DPI scaling is done in the manner above, without making you application per-monitor DPI aware (for per-monitor DPI aware applications GET_X_LPARAM(wParam), and GET_Y_LPARAM(wParam) always return correct value)?
Use NOTIFYICON_VERSION instead of NOTIFYICON_VERSION_4, this will position the mouse cursor at the tray icon when context menu is invoked, and then use GetCursorPos() to get mouse cursor's position. Display the context menu using TrackPopupMenu() with the coordinates.
PS: In the example above the DPI scaling value is changed from 150% to 125%. The context menu deviance is more pronounced when DPI scaling is done from a bigger value to a smaller value, when your tray icon area is on lower right of the screen. This is because when DPI scaling is done, and windows magnifies UI elements which are not per-monitor aware, using DPI virtualization, then things move right-wards, and down-wards. eg. if in an application a windows rectangle is (0,0,100,100) (screen coordinates), then after magnification to 150%, it may become (0,0,150,150). Now for tray icon's menu, if you specify coordinates which lie beyond bottom-right of the screen, then the OS will still display is at a bottom right position which lies inside the screen, and which ensures that menu is displayed properly. eg. if a screen is 1920x1080, and TrackPopupMenu()is given (10000,10000) for menu, the menu will still be displayed inside the 1920x1080 screen rectangle. Thus increasing DPI scaling will not move the context menu any further, if it has already reached the right-bottom most position.
#sahil-singh you are right about it, and I agree with everyone else that your application should be DPI aware, but when this is not a point here.
I had a similar issue where my application is (still) not DPI aware and GET_X_LPARAM(wParam) will return non-virtual coordinates. After passing this value to the TrackPopupMenu() I get wrong position on the screen.
The best way is to use GetMessagePos() instead of wParam. In this case, Windows will give you new DWORD with virtual coordinates and then use GET_X_LPARAM/GET_Y_LPARAM to get a values that you can pass to the TrackPopupMenu().
This seems to (2 years later in 2019) be documented on MSDN:
NOTIFYICONDATAA structure
uCallbackMessage
Type: UINT
When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the message contains the identifier of the taskbar icon in which the event occurred. This identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard message associated with the event. For example, when the pointer moves over a taskbar icon, lParam is set to WM_MOUSEMOVE.
When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive notification events in the form of application-defined messages through the uCallbackMessage member, but the interpretation of the lParam and wParam parameters of that message is changed as follows:
LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or WM_CONTEXTMENU.
HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.
GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN, NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST. If any of those messages are generated by the keyboard, wParam is set to the upper-left corner of the target icon. For all other messages, wParam is undefined.
GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages as defined for the X anchor.

Sending WM_HSCROLL to other program

Im trying to scroll other program (PowerPoint 2013) by sending WM_HSCROLL,
and it will work only if I offer the correct LPARAM (not NULL) to SendMessage.
The value of LPARAM is dynamic, it will change if you close and open a new program.
Now I can only get its value by hooking WM_HSCROLL and clicking scroll bar manually.
// This is my code:
LPARAM lParam = 0x0a616c38; // Found by hooking WM_HSCROLL
SendMessage(hTarget, WM_HSCROLL, SB_LINERIGHT, lParam);
So is it possible to get the correct LPARAM programatically?
Many thanks!
p.s. The scroll bar is not a real window in PowerPoint 2013.
p.s. It returns 1 if I use GetScrollInfo(hTarget, SB_CTL, &scrollinfo), but all values inside scrollinfo are zero.
p.s. Office Home and Student 2013 Official Site
Did you try to call GetLastError?
GetScrollInfo will probably not work across process boundaries, so I would say that's why you're not getting valid values back.
The lParam value of a WM_HSCROLL message is either NULL for the standard window scroll bar, or the handle of the scroll control. The handle of the scroll control will obviously change every time the program is run, so you need to find that out yourself before you can reliably simulate scroll input.
To do this, you can use the FindWindowEx function to search the parent window (hTarget in your example) for child windows of class "SCROLLBAR". As you will probably find more than one scrollbar child window you'll need some way to tell them apart - most probably, via the window's GWL_ID value as this will probably not change from run to run.

MFC - MDI main frame title is truncated when maximized child window has very long title

I have an MFC application using MDI, something similar to the MFC Scribble example/tutorial. When the child window is maximized, the caption/title of the main frame window is automatically adjusted so that the title of the child window is appended, like "Scribble - [Scribb1]". When the title of the child window is very long, it is truncated. The length of whole caption seems to be limited to about 160 characters. Is there a way to increase this limit and show more characters? I guess overriding WM_NCPAINT to redraw the caption is one way, but prefer other simple solution if there is one. I've tried overriding OnUpdateFrameTitle() and it doesn't seem to work for the whole combined caption.
Overriding OnUpdateFrameTitle and calling own implementation of the UpdateFrameTitleForDocument won’t work. Amit already tried your solution before even posting.
This is not even implemented by MFC. MDI window is not introduced by MFC. MDI frame window existed since I remember as part of the Windows OS.
Default MFC implementation down in the bowels of the MFC calls special MDI window procedure defined as DefFrameProc, as required.
Windows implementation for some mysterious reason truncate window text (displayed on the title bar of the MDI frame) to 160 characters, including terminating 0 when processing WM_SETTEXT message. This is probably the remnant of the old computers that did not have much memory to waste and all buffers sizes were kept to a minimum.
This is probably the remnant of the old computers that did not have much memory to waste and all buffers sizes were kept to a minimum and file names were 8.3 total 12 characters.
To fulfill your requirements and display full text you would have to make some changes.
Fortunately, I found really easy way of changing this behavior, in agreement with my MEMC requirement.
In your main frame class override DefWindowProcand change the code as follows:
LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if(WM_SETTEXT == message)
{
// if set text (called from UpdateFrameTitleForDocument)
// do not call DefWindowProc
return CWnd::DefWindowProc(message, wParam, lParam);
}
// otherwise call DefWindowProc that in turn calls DefFrameProc
return CMDIFrameWndEx::DefWindowProc(message, wParam, lParam);
}
Now build and run.
This works for classinc and Visual studio styles of the MFC app.
Have a look at CFrameWnd::OnUpdateFrameTitle which internally is calling CFrameWnd::UpdateFrameTitleForDocument() method. It does the magic. It is a virtual method. You can override OnUpdateFrameTitle in your CMainFrm class and set the window title as desired.
If you did not know, MFC library is provided totally with the source. The CFrameWnd source is in winfrm.cpp (search for this file, and you will learn where the source for MFC on your PC)

drawing under icons on desktop

I wrote a program who paints widget on desktop wallpaper, under desktop icons, and all was good.
But I reveal a strange bug - my program was not works on some other computers - they displays
the same desktop without widget after start.
So, have anybody any expirience, some knowledge in this area? Please, help me! :)
(I can't find any computers near, where widget not works, in my computer all works well.
I wrote a lot of test programs, whose tries draw anything on desktop with rather methods,
but test displays nothing too in my friends computers.)
Drawing method:
I wrote a dll, who finds desktop window (progman->child->child ))
and creates a hook and install own window procedure for desktop.
In window procedure i hook WM_ERASEBKGND:
case WM_ERASEBKGND:
HDC hdc = (HDC)wparam;
PaintDesktop(hdc);
WidgetPaint(hdc);
return 1;
// I skip in this code getting and setting clipping areas for fast drawing.
(I tried all combinations with clipping and without clipping)
And in tests I also trying to paint widget in WM_PAINT message with same results -- all already works at me, but works not at all my friends.
upd: I solved it! :)
I solved it!
It's simple was a 64-bit machines)
So I compile my code for 64-bit and widget start work pretty good!
And, also -
PaintDesktop(hdc);
will produce very bright wallpaper in Windows7 (and Vista) with Aero.
(User's wallpaper comes brighten).
Must be
CallWindowProc(OldWndProc, WM_ERASEBKGND, wparam, lparam);
Cause of this - Aero treats PaintDesktop() as usual paint (over virtual screen wallpaper, that is the same as on desktop) with color summation.
Regards! :)

Resources