I have this Windows Forms application where it sits in the notification area. Clicking on the icon brings it up front, clicking it again (or clicking on the app X icon) sends it back. This is the type of app that having the window always on top is important when it's displayed by clicking the icon (it's optional though).
Right-clicking the icon brings up a context menu where one can select to enable the "always on top" option or not. When the application first starts up, the app settings are read from an XML file and I'm 99% that this is working as it should, the TopMost property is properly read (and written).
After some time (minutes, hours, days, whatever; I normally hibernate and rarely shutdown) the TopMost stops working. I don't change the option, I don't think anything is changing the option value but I click the notification area icon and app is not brought up front. It shows up but it's on the background (it displays on Alt+Tab), it's not "always on top" as it should. I bring up the context menu, disable the option (cause it's enabled) and enable it back and it starts to work after that. The app is now "always on top". However, it can lose this ability anytime after a while.
I can't understand why this happens and how this happens. Does anyone have any idea why? If not, any idea how could I try to debug such behavior?
EDIT:
I added a piece of code to show a MessageBox when the TopMost property was changed to see if I could notice any strange behavior but it was no good. It didn't help because the form was with TopMost = true but it still was in the background...
There is more than just one "Topmost" window. Topmost just says "Before all non-topmost windows".
I am pretty sure a reinitialization of the desktop (such as when hibernating) requires another SetWindowPos(hwnd, HWND_TOPMOST, ...) (which is the underlying Win32 API call).
As a workaround, you could reset and set the property again when showing the window.
Another possibility is that hiding the window also changes the Z order - either implicitely how Win32 implements that, or explicitely in the way WinForms call the hide/show window.
Like peterchen i also don't have a clue how to get the root cause. But why not make it a little bit simpler?
When you click on your Icon you'll show up your window and rely that TopMost is still active. Why not call SetWindowPos() with the current setting right before you show the window. This shouldn't make any performance problems (only happens if the user clicks the icon) nor any other side effect.
I know, it would be great to find out the root cause, but maybe it's not worth if you can solve it with such a little workaround.
Related
I'm working on an open-source .NET clone (GitHub) of DeskPins by Elias Fotinis (direct download off Google Drive). Its main function is to make other windows always-on-top. When they are on top, a pin icon is added to the title bar, which looks like this:
The pin icon moves with the window during drag and drop, and really looks like a part of it. It even responds to click event, which cancels always on top status and removes the icon from display.
Question. Is it possible to implement something like this in C# (and p/invoke, I'd assume)?
Research
I tried to run this project.
Add Your Control On Top Another Application # CodeProject
It's a WPF demo, which is supposed to add a custom control to the title bar. Does not seem to work on Windows 7 x64. Not sure if it's the OS or otherwise. Problem - z-order is not consistent, title bar appears on top of other windows as well, and it does not move with the window, it tries, but lots of visual artifacts and flicker.
Tried to apply this solution to #1:
Attach form window to another window in C#
Basically replacing relevant call to SetWindowLong with this pattern:
SetWindowLong(guestHandle, GWL_STYLE, GetWindowLong(guestHandle, GWL_STYLE) | WS_CHILD);
SetParent(guestHandle, hostHandle);
This change broke everything, so nothing was added to the title bar. Could be that it's not meant for title bars, only for the user area of the form.
In any case, if there is a simple solution, please share your wisdom. If not, I would appreciate any hints and/or links for me to investigate the topic further.
Here's how I've done this in DeskPins.
Each pin is a popup window with a custom HRGN defining its shape. I did experiment with injecting a DLL into processes using hooks and drawing on the caption, but that was too messy for me back then.
DeskPins creates the pin using WS_POPUP, WS_EX_TOPMOST and WS_EX_TOOLWINDOW, with 0 as the parent window. Then it immediately sends it a message (WM_PIN_ASSIGNWND), passing the target window to be pinned and the polling rate in msec. After that, the pin runs independently from DeskPins and they only send information messages to each other.
The pin handles WM_PIN_ASSIGNWND by setting the target window as its parent, making it top-most and starting a polling timer. To set the parent it uses:
SetWindowLong(hPin, GWL_HWNDPARENT, (LONG)hPinned);
Note that Microsoft warns against setting the parent like this and instead suggests using SetParent(). However, SetParent() does some internal processing which prevents it from working across processes. Using SetWindowLong(GWL_HWNDPARENT) essentially tricks the window manager to accept this. It's a bit of a hack, but, hey... it works.
The polling timer runs continuously and tests whether the target window is destroyed, hidden, moved or had its WS_EX_TOPMOST flag changed and responds appropriately.
Occasionally, seemingly randomly, when I close a dialog form my main form seems to move back in the window order, disappearing behind the next application back (usually Visual Studio). It retains focus, so clicking it in the taskbar minimises it, requiring another click. Whenever this happens, the control colours seem to change a little as well, but revert if I maximise and restore the window.
Any idea why this happens?
Edit: This happens when I'm debugging; it might happen at runtime too, but I usually don't have anything behind the application then, so I haven't noticed. Nobody has mentioned it.
This will happen when the dialog closes and Windows cannot find any window in your app that isn't disabled. Forced to move the focus somewhere, it will pick a window of another app to give the focus to. Your form will disappear behind it.
Exactly why your main form is disabled when this happens isn't clear. The color change certainly suggests you are changing the Enabled property of the form. Everything turns battle-ship gray when you do that. Setting Enabled back to true after the dialog closes doesn't work, it's too late. Just don't tinker with Enabled, the ShowDialog() method already disables other windows.
I want to create an alarm app for myself. On certain conditions (i need to poll websites) i want my app to inform me and make it HARD TO MISS. So i can take appropriate action or ignore it if i need to do something else.
I wrote a test app and using a BalloonTip (ShowBalloonTip with notifyIcon) isnt great. One of my previous apps brings the window in front of you and does a MessageBox however that doesnt always work or work well (if i somehow miss it or accidentally forget to click ok no futher messages will occur).
What are your suggestions?
You could also make it a system tray application and change the icon out if there is something which requires attention, a la a messenger application. That may not be "hard to miss" but I am trained (for better or worse) to look down at the icon tray when I see something blinking.
Where I work, we have a TimeTracker application (built in house) with which developers are supposed to log what we worked on and when. I am notorious for not using it. So, I wrote my own (Windows Forms) version for my own use which, every hour opens up and takes over my screen:
It is a frameless dialog which consumes the entire screen.
TopMost = True.
On resize, it sets WindowsState = Normal and resizes to fill the screen.
While it is open, it polls for taskmgr.exe and procexp.exe and kills them if found.
It disables the start menu to prevent cmd.exe commands from the menu in Windows 7.
The only way to close it is to enter a log, only then is the OK button shown!
So far, it's working out well - no one has been able to break it!
My less drastic suggestion would be to have a notification which pops up momentarily above the system tray. After a second or two, fade it out. Keep showing the notification every 30 seconds or so until it is dismissed.
Always-on-top window in the corner of the screen?
You could always set your window to be a top most window, make it full screen, and activate it. It would be very, very hard to miss...
Granted, it would also be very annoying, and not something I'd do to other users...
My "real" suggestion would be to use sound along with standard notification methods if this is going to be used by other people, as that's an easy way to grab attention without necessarily killing their workflow. A modeless window that appears in a corner of the screen, especially if combined with sound and color, can be very effective to grab attention.
The industry has been adopting these ambient orb devices and variations of it when such a hard-to-miss notification is required. It is used for tracking the stock-market and for broken-daily-builds.
http://www.ambientdevices.com/cat/orb/MAN_Ambient%20Orb_3-23-03.pdf
Regards
I have a fullscreen window, and I want to prevent pop up windows that appear at the right bottom corner of my screen. I set the Topmost property to true, but apparently it does not help. I also tried activating the form and giving it the focus once it got deactivated, but that did not help either. What is a way to ignore such windows while the user is engaged with the fullscreen app? I am .NET programming in C#.
You can't do it, this fails the "what if two programs tried to do this" test:
those popups are just normal windows like yours, they also use the same top-most style you are using.
if there was a way to always be above other topmost windows they would have used it too rendering it useless (because the authors of the other apps are just as concerned about the user missing their "super important" notifications as you are about them interfering with your full screen app).
You can try and play dirty tricks to force your window to the top of the top-most z-order, but those popups are likely to use the exact same tricks, again making this all useless (and as an extra bonus all those dirty tricks can turn your app into a compatibility nightmare).
You can disable these balloon notifications using these steps:
Click Start, Run and type regedit
Navigate to the following subkey:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
In the right pane, create a DWORD value named EnableBalloonTips
Double-click the new entry, and give it a value of 0.
Quit Registry Editor.
Log off Windows (this is not very cool...), and then log back on for the changes to take effect.
if you need help in how doing this by program, don't hesitate to ask ;)
I don't think that you can block all the popups, windows might not let you do that. But you can try with SetWindowPos function and pass it HWND_TOP parameter. It might work a little better than Topmost = true.
I used a sys tray popup control on my personal project SvnRadar written in WPF.
The control is at the http://www.hardcodet.net/projects/wpf-notifyicon written by Philipp Sumi.
Very nice.Only thing you will be need to "detach" it from the SysTray screen coordinates and
make it appear where you wish.
Hope it helps.
Good luck.
I would like to be able to hide another application's window from the taskbar, without hiding the window itself. Specifically, I want to have several different Web browsers running, visible, available in the Alt+Tab list, but not taking up space on the taskbar.
(If anyone's curious why: I've written a dashboard app that uses Vista's DwmRegisterThumbnail APIs to show live previews of several windows at once -- a sort of "picture in picture", if you will. At that point, also having taskbar buttons for those windows seems redundant.)
I am aware that changing the other window's style to include WS_EX_TOOLWINDOW will hide it from the taskbar, and I tried this first. But, as expected, it had some side effects I didn't want: the title bar got shorter (not all bad, I guess) and the minimize and maximize buttons went away (not good). I also had to hide and re-show the window to get the taskbar to recognize the change, which caused repainting artifacts when I did it to IE windows.
My next thought was that, since windows with owners are hidden from the taskbar by default, maybe I could change the other windows to be owned by mine. But MSDN is pretty clear that "[a]fter creating an owned window, an application cannot transfer ownership of the window to another window."
I found this question that's worded similarly to mine, but it's specifically about windows from your own process, where you have complete control over window ownership.
Does anyone know of any other ways to hide a taskbar button, that will work for windows from another process?
Update: Tormod put me on the right track with ITaskbarList -- it works great. The pinvoke.net page had some errors (wrong GUID, methods declared alphabetically instead of in interface order), but I edited it, made corrections, and also added an example of how to instantiate the ITaskbarList via its coclass.
Update 2: If you use DeleteTab to hide a window's taskbar button, and then make that the active window (e.g. via SetForegroundWindow or Alt+Tab), its taskbar button will reappear. To keep the taskbar button hidden, I had to add a timer and keep calling DeleteTab. As long as you don't mind the taskbar button reappearing briefly whenever the window gets focused, this works well.
From MSDN:
Version 4.71 and later of Shell32.dll adds the capability to modify the contents of the taskbar. From an application, you can now add, remove, and activate taskbar buttons. Activating the item does not activate the window; it shows the item as pressed on the taskbar.
The taskbar modification capabilities are implemented in a Component Object Model (COM) object (CLSID_TaskbarList) that exposes the ITaskbarList interface (IID_ITaskbarList). You must call the ITaskbarList::HrInit method to initialize the object. You can then use the methods of the ITaskbarList interface to modify the contents of the taskbar.
It seems like COM interop is the way to go to reliably manipulate the contents of the taskbar. In particular, you would need to call the following functions:
ITaskbarList::AddTab
ITaskbarList::DeleteTab
You can find the C# signature for the ITaskbarList interface at pinvoke.net: ITaskbarList.
Have you tried removing WS_EX_APPWINDOW?