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.
Related
I am working on a simple active desktop replacement for a school that is migrating to Windows 7. The idea is to create a window with a few large buttons with pictures on so that young children who have trouble reading can use them.
I am using SetParent() to set my window's parent to 'Progman' so that it is always at the back and is not minimised when a user chooses to 'Show Desktop'. Everything works fine on Windows XP but on Windows 7 when users click 'Show Desktop' the window remains but the contents are replaced with the users Windows background. The buttons still work (you just can't see them) and if you resize or otherwise update the window they appear again. A picture of the problem:
Any ideas why this is happening? Does anyone know a way I could force a refresh of the window when 'Show Desktop' is pressed?
I suspect in your case your app is throwing an exception but not crashing. WPF apps have a tendency to do this if the exception is thrown during the ctor of some UI element. It can disrupt the rendering stack.
I tried to reproduce the problem but was unsuccessful. From my tests I was able to get the handle to 'Progman' and set the main window as the parent when using the Windows 7 basic theme (no Arrow glass).
When I used the Arrow theme, calling SetParent would cause the window to vanish. A little research turned up a possible fix. Instead of setting the parent as the 'Progman' window, you can try using the 'SysListView32' child (the child window used to hold the desktop icons).
The problem is obtaining 'SysListView32' isn't easy. It used to be a matter of traversing through 'Progman' to 'SHELLDLL_DefView' and then 'SysListView32', however, Windows 7 has changed that. 'SHELLDLL_DefView' is now a child of WorkerW.... some of the time.
Here is the best article I could find explaining this:
http://fernandomachadopirizen.wordpress.com/2010/08/09/give-me-a-handle-and-i-will-move-the-earth/
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.
Anyone know of an efficient way of detecting movement of any windows currently open on a windows system? I need to detect a window's movement, determine if it collides with my applications Form, and bump it out from underneath if necessary.
I know I can scan through an enumerated list and check each window -- but that is way to intensive to perform constantly.
Background:
I have a taskbar-esque application that docks on the side of a user's screen. When the "Always on Top" feature is on, maximized windows will take up the remaining available space without covering the toolbar, as expected.
However, if you drag a non-maximized window over the toolbar, the application goes behind the toolbar (also expected), but you can no longer grab onto the title bar to move it back -- the window is stuck unless you disable "Always on Top" and then move it. So, I want to bump the window out from underneath.
Although not a direct answer, one possible solution to this is to create your application as an application desktop toolbar rather than a regular window. From the docs:
An application desktop toolbar(also called an appbar) is a window that is similar to the Microsoft Windows taskbar. It is anchored to an edge of the screen... The system prevents other applications from using the desktop area occupied by an appbar. (emphasis added)
This may not be a great fit for your scenario because it is oriented towards COM and unmanaged code rather than managed apps: however see this CodeProject article for info about using this feature from C#.
Failing that, you could try installing a hook (see SetWindowsHookEx) and listening for move messages but this is pretty low-level...
Try checking your PaintEventArgs ClipRectangle ..
(edit: and/or WindowFromPoint shooting match)
You can get notification of window movements using a CBT Hook: http://msdn.microsoft.com/en-us/library/ms644977(VS.85).aspx
http://www.codeproject.com/KB/dialog/FindWindow.aspx?msg=3262771
"FindWindow By Jörg Bausch"
Will get you the external (not your app's) window ID (IntPtr) the mouse went up over from within your C# application. For the desktop, and everything else on the desktop, it will return the same pointer (you can't distinguish, using this code, between as mouse-up on a folder, the desktop, the Recycle Bin).
http://www.codeproject.com/KB/cs/globalhook.aspx
"Processing Global Mouse and Keyboard Hooks in C# By George Mamaladze"
Will allow you to create GlobalHook for keyboard and mouse-events in C#. I've used it recently in VS 2010 beta 2 : it is NOT USABLE compiled against FrameWork 4.0, but does compile and work okay against FrameWork 3.5 and lower. If you download only George's demo app, be aware the download doesn't include the required dll, and will fail when you launch the .exe file (which I have brought to George's attention).
I've never worked with a "desktop application toolbar;" I hope this is relevant.
best,
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?