How can I remove an arbitrary HWND border (WinAPI)? - c

Long story short, I'm building an i3-style tiling window manager, and need to override the chrome of arbitrary windows to compeltely remove borders, the titlebar, and associated buttons.
Having tried just about every logical combination of WS_STYLE and WS_EX styles relating to chroming, I'm getting nothing for most apps. It's fine with simple native programs (terminals work as expected), but for the vast majority of other apps, the same problem occurs;
The above result is present in most cases. There is a surrounding border which is of the same size, but with variant behaviour per app. As far as I can tell, this is the "transparent" (obviously not in this case) border used for mouse drag hittesting (although with the frame disabled in styling, this shouldn't be present). It seems that the framebuffer isn't being cleared in this area (i.e. if the window is resized into itself, the border is filled with previous pixels at that location).
This is a quick weekend project- my job isn't related to WinAPI programming, so this is all new to me, but if I'm assuming right then this border is effectively the non-client area? In other words, the non-clientarea must be zeroed so that the client area takes up the entire window rect. I've attempted to handle this by subclassing all target windows with GWL_WNDPROC and zeroing WM_NCCALCSIZE, but I'm getting the same result.
How can I force an arbitrary window (which I don't create myself) to completely remove it's entire non-client area?

I use the following function to remove/restore the borders of a window:
void WndSetFrame(HWND hWnd, int bSmall)
{
static int wwStyle=0; // remember initial style
int ww= GetWindowLong(hWnd, GWL_STYLE);
if (bSmall) {
wwStyle= ww;
ww &= ~(WS_OVERLAPPED|WS_CAPTION|WS_MAXIMIZEBOX|WS_MINIMIZEBOX|WS_SIZEBOX|WS_SYSMENU);
ww |= (WS_POPUP /*|WS_DLGFRAME*/);
if (ww==wwStyle) return;
SetWindowLong(hWnd, GWL_STYLE, ww);
}
else {
if (!wwStyle || ww==wwStyle) return;
SetWindowLong(hWnd, GWL_STYLE, wwStyle);
}
SetWindowPos (hWnd,0,0,0,0,0,SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
}

Related

How to prevent two Top Most windows from competing for visibility when moving?

My executable creates a window that constantly moves left and right using SetWindowPos on a 1millisecond timer.
When NOT given the WS_EX_TOPMOST style, opening two of the executables would have the second window being consistantly covered by the first window while both windows are moving.
However, once I gave the executable the WS_EX_TOPMOST style, running the executable twice would have both windows consistantly competing to be shown above one another.
I'm trying to make the windows topmost so they would always be shown, but not compete for visibility.
I suspected this issue to occur because of something to do with the Z order of topmost Windows, so I tried to arrange the Z order as such:
//prior to this, the window is NOT given the WS_EX_TOPMOST style
case WM_CREATE:
{
HWND testhWnd=FindWindow("name","name");
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
//Check if previous instance of window that was promoted to TOPMOST exists
if((exStyle & WS_EX_TOPMOST) ==WS_EX_TOPMOST)
//Precede current window over previous window in Z order
{
SetWindowPos(hwnd,testhWnd,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
else
{
SetWindowPos(hwnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
break;
}
But it doesn't seem to fix the issue. Is there even Z indexing in Topmost windows? How can I fix this issue? Thank you for reading!

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).

How to make label transparent without any flickering at load time

I have a panel and on that I've a picturebox. There are around 20 labels that I've to show in the panel. I want the background of Label to be transparent ie the image in picturebox is shown and the label displays only the text.
Now since labels do not exhibit true transparency I made the labels child of picturebox
this.lbl1.Parent = pictureBox1;
This has solved my immediate problem but now when the form loads, all the labels take a while to become visible and do so one at a time. I'd appreciate if you guys can give some solution for this.
Thanks in advance
The standard cure for flicker is double-buffering. But that cannot solve this kind of flicker. It is a different kind, caused by having multiple windows overlapping each other. Each label is its own window. When the form needs to paint itself, it draws its background leaving holes for the child windows. Each child window then takes a turn drawing itself. And their child windows draw themselves next. Etcetera.
This becomes noticeable when one control takes a while to draw, no doubt your picture box. Especially when it displays a large image that needs to be resized. The holes for the child windows stay unpainted while the picture box draws. They have a white background, black when you use the form's TransparencyKey or Opacity property. This can contrast badly with the image in your picture box, that effect is perceived by the user as flicker.
One immediate cure is to not use controls so you don't pay for their window. A Label is very convenient but it is a massive waste of system resources to burn up a window just to display a string. You can simply implement the picture box' Paint event and draw the strings with TextRenderer.DrawText(). PictureBox has double-buffering turned on by default so the image as well as the text is drawn completely smoothly, no more flicker. The obvious disadvantage is that you lose the convenience of point-and-click, you have to write code.
There are other fixes possible. One of them is to prevent the picture box from leaving holes for the child windows. It will draw the entire image, the labels pop on top of them. That's still flicker but not nearly as noticeable. Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
internal class MyPictureBox : PictureBox {
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
}
Compile and drop the new picture box control from the top of the toolbox onto your form.
Yet another possible workaround is to make the form and all of its children double-buffered. This doesn't speed up the painting at all but all of the windows get rendered into a memory buffer, the result is blitted to the screen. You'll notice a delay but the window suddenly pops on the screen. This is called compositing. Winforms doesn't support this directly since it can have side-effects but it is easy to enable. Paste this code into your form class:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
Supported by XP and later. Watch out for painting artifacts.
or you can ditch the labels and draw the text yourself:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
TextRenderer.DrawText(e.Graphics, "Label1", SystemFonts.DefaultFont,
new Point(10, 10), Color.Black, Color.Empty);
}
The label does not support transparency, you must create your own unique custom control, you can see these code examples.
http://www.codeproject.com/KB/dotnet/transparent_controls_net.aspx http://www.codeproject.com/KB/vb/uLabelX.aspx
Bye

Form height problem when FormBorderStyle is NONE

I have a borderless form (FormBorderStyle = None) with the height of 23 pixels (set in the designer)
When .NET draws my form at runtime - it draws it 38 pixels high (it adds the height of a title-bar for some reason).
MessageBox.Show(this.Height.ToString()); //this shows 38!! why?
To work it around I have to set "Height = 23;" in the Form_Load event.
private void MyForm_Load(object sender, EventArgs e)
{
this.Height = 23; //workaround. wtf??
}
You can try this yourself in Visual Studio 2010 (Winforms App, target Framework - 2.0).
Wtf?
Yeah, it is a bug, of sorts. Note how in the designer you set the size of the form with the Width and Height properties. Those properties include the size of the borders and the title bar. That's a problem however, your form may run on a machine where the user has increased, say, the title bar font size. That then would reduce the size of the window's client area. Or in other words, the form's ClientSize property would change on that machine. Leaving less room for the controls and messing up the design of your form pretty badly.
There's code inside the Form class that runs after the Handle is created, right before the Load event runs. It recalculates the Size of the form, using the same ClientSize you had on your machine. Now everything is good, the Height of the form won't match the one you set in the designer but the form otherwise looks the same and the layout of the controls is identical.
That same code also ensures that the window doesn't get too small. And that's where it falls over, it doesn't pay enough attention to the FormBorderStyle property. Clipping the height to the title bar size plus the client area height, as you found out. It also prevents the form getting too narrow, trying to make sure that the icon and min/max/close buttons are always visible. Even if you don't have any.
The workaround is to change the ClientSize after this code runs, the OnLoad override or Load event handler is the right place for that. Beware that if you hard-code the form size like this then you should also set the AutoScaleMode property to None. Make sure that this doesn't cause trouble on a machine that has a different DPI setting.

Resources