Show a taskbar item with a NativeWindow - c#

My application is intended to work almost entirely through a Windows 7 taskbar item with the use of thumbnails and jump lists. I know I can easily create a Form and simply hide it, but this seems like overkill. Plus, I'd like to toy around with NativeWindow as much as possible, because I've never used it before.
Essentially, I have a class called RootWindow that derives from NativeWindow that will handle hotkeys and hopefully everything else. I don't need a visible window at all, but simply something to process window messages and provide a taskbar item that I can attach thumbnails and jump lists to.
Is there some kind of special CreateParams option I need to pass to CreateHandle? Or am I out of luck?
EDIT: Well, it was easier than I thought it would be, although it's not exactly what I want. Once I passed the NativeWindow's handle to the ShowWindow API, the taskbar item appeared. However, it also shows a window in the upper left corner of the screen. Is there some way to get rid of that window while still showing the taskbar item?
public class RootWindow : NativeWindow {
public const int SW_SHOWNOACTIVATE = 4;
[DllImport("User32.dll")]
private static extern int ShowWindow(IntPtr hWnd, short cmdShow);
public RootWindow() {
CreateHandle(new CreateParams());
ShowWindow(this.Handle, SW_SHOWNOACTIVATE);
}
}

The trick was to set the window's style to WS_POPUP.
const int WS_POPUP = unchecked((int)0x80000000);
const int SW_SHOWNOACTIVATE = 4;
CreateHandle(new CreateParams() {
Style = WS_POPUP
});
ShowWindow(Handle, SW_SHOWNOACTIVATE);
I also disabled Aero Peek for the window, since it's just for background work.
const int DWMNCRP_ENABLED = 2;
const int DWMWA_DISALLOW_PEEK = 11;
int policy = DWMNCRP_ENABLED;
DwmSetWindowAttribute(Handle, DWMWA_DISALLOW_PEEK, ref policy, sizeof(int));

Related

How to hide a taskbar when AutoHideTaskbar option selected and a script (ahk script) trying to hide taskbar too?

My application is designed to launch in full screen, at any cost taskbar should not be visible to the user. For taskbar hiding below ahk scripts will be running in the background to perform the needed operations.
Regarding AHK scripting please select the below link for its description.
http://ahkscript.org/
The script doesn`t work if "Auto Hide taskbar" functionality of windows 7 is selected.
Hence I have taken the below C# code to solve the issue from the application side.
But in certain conditions like when the application launches for the first time after windows restart, showwindow function is not working properly especially when Auto Hide taskbar option selected.
Sample Code
[DllImport("user32.dll")]
public static extern int FindWindowEx(int parentHandle, int childAfter, string className, int windowTitle);
[DllImport("user32.dll")]
private static extern int GetDesktopWindow();
[DllImport("user32.dll")]
private static extern int ShowWindow(int hwnd, int command);
protected static int Handle
{
get
{
return (int)FindWindow("Shell_TrayWnd", "");
}
}
protected static int HandleOfStartButton
{
get
{
int handleOfDesktop = GetDesktopWindow();
int handleOfStartButton = FindWindowEx(handleOfDesktop, 0, "button", 0);
return handleOfStartButton;
}
}
public static void HideTaskbar()
{
int Taskbar = ShowWindow(Handle, SW_HIDE);
int StartButton = ShowWindow(HandleOfStartButton, SW_HIDE);
}
private void button1_Click(object sender, EventArgs e)
{
HideTaskbar();
}
Script
Below script hides the taskbar and disables the execution of some keys.(right window and left window button and ctrl+esc)
WinHide,ahk_class Shell_TrayWnd
LWin::Suspend
RWin::Suspend
^Esc::Suspend
other options I have tried out
[DllImport("user32.dll")]
public static extern bool SetWindowPos(
int hWnd, // handle to window
int hWndInsertAfter, // placement-order handle
short X, // horizontal position
short Y, // vertical position
short cx, // width
short cy, // height
uint uFlags // window-positioning options
);
private void button1_Click(object sender, EventArgs e)
{
int hwnd = FindWindow("Shell_TrayWnd", "");
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_HIDEWINDOW);
}
Above code alters the functionality. Not working properly.
Please suggest any ways to handle this scenario.
Here's an AutoHotkey solution to check OS Version and change the Hide Taskbar Setting:
If (A_OSVersion = WIN_7) {
; retrieve Registry Value of AutoHide Taskbar
RegRead, OutputVar, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2, Settings
; If AutoHide Taskbar is On
If (SubStr(OutputVar, 18, 1) == 3) {
ChangeSetting := RegExReplace(OutputVar, (03), "02",, 1, 16)
;ChangeSetting := StrReplace(OutputVar, "3", "2",,1)
RegWrite, REG_BINARY, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2, Settings, % ChangeSetting
; The Code below Automatically applies Registry settings (and on Win 8.1 Explorer.exe is also restarted immediatley
; Alternatively you could use Shutdown Command to Reboot the system but this is much faster...
Process, Close, explorer.exe
}
}
Here's some code if you just want to test it out:
Gui, Add, Button, x1 y1 w110 h30 gButton1, Change AutoHide
Gui, Show,, New GUI Window
Return
Button1:
RegRead, OutputVar, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2, Settings
ChangeSetting := ((SubStr(OutputVar, 18, 1) == 3)
? RegExReplace(OutputVar, (03), "02",, 1, 16)
: RegExReplace(OutputVar, (02), "03",, 1, 16))
RegWrite, REG_BINARY, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2, Settings, % ChangeSetting
Process, Close, explorer.exe
Return
GuiClose:
ExitApp
People will still be able to access the taskbar by pressing the Windows key on the keyboard or some other shortcut. All you're doing is hacking the computer's registry keys to mess with the user's customized shortcuts.
What you really want to do is create a fullscreen maximized window (similar to what you see when you maximize a youtube video).
Set the properties of your window such that:
1. FormBorderStyle = None,
2. On form load, get screen rects and resize your window to be of that size.

Why isn't MessageBox TopMost?

I recently found out that by default MessageBoxes were not the top most form when displayed by default and I was wondering if anyone knew any circumstances when you wouldn't want the messagebox to be shown on top of other windows?
I found the issue when I started to show splash screens whilst loading an application, and it looked like my program was still running but there was a MessageBox behind the splash screen that was waiting for input.. The splash screen was shown on a different thread to the thread that called the messagebox so I imagine this is why it didn't appear above the splash; but this still doesn't explain why MessageBox doesn't have the MB_TOPMOST flag by default?
Edit
To better clarify:
in the end I had to do something similar to this in the end to make a messagebox, code isn't exactly correct as wrote from memory)
[DllImport("User32.dll")]
private int extern MessageBox(windowhandle, message, caption, flag);
public static void MessageBox(windowhandle, string message, string caption)
{
MessageBox(windowhandle, message,caption, MB_TOPMOST);
}
The proposed solutions work if you can get a handle or reference to the window the dialog is supposed to appear on top of. However, this may not always be possible or easy to achieve:
the window is a splash screen and should not tightly coupled with your business logic
the window is created by another class or library than the current one
the window is out of your control, i.e. from a third party (native) library
In such scenarios, you could use the Win232 MessageBox API from User32.dll, but a simpler, managed solution is also available:
MessageBox.Show(new Form { TopMost = true }, "Hello, I'm on top!");
The code new Form { TopMost = true } will create a hidden form with the MB_TOPMOST property, which is inherited by the messagebox dialog window. As a result, it will appear on top of all your other windows. Using new Form() inline has no side-effects, has no visual appearance and it will be destroyed normally via the garbage collector.
Note: if you are not inside a form already, don't forget the namespace, this is System.Windows.Forms.MessageBox, not System.Windows.MessageBox! (thanks, user1).
To show the MessageBox on top-most of all for the application
Code
//Should be MessageBox.Show() below
MessageBox.Show(this, "My top most message");
Reason for not being MB_TOPMOST by default
If MB_TOPMOST will be default then the MessageBox will show up in a 'system modal' mode and it will be exactly on top on that form and side effects are that the 'system modal' mode will cause the MessageBox to Block the windows until the message is dismissed normally it will be 'application modal' mode.
Reference links
MSDN forum - How to display a MessageBox as topmost window
SO - C# MessageBox To Front When App is Minimized To Tray
When showing MessageBox provide its owner as the first argument. For example when invoking from a Form instance call:
MessageBox.Show(this, "Message");
Provide a reference to the window owning it as the first argument.
Message boxes (and modal forms in general) do not appear on top of all windows of your application. They only appear on top of their owner. If you want your message-box (or other modal forms) be on top of your splash screen, set their owner to the splash form instance.
I try to paste a more complete code piece, it's definitely working
[CLSCompliant(false)]
[DllImport("user32.dll", EntryPoint = "MessageBox")]
public static extern int MessageBoxUser32(int hWnd, String text, String caption, uint type);
const uint MB_TOPMOST = 0x00040000;
const uint MB_OK = 0x00000000;
const uint MB_OKCANCEL = 0x00000001;
const uint MB_ABORTRETRYIGNORE = 0x00000002;
const uint MB_YESNOCANCEL = 0x00000003;
const uint MB_YESNO = 0x00000004;
const uint MB_RETRYCANCEL = 0x00000005;
public static void ShowMessageBox(string message, bool topMost = true
, string title = null, MessageBoxButtons buttons = MessageBoxButtons.OK)
{
if(topMost)
{
uint mbv = MB_TOPMOST;
if (buttons == MessageBoxButtons.OK)
mbv |= MB_OK;
if (buttons == MessageBoxButtons.OKCancel)
mbv |= MB_OKCANCEL;
if (buttons == MessageBoxButtons.AbortRetryIgnore)
mbv |= MB_ABORTRETRYIGNORE;
if (buttons == MessageBoxButtons.YesNoCancel)
mbv |= MB_YESNOCANCEL;
if (buttons == MessageBoxButtons.YesNo)
mbv |= MB_YESNO;
if (buttons == MessageBoxButtons.RetryCancel)
mbv |= MB_RETRYCANCEL;
MessageBoxUser32(0, message, title == null ? string.Empty : title, MB_TOPMOST);
}
else
{
MessageBox.Show(message, title == null ? string.Empty : title, buttons);
}
}
The answer given above is obviously correct minus the fact that it needs to call System.iDisposable.Dispose on the object new Form.
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
MessageBoxIcon icon = MessageBoxIcon.Error;
string message = Resources.ResourceManager.GetString("MESSAGE");
string caption = Resources.ResourceManager.GetString("TITLE");
DialogResult result;
Form form;
using (form = new Form())
{
form.TopMost = true;
result = MessageBox.Show(form, caption, message, buttons, icon);
}
if (result == DialogResult.Yes)
{
// do something with the result
}
For more:
Top-Most-MessageBox Examples

Hiding an application from the taskbar

I have been struggling to hide another application from the taskbar from my application.
I have been using the SetWindowLong function in order to set/remove WS_EX_APPWINDOW on the extended style.
I have tried both setting and removing the property individually as well as taking the current WindowLong, and removing/adding it to that one, like so:
SetWindowLong(pMainWindow, GWL_EXSTYLE, GetWindowLong(pMainWindow) & WS_EX_APPWINDOW);
And tried removing it like so:
SetWindowLong(pMainWindow, GWL_EXSTYLE, GetWindowLong(pMainWindow) & ~WS_EX_APPWINDOW);
Also tried both those methods without first getting the window long. Here is my entire code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
[DllImport("User32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("User32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_HIDE = 0x00;
private const int SW_SHOW = 0x05;
private const int WS_EX_APPWINDOW = 0x40000;
private const int GWL_EXSTYLE = -0x14;
private void HideWindowFromTaskbar(IntPtr pMainWindow)
{
SetWindowLong(pMainWindow, GWL_EXSTYLE, ~WS_EX_APPWINDOW);
ShowWindow(pMainWindow, SW_HIDE);
ShowWindow(pMainWindow, SW_SHOW);
}
private void ButtonHide_Click(object sender, RoutedEventArgs e)
{
HideWindowFromTaskbar(Process.GetProcessesByName("notepad")[0].MainWindowHandle);
}
}
I have noticed changes in Spy++ looking at the properties. I had a bunch of different results, like WS_EX_APPWINDOW being added, but also randomly have other attributes disappearing, etc.
When looking at the messages, I also saw that it DID get messages like STYLE_CHANGED.
The rules for determining which windows have buttons on the taskbar are documented on MSDN. Raymond Chen gives the following summary of these rules:
There are some basic rules on which windows go into the taskbar. In
short:
If the WS_EX_APPWINDOW extended style is set, then it will show (when visible).
If the window is a top-level unowned window, then it will show (when visible).
Otherwise it doesn't show.
The fact that you are trying to modify a window in another app severely hampers you. You are removing the WS_EX_APPWINDOW extended style. This is not enough because the window in question will be a top-level unowned window (see bullet point 2). You cannot change the owner of a window once it has been created and since the window is controlled by another process you are pretty much stuck.
The only option remaining is to remove the WS_EX_APPWINDOW extended style and replace it with WS_EX_TOOLWINDOW. This will indeed get the window off the taskbar but it will change the appearance of the window:
The window is intended to be used as a floating toolbar. A tool window
has a title bar that is shorter than a normal title bar, and the
window title is drawn using a smaller font. A tool window does not
appear in the taskbar or in the dialog that appears when the user
presses ALT+TAB. If a tool window has a system menu, its icon is not
displayed on the title bar. However, you can display the system menu
by right-clicking or by typing ALT+SPACE.
Here is how I do this.
1. Create a window (hwndOwner) with WS_POPUP style and WS_EX_TOOLWINDOW
2. Call SetWindowLong(hwnd, GWL_HWNDPARENT, hwndOwner)
3. Call SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | ~WS_EX_APPWINDOW)
This will remove the target window from the taskbar and alt tab. This will work on every OS since WS2000 at least.
Now, David Heffernan pointed to the MSDN documentation which says you can't do this. Well, I don't know why it says that but its inaccurate. You can do this and INFACT the .NET Framework does this, just use .NET Reflector to examine the code for System.Windows.Forms.Form.Owner property - it uses SetWindowLong to transfer ownership, as often as you would like!
And for more evidence of incorrect MSDN documentation, look no further than the docs for the Owner property, it says "A top-level window cannot have an owner." it should be the exact opposite of this, ONLY top-level windows can have an owner!
private const int SW_HIDE = 0x00;
private const int SW_SHOW = 0x05;
private const int WS_EX_APPWINDOW = 0x40000;
private const int GWL_EXSTYLE = -0x14;
private const int WS_EX_TOOLWINDOW = 0x0080;
private static void HideAppinTaskBar()
{
var Handle = FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad");
ShowWindow(Handle, SW_HIDE);
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_TOOLWINDOW);
ShowWindow(Handle, SW_SHOW);
}
This works for me - tested with notepad. WinXp 32 Bit machine.
This works in WinUI3 with PInvoke.User32 nuget package on .NET 6.0:
Adapted from Alex's answer.
private void HideAppFromTaskBar(MainWindow window)
{
//Get the Window's HWND
var windowNative = window.As<IWindowNative>();
var windowHandle = windowNative.WindowHandle;
User32.ShowWindow(windowHandle, User32.WindowShowStyle.SW_HIDE);
var flags = (User32.SetWindowLongFlags)User32.GetWindowLong(windowHandle, User32.WindowLongIndexFlags.GWL_EXSTYLE);
User32.SetWindowLong(windowHandle, User32.WindowLongIndexFlags.GWL_EXSTYLE, flags | User32.SetWindowLongFlags.WS_EX_TOOLWINDOW);
User32.ShowWindow(windowHandle, User32.WindowShowStyle.SW_SHOW);
}

How to make a window Draggable(C# Winforms)?

I have form. I have enabled the transparency on the form and I have removed it's Title Bar and Border. Inside that i have created a Custom UI, Which have the same features like a window. Basically, my idea is to create custom window.
Everything is working as expected but only the windows dragging is not working. I am not sure how to enable it. I googled for this. But i didn't find any useful info for me.
Please help me to implement this window dragging.
I've implemented this behavior by capturing mousedown (uncapture on mouseup), and then mousemove.
Just move the form co-ordinates (left, top), equivalent amounts to the mouse movement (those events have the amount the mouse moved).
This worked fine for me.
class YourForm : Form
{
private const int WM_NCHITTEST = 0x84;
private const int HTCLIENT = 0x1;
private const int HTCAPTION = 0x2;
///
/// Handling the window messages
///
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if (message.Msg == WM_NCHITTEST && (int)message.Result == HTCLIENT)
message.Result = (IntPtr)HTCAPTION;
}
}
The easiest way is to process WM_NCHITTEST message and return HTCAPTION for the portions of your custom window which work like the title bar does in a normal window. Windows will do the rest.

C#: Getting label injected into native app's statusbar pane to show up

I am writing a .Net COM DLL that runs inside a native Windows application.
I am attempting to inject an additional pane into this app's statusbar, and it does not have any specific implementation to do so, so I am trying to subclass the app's statusbar myself.
I am using the Win32 API SetParent() to switch the parent of a label control from a .Net form to the msctls_statusbar32 instance. I used a label because it is the closest implementation to a native "static" class control that I could find without writing my own control.
Somehow I've even managed to get NativeWindow to successfully hook in to both the statusbar and my label's messages (though at the moment it just passes them all to the next WndProc), and I've assigned matching styles and styleExs to my label's window, and I can see my label as a child with the msctls_statusbar32 as its parent. Everything looks like it should be working correctly, but it's not. My control does not show up in the parent app's statusbar.
What I don't understand is why it is not showing up. Nearly everything I can think of matches correctly -- granted, the class for my label is "WindowsForms10.STATIC.app.0.378734a" and not "static", but other than that it is on the correct process and thread, has matching window styles (at least the hex value... Spy++ seems to enumerate them differently), and for all purposes pretty much blends in with the rest of the controls. Would anybody know what else needs to be done to get it to be visible?
(I had originally gone the route of CreateWindowEx and setting WNDPROC callbacks but I could not get the app to work... it would freeze for a minute or so and then unfreeze, and I would notice my window disappeared from the window tree)
Thank you!
you can try working with existing status bar control; what you can do is to reset text to an existing section of it or add a new one; also you would probably need to set up new widths to existing sections of the statusbar. You can find details on how to work with statusbar control here:msdn Status Bars
Please find an example of how you could do it below. I actually tried it with c# com object used by win32 application and it seem to work fine for me.
[ComVisible(true)]
[Guid("CC5B405F-F3CD-417E-AA00-4638A12A2E94"),
ClassInterface(ClassInterfaceType.None)]
public class TestInterface : ITestInterface // see declaration of the interface below
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);
public const int SB_SETTEXT = 1035;
public const int SB_SETPARTS = 1028;
public const int SB_GETPARTS = 1030;
public unsafe void Test()
{
IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
// find status bar control on the main window of the application
IntPtr statusBarHandle = FindWindowEx(mainWindowHandle, IntPtr.Zero, "msctls_statusbar32", IntPtr.Zero);
if (statusBarHandle != IntPtr.Zero)
{
// set text for the existing part with index 0
IntPtr text = Marshal.StringToHGlobalAuto("test text 0");
SendMessage(statusBarHandle, SB_SETTEXT, 0, text);
Marshal.FreeHGlobal(text);
// create new parts width array
int nParts = SendMessage(statusBarHandle, SB_GETPARTS, 0, IntPtr.Zero).ToInt32();
nParts++;
IntPtr memPtr = Marshal.AllocHGlobal(sizeof(int) * nParts);
int partWidth = 100; // set parts width according to the form size
for (int i = 0; i < nParts; i++)
{
Marshal.WriteInt32(memPtr, i*sizeof(int), partWidth);
partWidth += partWidth;
}
SendMessage(statusBarHandle, SB_SETPARTS, nParts, memPtr);
Marshal.FreeHGlobal(memPtr);
// set text for the new part
IntPtr text0 = Marshal.StringToHGlobalAuto("new section text 1");
SendMessage(statusBarHandle, SB_SETTEXT, nParts-1, text0);
Marshal.FreeHGlobal(text0);
}
}
}
[ComVisible(true)]
[Guid("694C1820-04B6-4988-928F-FD858B95C880")]
public interface ITestInterface
{
[DispId(1)]
void Test();
}
hope this helps, regards
Many possible reasons:
The .Net Label control blows up when it finds it does not have a WinForms parent.
The native Status bar is drawing over the label control becuase of incorrect Z-Order.
The Label control is not visible.
As it turns out the answer was dumbfoundingly simple... the label's X and Y coords were out of the display area of the statusbar parent. Moving them to (0, 0) and it shows up right there! Of course, now the problems have moved on to: C# WinForms control in .Net COM Server won't redraw

Categories

Resources