I am trying to pin a form to the desktop, like the gadget sidebar.
It should not be affected by Win+D and Show desktop buttons.
Which is the best methode for this?
I can find the Progman and set the parent of my form to this, but I read it's not the best way.
Disclaimer : just because you can do something a certain way in WinForms without API calls or "hooking," doesn't mean that you should use that technique. The API calls for making a Form/Window immovable, topmost, etc. are really very simple. Note that the usual API "simple technique" will have the same problem as the technique demonstrated below of appearing over file and folder icons on the desktop.
Note that by "interfering" with the designed behaviors of the Windows-D key combination and the "Show Desktop" icon-hickey in the TaskBar, you are engaging in non-standard configuration of the user's machine : common "wisdom" indicates it's not a good idea to do this !
A "pseudo-sidebar" can be emulated in Vista in C# WinForms ... impervious to being hidden by Windows-D or use of the "Show Desktop" icon-hickey in the System Tray ... without API calls or "hooking" : but there's a catch : the Form/Window is going to appear on top of items on the Desktop (folders, files), and, by virtue of its being immovable, you won't be able to access the items "under it." If you're willing to "put up" with that limitation : read on :) Another "smaller weirdness" noted at the end of this.
Example :
create a new Form solution in Visual Studio :
a. set the Form's size, background color, etc. to taste
b. add your controls, or user controls.
c. set the Form's 'TopMost property to 'true if you want to always appear in front of other application's windows, 'false if you want other application's windows to be able to appear in front of it.
set the 'ControlBox, 'MinimizeBox, and 'MaximizeBox properties of the Form to 'false.
set the 'FormBorderStyle to one of the 'fixed styles shown in the Property Explorer drop-down. as you may know using FormBorderStyle = FixedToolWindow with the Text property of the Form an "empty string" : you'll have a window with no TitleBar and Caption. Discussion : yes, you could allow it to be resized if you wish : just write some code in the SizeChanged handler, and leave the Form unlocked.
these event handlers should give you an idea of what's required to make the Form unclosable, unmovable, un-minimizable, etc.
......... rough sketch code p.o.c. follows : obviously you can refactor, improve ...
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
// set your Form's 'Top here ...
private void Form1_LocationChanged(object sender, EventArgs e)
{
this.Top = 100;
this.Left = Screen.PrimaryScreen.Bounds.Width - this.Width;
}
private void Form1_VisibleChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
}
private void Form1_Deactivate(object sender, EventArgs e)
{
this.Activate();
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("button1 is alive");
}
What's that 'Click event handler for 'button1 doing in there ? Just to verify in my test that the Form was usable.
It's the re-activation of the Form in the 'Deactivate Event handler above that makes the Form immune to Windows-D and "Show Desktop" : but that also has a strange side-effect in Vista : when you switch focus away from the Form's window (by, for example, clicking on a folder on the Desktop) : the TaskBar will bounce-up, stay up, (if it's hidden) and blink on and off a while : I guess to show you it's yearning to be active.
Putting up with a stuck-open TaskBar may be yet another price of using this "ersatz sidebar," and not using API's.
Note : don't forget : you want 'Opacity : you got 'Opacity :)
[1] Note : You do want to think about giving the user a way to hide the Form or close it ? [/1]
It sounds like what you're after is an Application Desktop Toolbar.
I could be wrong but, may be Win+D probably sends a global minimize message to all open windows
So Try making your form immune to window minimize message.
using hooks
Heres a related article.
Hooking
Thanks all for the answers,
I had this methode at first:
[DllImport("User32.dll")]
static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
IntPtr pWnd = FindWindow("Progman", null);
pWnd = FindWindowEx(pWnd, IntPtr.Zero, "SHELLDLL_DefVIew", null);
pWnd = FindWindowEx(pWnd, IntPtr.Zero, "SysListView32", null);
IntPtr tWnd = this.Handle;
SetParent(tWnd, pWnd);
But the marjor issue is that child windows are also parented to the deskop, and you cannot use any form of opacity or transparency.
use the following code and pass the window handle to the function while form load hope fully this resolves your problem
public void SetFormOnDesktop(IntPtr hwnd) {
IntPtr hwndf = hwnd;
IntPtr hwndParent = FindWindow("ProgMan", null);
SetParent(hwndf, hwndParent);
}
A simple workaround could be to just use Win+M and don't listen to the Deactivation Event. This will remove the flickering from activating/deactivating.
Related
I have a program which runs in the background, and when something happens a message box appears, I want to have it so clicking yes will switch to a specified program.
I just can't find what ClassName and CaptionName work with it, I need it to work with the game World of Warcraft.
The Window Title is "Wold of Warcraft" on Task manager its called "World of Warcraft Retail", when I inspect its' properties it says "Wow-64" Properties, on properties it says product name is "World of Warcraft" so I've tried every combination of these and nothing works. The code works if I put:
BringToFront("Notepad", "Untitled - Notepad");
So it works, I just don't know what I need for it to apply to WoW.
My code is:
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
private void BringToFront(string className, string CaptionName)
{
SetForegroundWindow(FindWindow(className, CaptionName));
}
private void Alert()
{
string title = "WoW Queue Alert: Message";
string message = "The Queue is ready to accept!";
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
DialogResult result;
result = MessageBox.Show(new Form() { TopMost = true }, message, title, buttons, MessageBoxIcon.Information);
if (result == System.Windows.Forms.DialogResult.Yes)
{
BringToFront("World of Warcraft Retail", "World of Warcraft");
}
}
I really don't see anything special about WoW, and going by how the notepad example works the correct code should be:
BringToFront("World of Warcraft Retail", "World of Warcraft");
Being a fullscreen program should affect it, and I can't see there being anyway Blizzard has implemented something to stop this function.
Edit: I just set the ClassName to null and works, as caption name is just the window title. No idea what the ClassName is I tried everything I could find.
You should use some window inspection tool to inspect the window of World of Warcraft to get its Class Name and Window Name. A suggestion would be Spy++ if you installed Visual Studio (it comes along with it). It can help you detect the window's Class Name and Window Name, and Windows Messages sent to that window easily.
For example, the picture below shows all windows in current environment. Highlighted entry is a PowerPoint instance, which has Caption = "Microsoft PowerPoint" and ClassName = "PPTFrameClass".
Another approach will be writing a "always-inactivate" winform and call GetForegroundWindow from that form to get the window handle of the window you want.
To create an "always-inactivate" winform, simply override its CreateParams property:
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= 0x8000000; // WS_EX_NOACTIVATE - requires Win 2000 or higher
return p;
}
}
By overriding this, the form won't get focus even on a mouse click. Therefore, you could create a button to trigger GetForegroundWindow(). Signature of the function in C# looks like below:
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
which returns the window handle of current fore ground window.
I have two forms in my application MainForm and HexCompare. If I click off of my app to another window then I click back on one of the two forms only one of them comes to the front. How can I make it so if I click either one of the two forms it will bring both to the top of all open forms in the application? Right now I need to select each form individually to get them to the top of my stack of windows (and this can be very annoying due to the fact that HexCompare has ShowInTaskbar set to false
A good example of this working the way I want to is how most Find dialogs work. If the find dialog is clicked it brings the main form to the front if it is hidden by another application, and if the main form is clicked the find dialog will come to the front if it is hidden by another application.
How MainForm is invoked.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
How HexCompare is invoked
private void lstCaputres_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedItem = (Tuple<DateTime, byte[]>)lstCaputres.SelectedItem;
if (hexCompare == null || hexCompare.IsDisposed)
{
hexCompare = new HexCompare(selectedItem.Item2);
hexCompare.Show();
}
else
hexCompare.ChangeValue(selectedItem.Item2);
}
EDIT:
It seems that HexCompare's value of Parent is Null. If I could somehow set it to MainForm would that solve my issue, and if so how to I set it?
EDIT2:
I have semi-solved it using Tigran's solution but it causes flickering as each form is brought to the front, if there is a better solution I am still interested.
//In MainForm.cs
private void MainForm_Activated(object sender, EventArgs e)
{
hexCompare.BringToFront();
this.BringToFront();
}
//in HexCompare.cs
private void HexCompare_Activated(object sender, EventArgs e)
{
parent.BringToFront();
this.BringToFront();
}
You can use the following API wrapper to bring a form to the front of the z-order without having it steal focus. This function can be called in the Activated event of your main form, just pass it your HexCompare form as the parameter. This is not much different from the other answer but I've never witnessed any flicker as you mention in the comments.
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = 0;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // window handle
int hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags); // window positioning flags
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public void ShowInactiveTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
To me seems that should be enough to set TopMost=true and call BringToFront() on both forms.
hexCompare = new HexCompare(selectedItem.Item2);
hexCompare.TopMost = true;
hexCompare.Show();
hexCompare.BringToFront();
Something like this.
My application needs to display a popup near the cursor position of any active application when certain conditions are met (known by my app).
So I would like to display the form (without stealing the focus from the active application). I tried using ShowWindow with the SW_SHOWNOACTIVATE param but, this way, the my form is displayed under the currently active form.
How can I force my form to be displayed on top of any form active on the screen without stealing the input focus?
Thanks.
What you want is to use the TopMost property of the form you want to stay on top.
You need to add a bit of plumbing to the pop-up form so you can override the WM_ACTIVATE message:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_ACTIVATE)
{
if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
{
if (m.LParam != IntPtr.Zero)
{
SetActiveWindow(m.LParam);
}
else
{
// Could not find sender, just in-activate it.
SetActiveWindow(IntPtr.Zero);
}
}
}
base.WndProc(ref m);
}
Make sure you add the following to your pop-up form as well:
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
You can use the pop-up form as you would any other, by calling Show() on it. You can make it the top-most window through the TopMost property, as with other forms.
I'm wanting to write a virtual keyboard, like windows onscreen keyboard for touchscreen pcs.
But I'm having problem with my virtual keyboard stealing the focus from the application being used. The windows onscreen keyboard mantains the focus on the current application even when the user clicks on it. Is there a way to do the same with windows forms in C#?
The only thing I can do for now is to send a keyboard event to an especific application, like notepad in the following code. If I could make the form not focusable, I could get the current focused window with GetForegroundWindow.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private void button1_Click(object sender, EventArgs e)
{
IntPtr calculatorHandle = FindWindow("notepad", null);
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("111");
}
Is there a way this can be done? Any suggestions of a better way to have the form sending keyboard events to the application being used?
Thanks!!
Its solved!
I've tried the solution from gehho, but I also needed to override the CreateParams method:
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
var createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_NOACTIVATE;
return createParams;
}
}
Instead of trying to reset the active window after your one has been clicked, I would rather try to prevent your window from receiving focus/being activated.
Have a look at this article. At the end, the author briefly explains how this can be done:
How can I prevent my window from getting activation and focus when shown?
In Windows Forms 2.0 there is a new property called
ShowWithoutActivation – which you would need to override on the Form.
In native applications you can use SetWindowPos with the
SWP_NOACTIVATE flag or the ShowWindow with the SW_SHOWNA flag.
Furthermore, in this article he provides a code example for Windows Forms:
If you want a full-on form, you can now override a property called
ShowWithoutActivation:
public class NoActivateForm : Form
{
protected override bool ShowWithoutActivation => true;
}
Keep in mind this does not “prevent” activation all the time – you can
still activate by calling the Activate(), Focus()… etc methods. If
you want to prevent clicks on the client area from activating the
window, you can handle the WM_MOUSEACTIVATE message.
private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATE;
return;
}
base.WndProc(ref m);
}
I 'answered' this in a related question - but it is more of an additional question that I having trouble with and I need more recent answers...
Basically I have an application that stays open on the screen and the user can press a button on my app once they have made an entry into one of three 3rd party applications.
When they click the button on my app, I need to determine which of the three applications they last used in order to know which database to talk to. I have gone down the route of looking at GetForeGroundWindow and GetWindow however the Window handle I get from GetWindow always refers to a window with title M. I have used the Winternal Explorer tool from the Managed Windows API tools and I can locate the M handle being returned and it is a 'child' of the process that I am after - but from this handle I cant get the process name.
I have done up a small example app using simple windows forms - and I lauch it and then make Notepad the focus and then click on my button and I get the handle - but when looking at the MainWindowHandle of all the processes, it is not listed, but using Winternal Explorer I can see that is a sub process of the notepad process.
Any suggestions on why I am only getting this subprocess handle returned instead of the actual process handle??
Sample code is below - being run on a Windows XP sp 3 machine
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestWindowsAPI
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
IntPtr thisWindow = GetForegroundWindow();
IntPtr lastWindow = GetWindow(thisWindow, 2);
tbThisWindow.Text = thisWindow.ToString();
tbLastWindow.Text = lastWindow.ToString();
}
}
}
You can use GetWindowThreadProcessId to get the process id from the (sub)window handle:
uint lastProcess;
GetWindowThreadProcessId(lastWindow, out lastProcess);
Pent Ploompuu - that was spot on - excellent work! Cheers
For anyone else - this is what my test function ended up looking like:
private void button1_Click(object sender, EventArgs e)
{
IntPtr thisWindow = GetForegroundWindow();
IntPtr lastWindow = GetWindow(thisWindow, 2);
uint processID = 0;
var parentWindow = GetWindowThreadProcessId(lastWindow, out processID);
tbThisWindow.Text = thisWindow.ToString();
tbLastWindow.Text = lastWindow.ToString();
tbParentWindow.Text = parentWindow.ToString();
tbLastProcess.Text = processID.ToString();
var processName = from cp in Process.GetProcesses() where cp.Id == processID select cp.ProcessName;
tbParentName.Text = processName.FirstOrDefault();
}
Try overriding the WndProc (or adding an IMessageFilter) for each of the programs and returning an "app ID" when a particular message is sent. Then just use SendMessage on the window handle to get the app ID.