SetForegroundWindow not working, how to check Class/Caption names - c#

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.

Related

How can I determine when my application's console window gets or loses focus?

Is there a simple way to do this?
Or at the very least check if the console is currently in focus?
Imagine something like a game (thats not the case here but the analogy holds) - it would be useful if it could pause automatically. I need something similar.
If the window you were interested in were not a console window, this would have been very simple to do by just tapping into the appropriate focus event. But console windows don't have focus events, so the easy way out is not available here.
What you can do is set up an event handler to receive WinEvents generated by the UI Automation services. An event is generated whenever the window focus changes; you can get the HWND of the newly focused window and compare it to that of your console window. If they match, you just got focus; if they don't, you don't have focus (either just lost it or never had it to begin with).
The most convenient way to tap into UI Automation is through the System.Windows.Automation namespace. You can set up the event handler with AddAutomationFocusChangedEventHandler, which will give you an instance of AutomationFocusChangedEventArgs from which you can determine which window has received focus.
Here's some sample code:
AutomationFocusChangedEventHandler focusHandler = OnFocusChange;
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
MessageBox.Show("Listening to focus changes");
Automation.RemoveAutomationFocusChangedEventHandler(focusHandler);
where OnFocusChange is:
void OnFocusChange(object source, AutomationFocusChangedEventArgs e)
{
var focusedHandle = new IntPtr(AutomationElement.FocusedElement.Current.NativeWindowHandle);
var myConsoleHandle = Process.GetCurrentProcess().MainWindowHandle;
if (focusedHandle == myConsoleHandle)
{
// ...
}
}
Note that I am assuming the console is your process's main window for simplicity; if that's not the case, you need to get a HWND to the console window some other way.
Also note that in order to receive automation events, your process must be running a message loop (in this case also known as a "dispatcher loop"), which in turn requires a thread being dedicated to running it. In the example above this happens automatically when MessageBox.Show is called, but in the general case you will have to take proper care of it.
I can't add a comment so I'm just going to have to post an answer. You can test the theory posted by DJ KRAZE like this:
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
{
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero) {
return false; // No window is currently activated
}else{
Console.WriteLine("Application is focused!");
}
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
So if it doesn't return false, then it will print "Application is focused!" in the application. You can always add something where "return false" is to do something when it's not focused as well.

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

Make a form not focusable in C#

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

c# pin a form to the deskop

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.

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