SetClassLong(hWnd, GCL_HICON, hIcon) cannot replace WinForms Form.Icon - winforms

I'd like to use a specific ICO file as my icon with a WinForms application. Since I want to be able to specify a small icon (16x16) for the title bar and a normal icon (32x32) when Alt-Tabbing, I cannot use the Form.Icon property which accepts a single System.Drawing.Icon object, which forces me to use either the low res icon, or the normal icon.
I posted a related question which came up with a very simple solution which works fine for native Win32 applications:
SetClassLong(hWnd, GCL_HICON, hIcon32x32);
SetClassLong(hWnd, GCL_HICONSM, hIcon16x16);
Trying to apply the same trick on a Form does not work. I have following P/Invoke definitions:
[DllImport ("User32.dll")]
extern static int SetClassLong(System.IntPtr hWnd, int index, int value);
const int GCL_HICON = -14;
const int GCL_HICONSM = -34;
and I then simply call:
System.IntPtr hIcon32x32 = ...;
System.IntPtr hIcon16x16 = ...;
SetClassLong(this.Handle, GCL_HICON, hIcon32x32.ToInt32());
SetClassLong(this.Handle, GCL_HICONSM, hIcon16x16.ToInt32());
and never call Form.Icon. This does not work, however:
The icon in the form is still the default WinForms provided icon.
When pressing Alt-Tab, I still see the WinForms default icon.
...but, what's interesting, is that when I press Alt-Tab, I see for a very very short moment the icon I defined using GCL_HICON (or GCL_HICONSM if I do not use GCL_HICON). Something seems to be happening behind the scenes, which forces Windows to paint the icon using the WinForms default icon.
I can't figure out what I've done wrong and what is going on behind the scenes.
edited: I really want to be able to provide two different icons created on the fly, not bind the Form.Icon to an icon on disk. That's why I am trying to use the P/Invoke code to specify the icons in memory.

I haven't actually verified this by testing it or looking at the disassembled WinForms code, so I'm not sure if this answer will satisfy the bounty condition of "credible and/or official sources". But I think I'm pretty [in]credible, so I'll give it a shot anyway!
You're setting the icon associated with the window class. You're doing it with the SetClassLong[Ptr] function and GCL_HICON/GCL_HICONSM indices, but it has the same effect as setting it in the WNDCLASSEX structure at the time that the class is registered. This sets the default icon for windows of that class.
However, individual windows can set their own icons, overriding the default icon provided by their class. You do this by sending the WM_SETICON message, passing either ICON_BIG or ICON_SMALL as the wParam and a handle to the icon as the lParam. Presumably, this is what WinForms is doing. That's why the "default" WinForms icon is appearing instead of the default window class icon you're assigning, because WinForms is setting its default icon using WM_SETICON, not via the window class. The only thing "default" about the WinForms icon is that it's assigned automatically by the framework if you don't assign a different custom icon. It doesn't fit any other definition of "default"—certainly not one that might be used from a Win32 perspective.
The Form.Icon property definitely uses WM_SETICON to modify the icon, that's why it is working as expected. Now, you say you don't want to set the Icon property because
I really want to be able to provide two different icons created on the fly, not bind the Form.Icon to an icon on disk. That's why I am trying to use the P/Invoke code to specify the icons in memory.
But that doesn't mean you can't set the Icon property. You can specify a handle to an icon (HICON) here, just as well as you can if you use P/Invoke. All you need is the static Icon.FromHandle method, which creates a new Icon object from the specified HICON. You then assign this Icon object to the form's Icon property.
You don't have to, though. You can use P/Invoke if you want:
const int WM_SETICON = 0x80;
enum IconType
{
ICON_BIG = 1;
ICON_SMALL = 0;
}
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
int message,
IntPtr wParam,
IntPtr lParam);
Then, call it similar to what you have:
IntPtr hIcon32x32 = ...;
IntPtr hIcon16x16 = ...;
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_BIG, hIcon32x32);
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_SMALL, hIcon16x16);
Only one thing you're doing wrong: assuming that the "big" icon will always be 32x32 pixels and that the "small" icon will always be 16x16 pixels. At least, I'm assuming that you're doing this from the names of the variables. If so, that's an invalid assumption. Those are only the most common sizes. They are not guaranteed to be the same in all environments. This is why it's important to provide larger icons in your .ico file; for example, a 48x48 icon. Since you're setting the icons dynamically, Windows won't have access to a larger icon to downsample and you might end up with something really blurry and ugly when your 32x32 icon is scaled up.
To retrieve the actual sizes, call the GetSystemMetrics function. The SM_CXICON and SM_CYICON flags will tell you the X and Y dimensions, respectively, of the "big" icon. The SM_CXSMICON and SM_CYSMICON flags will tell you the X and Y dimensions, respectively, of the "small" icon.
const int SM_CXICON = 11;
const int SM_CYICON = 12;
const int SM_CXSMICON = 49;
const int SM_CYSMICON = 50;
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);
static Size GetBigIconSize()
{
int x = GetSystemMetrics(SM_CXICON);
int y = GetSystemMetrics(SM_CYICON);
return Size(x, y);
}
static Size GetSmallIconSize()
{
int x = GetSystemMetrics(SM_CXSMICON);
int y = GetSystemMetrics(SM_CYSMICON);
return Size(x, y);
}

You can use Form.Icon. You just need a single icon file that contains 16x16 and 32x32 pixel versions of your icon.
I just tried it, using a single icon file that contains a 32x32 pixel red circle and a 16x16 blue rectangle. The small windows icon shows the blue rectangle, the alt-tab icon shows a red circle.
No need for P/Invoke at all.

Related

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

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);
}

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.

What is the Aero function for previewing the screen state behind a window?

I have looked everywhere to see how to use Aero in my program. I fail to find any C function that previews the windows behind your own, like File Explorer or any mainstream browser does on their title bars.
Some programs fake it by just adding an image that looks like the Windows 7 title bar -- without Aero -- but I consider it kind of cheating. I found this code on the link below:
[DllImport ("dwmapi.dll" Entry Point = "# 113", SetLastError = true)]
internal static external DwmpActivateLivePreview uint (uint a, IntPtr b, uint c, uint d);
[DllImport ("dwmapi.dll" Entry Point = "# 105", SetLastError = true)]
internal static bool external DwmpStartOrStopFlip3D ();
// Activate Aero peek into the desired Handle
DwmpActivateLivePreview (1, Handle, 0, 1);
// Disable Aero peek
DwmpActivateLivePreview (0, Handle, 0, 1);
// start or stop the Aero Flip 3D
DwmpStartOrStopFlip3D ();
But have no idea what it means. Is the implementation of Aero Peek, automatically function with the PreviewWindows(or whatever) function?
I'm lost.
This link is in Dutch, just run it through Google Translate
I am not trying to toggle whether or not Aero Peek and/or Flip is activated, or change the icon for my application when the mouse hovers on its taskbar icon. I am instead looking for a function that takes the current screen state of applications behind my own and returns it as an image for display in my application. As a bonus, does the (presumably) returned image come blurred, or is that effect that is separately applied? I think the name for it is Aero Glass.
As I understand you want to get the state (in terms of the display) of the windows behind your application. You can achieve it by doing the following,
HWND hwnd_behind = GetNextWindow(your_window_handle, GW_HWNDNEXT);
HDC hdc = GetWindowDC(hwnd_behind);
RECT rect;
GetWindowRect(hwnd_behind,rect);
HDC bitmap = MakeABitMapDC();
StretchBlt(bitmap,0,0,dW,dH,hdc,0,0,rect.width,rect.height,SRCCOPY);
You can plug this code into handlers which returns the bitmap when windows asks applications for a preview bitmap.
Left the details like "MakeABitMapDC" for the sake of brevity.
I am lost is kinda a big area to see what you don't understand but I will try anyway.
If mean that you don't know what the parameters do in the:
[DllImport ("dwmapi.dll" Entry Point = "# 113", SetLastError = true)]
internal static external DwmpActivateLivePreview uint (uint a, IntPtr b, uint c, uint d);
It is basically like this:
a: Is the parameter for activating Aero Peek.
b: Is the handle the AeroPeek focusses too.
c: Is a tricky one. If you pass a handle of a window to that parameter and the TopMost property is set it will appear on top of the AeroPeek.
d: I have no idea what d does but the times i have used DwmpActivateLivePreview I always put it on 1.
So the signature is like this:
internal static extern uint DwmpActivateLivePreview(uint active, IntPtr handle, IntPtr onTopHandle, uint d);
The DwmpStartOrStopFlip3D method activates the "Windows + Tab" effect.
[DllImport("dwmapi.dll", EntryPoint = "#105", SetLastError = true)]
internal static extern bool DwmpStartOrStopFlip3D();
Remember though that there is a reason that they are undocumented since they were not meant for us to use them.
If by chance you want to have the Aero Peek effect inside your application you can look at the DwmSetIconicLivePreviewBitmap function located in DWM.
More information here: MSDN: DwmSetIconicLivePreviewBitmap function

c# MouseWheel Scroll and RTB VScroll Event

I am a bit confused:
We have a chat application that has a requirement to NOT scroll chat, if the user is scrolled up; and we have a scrollInfo class that provides us with the ability to check if the scroll bar thumb is at the bottom of the scrollbar, so that we can identify if we should scroll or not.
Unfortunately, there are many different ways to scroll chat. One could use the scrollbars up/down boxes, or the scrollbar thumb, or just scroll using the mouse wheel.
However, if the up/down arrows are used, we have no issue.
And, if the scrollbar thumb is used, the VScroll event does fire, but we have no way of identifying which DIRECTION the thumb is moved, or how to check if it's being held down, without using a timer to constantly query the left mouse button status.
And, lastly, if mousewheel is scrolled, while we can tell which direction it's going by looking at the Delta property, the VScroll event still fires after the Mouse Wheel event is handled. So, we essentially have a duplicate scroll happen.
I need some help with this. Our chat application is as follows:
.NET 4.0 Windows Forms C# Application
Built in Visual Studio 2010
*Chat Output: * RichTextBox that auto formats text entering it.
Here's an example of our logic, that just isn't working:
User Scrolls Up using Mouse wheel: When new messages come in, they should NOT scroll the chat output box back down. Instead, when a scroll event happens, it should be able to detect where the scroll thumb is, and make that determination.
User Scrolls up using the ScrollBar thumb: VScroll event is fired, which checks if the scrollbar is at the 'bottom' of the scroll box. And, if it is, it does the full scroll event, to ensure that the caret is always placed at the end of the scroll box, and is hidden (so that the READ ONLY chat ouput, has no actual blinking I beam).
User scrolls using the scrollbar up/down arrows: works just fine. (no need to explain this one, as it's already working).
I need some clarification here, how can i properly check these events?
-------- EDIT FOR CLARIFICATION ---------
Contents of scrollInfo class:
internal class Scrollinfo
{
internal const uint ObjidVscroll = 0xFFFFFFFB;
[DllImport("user32.dll", SetLastError = true, EntryPoint = "GetScrollBarInfo")]
private static extern int GetScrollBarInfo(IntPtr hWnd,
uint idObject,
ref Scrollbarinfo psbi);
internal static bool CheckBottom(Control rtb, int postion)
{
var info = new Scrollbarinfo();
info.CbSize = Marshal.SizeOf(info);
var chk = GetScrollBarInfo(rtb.Handle,
ObjidVscroll,
ref info);
if (chk == 0)
GetScrollBarInfo(rtb.Handle,
ObjidVscroll,
ref info);
bool isbottom = info.XyThumbBottom
>= (info.RcScrollBar.Bottom - info.RcScrollBar.Top - (info.DxyLineButton + 1));
if (info.DxyLineButton <= 0) isbottom = true;
if (info.XyThumbBottom <= 0) isbottom = true;
return isbottom;
}
}
internal struct Scrollbarinfo
{
internal int CbSize;
internal Rect RcScrollBar;
internal int DxyLineButton;
internal int XyThumbTop;
internal int XyThumbBottom;
internal int Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
internal int[] Rgstate;
}
internal struct Rect
{
internal int Left;
internal int Top;
internal int Right;
internal int Bottom;
}}
You could try something like this:
Update the box with the new text to be displayed and then find the last visible line using the following code:
rtb.GetLineFromCharIndex(rtb.GetCharIndexFromPosition(new Point(rtb.Width, rtb.Height)));
Now next time that you need to put text into the RichTextBox, just check for the last visible line using the same code as above.
If the new last visible line is less than the older one, it means the user has scrolled upwards (or the RichTextBox has been resized). In either case, you'll just have to move the caret to the end of the text and then call the RichTextBox.ScrollToCaret() method to restore the position.
I also recommend you take a look at the following on the MSDN site:
RichTextBox Methods
RichTextBox.GetCharIndexFromPosition Method
RichTextBox.GetLineFromCharIndex Method
RichTextBox.Select Method¹
RichTextBox.ScrollToCaret Method
¹ This can be used to set the caret position.

Win32 CreateWindowEx() window icon not showing properly

I create my window using CreateWindowEx, but I only get an icon in the task bar; both the window's title bar and the icon that shows when I Alt+Tab show this type of dummy icon: . I set both the hIcon and the hIconSm fields in the WNDCLASSEX to the value returned from ::LoadIcon(hInstance, IDI_APPLICATION) (not NULL).
The .ico file itself was made out of a .png which I genereated through http://converticon.com to 16x16, 32x32 and 96x96 (what are the recommended sizes btw?). Could it be that I'm using the wrong sizes or color depth? If so, why does it work in the task bar (different size)? Thanks!
The icon must be defined as a resource somewhere. In your resource header there should be a line like this:
#define IDI_MYICON 1000
Then in your call to LoadIcon() it should be something like:
wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_MYICON));
If you're using Visual Studio it's pretty easy to add resources. Just right click Resources in the solution explorer.

Resources