This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I bring my application window to the front?
I am having an issue with SwitchToThisWindow
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace BringToFront
{
public partial class Form1 : Form
{
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String className, String windowName);
[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool turnOn);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
IntPtr activeWindowHandle = GetForegroundWindow();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
if (!checkBox1.Checked)
bringToFront(comboBox1.SelectedItem.ToString());
else
timer1.Enabled = true;
}
catch
{
MessageBox.Show("Please choose a Process Name");
}
}
public static void bringToFront(string title)
{
IntPtr handle = FindWindow(null, title);
if (handle == IntPtr.Zero)
{
return;
}
SwitchToThisWindow(handle, true);
}
private void comboBox1_Click(object sender, EventArgs e)
{
comboBox1.Items.Clear();
Process[] process = Process.GetProcesses();
foreach (Process processes in process)
{
if (!String.IsNullOrEmpty(processes.MainWindowTitle))
comboBox1.Items.Add(processes.MainWindowTitle.ToString());
}
}
private void timer1_Tick(object sender, EventArgs e)
{
Process process = Process.GetCurrentProcess();
string title = process.ProcessName.ToString();
IntPtr handle = FindWindow(null, title);
if (activeWindowHandle != handle)
bringToFront(comboBox1.SelectedItem.ToString());
if (!checkBox1.Checked)
timer1.Enabled = false;
}
}
}
As you can see I'm trying to bring the process that is selected to the front and keep it in the front by doing a timer every 5 seconds and rebringing it to the front. It works perfectly when running the application through Microsoft Visual Studios, but when I run the program as a standalone, it works how every other function like this does and only makes it flash in taskbar instead of bringing it to the front.
Why are the permissions different and is there anyway to fix this?
Via the solution by #ReedCopsy here, I suggest to make the selected handle TopMost after you've switched to that window. Using this solution, no new app can become top over the selected window.
Add the following to your code:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_SHOWWINDOW = 0x0040;
and change your bringToFront by adding a call to SetWindowPos:
public static void bringToFront(string title)
{
IntPtr handle = FindWindow(null, title);
if (handle == IntPtr.Zero)
{
return;
}
SwitchToThisWindow(handle, true);
// Call this way:
SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
}
Related
I have a WPF window that is supposed to be a "desktop gadget".
My users are asking for a way to prevent it from disappearing when they hit "Show Desktop".
Making the window always topmost works but some of my users do not want the window always on top.
Short of running a timer every x seconds to activate the window, is there a proper way to achieve this?
I ended up developing my own solution.
I scoured the internet for weeks trying to find an answer so I'm kind of proud of this one.
So what we do is use pinvoke to create a hook for the EVENT_SYSTEM_FOREGROUND window event.
This event triggers whenever the foreground window is changed.
Now what I noticed is when the "Show Desktop" command is issued, the WorkerW window class becomes foreground.
Note this WorkerW window is not the desktop and I confirmed the hwnd of this WorkerW window is not the Desktop hwnd.
So what we do is whenever the WorkerW window becomes the foreground, we set our "WPF Gadget Window" to be topmost!
Whenever a window other the WorkerW window becomes the foreground, we remove topmost from our "WPF Gadget Window".
If you want to take it a step further, you can uncomment out the part where I check if the new foreground window is also "PROGMAN", which is the Desktop window.
However, this will lead to your window becoming topmost if the user clicks their desktop on a different monitor. In my case, I did not want this behavior, but I figured some of you might.
Confirmed to work in Windows 10. Should work in older versions of Windows.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace YourNamespace
{
internal static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, ShowDesktop.WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
internal static extern int GetClassName(IntPtr hwnd, StringBuilder name, int count);
}
public static class ShowDesktop
{
private const uint WINEVENT_OUTOFCONTEXT = 0u;
private const uint EVENT_SYSTEM_FOREGROUND = 3u;
private const string WORKERW = "WorkerW";
private const string PROGMAN = "Progman";
public static void AddHook(Window window)
{
if (IsHooked)
{
return;
}
IsHooked = true;
_delegate = new WinEventDelegate(WinEventHook);
_hookIntPtr = NativeMethods.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _delegate, 0, 0, WINEVENT_OUTOFCONTEXT);
_window = window;
}
public static void RemoveHook()
{
if (!IsHooked)
{
return;
}
IsHooked = false;
NativeMethods.UnhookWinEvent(_hookIntPtr.Value);
_delegate = null;
_hookIntPtr = null;
_window = null;
}
private static string GetWindowClass(IntPtr hwnd)
{
StringBuilder _sb = new StringBuilder(32);
NativeMethods.GetClassName(hwnd, _sb, _sb.Capacity);
return _sb.ToString();
}
internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private static void WinEventHook(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
string _class = GetWindowClass(hwnd);
if (string.Equals(_class, WORKERW, StringComparison.Ordinal) /*|| string.Equals(_class, PROGMAN, StringComparison.Ordinal)*/ )
{
_window.Topmost = true;
}
else
{
_window.Topmost = false;
}
}
}
public static bool IsHooked { get; private set; } = false;
private static IntPtr? _hookIntPtr { get; set; }
private static WinEventDelegate _delegate { get; set; }
private static Window _window { get; set; }
}
}
You can use "StateChanged" event of the window. It fires when "WindowState" property changes. You can use this event and maximize the window when the state changed to minimized.
UPDATE
Try this code:
private async void Window_StateChanged_1(object sender, EventArgs e)
{
await MaximizeWindow(this);
}
public Task MaximizeWindow(Window window)
{
return Task.Factory.StartNew(() =>
{
this.Dispatcher.Invoke((Action)(() =>
{
Thread.Sleep(100);
window.WindowState = System.Windows.WindowState.Maximized;
}));
});
}
I'm trying to stick my form to a window of another application (let's say Microsoft Outlook). When I move the Outlook window, my form should still stick at the right-hand side of it.
At the moment, I'm monitoring Outlook's position in a while(true) loop (with a sleep()) and adjusting my form's position to it.
Here are two problems:
If the sleep() duration is too short, it takes much performance to check Outlook's position and to adjust my form that often.
If the sleep() duration is too long, my form is too slow in adjusting to Outlook (it lags).
Isn't there a native solution for this?
you have to get a hook on the process and listen to event
this should give you a good starting point
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B;
private const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A;
private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Width = 100;
this.Height = 100;
this.TopMost = true;
int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;
//this will also be triggered by mouse moving over the process windows
//NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
}
private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Rect move = new Rect();
if (eventType == EVENT_SYSTEM_MOVESIZESTART)
{
NativeMethods.GetWindowRect(hwnd, ref move);
Debug.WriteLine("EVENT_SYSTEM_MOVESIZESTART");
}
else if (eventType == EVENT_SYSTEM_MOVESIZEEND)
{
NativeMethods.GetWindowRect(hwnd, ref move);
Debug.WriteLine("EVENT_SYSTEM_MOVESIZEEND");
}
this.Left = move.Left;
this.Top = move.Top;
}
}
public struct Rect
{
public int Left { get; set; }
public int Top { get; set; }
public int Right { get; set; }
public int Bottom { get; set; }
}
static class NativeMethods
{
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);
}
}
You could use any of the windows hook functions WH_GETMESSAGE, WH_GETMESSAGERET, WH_SYSMSGFILTER or WH_MSGFILTER.
In this case you would be interested in WM_MOVE and WM_MOVING, where WM_MOVE is sent after the window is moved (aka, done moving), and WM_MOVING is sent while the window is moved (and so you will get alot of those).
Start here with reading:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644959(v=vs.85).aspx
The answer from Fredou was allmost the answer.
I used it in my application, but got an exception "callbackoncollecteddelegate".
To make it work its neccessary to attach the WinEventProc on a property.
...
private NativeMethods.WinEventDelegate winEventProc;
private void Form1_Load(object sender, EventArgs e)
{
this.Width = 100;
this.Height = 100;
this.TopMost = true;
int processId = Process.GetProcessesByName("OUTLOOK")[0].Id;
//this will also be triggered by mouse moving over the process windows
//NativeMethods.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
this.winEventProc = new NativeMethods.WinEventDelegate(WinEventProc);
NativeMethods.SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, this.winEventProc, (uint)processId, (uint)0, WINEVENT_OUTOFCONTEXT);
}
...
See CallbackOnCollectedDelegate at Application.Run(new Form1())
I have sent my user out to the browser with Application.OpenURL. And now I want to programatically bring unity back to the foreground.
Is there any way to do it without a plugin?
Thanks.
Use GetActiveWindow to get the window's handle before you send the user away, then use SetForegroundWindow using that handle. Before you use SetForegroundWindow, you can try simulating an Alt keypress to bring up a menu to abide by certain limitations of SetForegroundWindow:
private IntPtr unityWindow;
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
const int ALT = 0xA4;
const int EXTENDEDKEY = 0x1;
const int KEYUP = 0x2;
private void SendUser()
{
unityWindow = GetActiveWindow();
Application.OpenURL("http://example.com");
StartCoroutine(RefocusWindow(30f));
}
private IEnumerator RefocusWindow(float waitSeconds) {
// wait for new window to appear
yield return new WaitWhile(() => unityWindow == GetActiveWindow());
yield return new WaitForSeconds(waitSeconds);
// Simulate alt press
keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
// Simulate alt release
keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);
SetForegroundWindow(unityWindow);
}
if you are using Unity3D in Windows, try below code after calling Application.OpenURL(...) :
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
var prc = Process.GetProcessesByName("..."); //Get Unity's process, or
var prc = Process.GetCurrentProcess();
if (prc.Length > 0)
SetForegroundWindow(prc[0].MainWindowHandle);
This worked for me on Unity 5.0.1 / Windows 8.1:
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;
public class ForeGrounder : MonoBehaviour {
private const uint LOCK = 1;
private const uint UNLOCK = 2;
private IntPtr window;
void Start() {
LockSetForegroundWindow(LOCK);
window = GetActiveWindow();
StartCoroutine(Checker());
}
IEnumerator Checker() {
while (true) {
yield return new WaitForSeconds(1);
IntPtr newWindow = GetActiveWindow();
if (window != newWindow) {
Debug.Log("Set to foreground");
SwitchToThisWindow(window, true);
}
}
}
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
static extern bool LockSetForegroundWindow(uint uLockCode);
[DllImport("user32.dll")]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
}
For Mac Standalone we can try
public void OpenURLInDefaultBrowser(string URL)
{
#if UNITY_STANDALONE_OSX
Screen.fullScreenMode = FullScreenMode.Windowed;
#endif
Application.OpenURL(URL);
StartCoroutine(ReFocusUnity());
}
private IEnumerator ReFocusUnity()
{
//You can wait for some seconds or wait for any callback you are expecting
yield return new WaitForSeconds(5f);
#if UNITY_STANDALONE_OSX
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
#endif
}
How can I prevent WPF window from minimizing when users clicks on show desktop button?
This link will help you : Get the minimize box click of a WPF window
you need to catch the event and handle it yourself.
Edit : This method will alert you once the state is changed, so it might not be the "best" solution but it could work.
Windows are not minimized when "Show Desktop" is issued. Instead the "WorkerW" and "Desktop" windows are brought to the foreground.
I ended up developing my own solution.
I scoured the internet for weeks trying to find an answer so I'm kind of proud of this one.
So what we do is use pinvoke to create a hook for the EVENT_SYSTEM_FOREGROUND window event.
This event triggers whenever the foreground window is changed.
Now what I noticed is when the "Show Desktop" command is issued, the WorkerW window class becomes foreground.
Note this WorkerW window is not the desktop and I confirmed the hwnd of this WorkerW window is not the Desktop hwnd.
So what we do is whenever the WorkerW window becomes the foreground, we set our "WPF Gadget Window" to be topmost!
Whenever a window other the WorkerW window becomes the foreground, we remove topmost from our "WPF Gadget Window".
If you want to take it a step further, you can uncomment out the part where I check if the new foreground window is also "PROGMAN", which is the Desktop window.
However, this will lead to your window becoming topmost if the user clicks their desktop on a different monitor. In my case, I did not want this behavior, but I figured some of you might.
Confirmed to work in Windows 10. Should work in older versions of Windows.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace YourNamespace
{
internal static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, ShowDesktop.WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
internal static extern int GetClassName(IntPtr hwnd, StringBuilder name, int count);
}
public static class ShowDesktop
{
private const uint WINEVENT_OUTOFCONTEXT = 0u;
private const uint EVENT_SYSTEM_FOREGROUND = 3u;
private const string WORKERW = "WorkerW";
private const string PROGMAN = "Progman";
public static void AddHook(Window window)
{
if (IsHooked)
{
return;
}
IsHooked = true;
_delegate = new WinEventDelegate(WinEventHook);
_hookIntPtr = NativeMethods.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _delegate, 0, 0, WINEVENT_OUTOFCONTEXT);
_window = window;
}
public static void RemoveHook()
{
if (!IsHooked)
{
return;
}
IsHooked = false;
NativeMethods.UnhookWinEvent(_hookIntPtr.Value);
_delegate = null;
_hookIntPtr = null;
_window = null;
}
private static string GetWindowClass(IntPtr hwnd)
{
StringBuilder _sb = new StringBuilder(32);
NativeMethods.GetClassName(hwnd, _sb, _sb.Capacity);
return _sb.ToString();
}
internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private static void WinEventHook(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
string _class = GetWindowClass(hwnd);
if (string.Equals(_class, WORKERW, StringComparison.Ordinal) /*|| string.Equals(_class, PROGMAN, StringComparison.Ordinal)*/ )
{
_window.Topmost = true;
}
else
{
_window.Topmost = false;
}
}
}
public static bool IsHooked { get; private set; } = false;
private static IntPtr? _hookIntPtr { get; set; }
private static WinEventDelegate _delegate { get; set; }
private static Window _window { get; set; }
}
}
You can change your window's parent to not be affected by Show Desktop. (as stated here: Window "on desktop")
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindowLoaded;
}
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
var ProgmanHwnd = FindWindowEx(FindWindowEx(FindWindow("Progman", "Program Manager"), IntPtr.Zero, "SHELLDLL_DefView",""), IntPtr.Zero,"SysListView32", "FolderView");
SetParent(hwnd, ProgmanHwnd);
}
}
I'm trying to create a message-only window to receive window messages from an MFC library class, within a winforms application.
I've tried subclassing NativeWindow, and in the constructor requesting a window handle like this:
CreateParams cp = new CreateParams();
cp.Parent = (IntPtr)HWND_MESSAGE;
this.CreateHandle(cp);
but I get a Win32Exception thrown with the message "Error creating window handle". How do I create a message-only window from windows forms? Is using NativeWindow the right approach?
Try that :
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
static IntPtr HWND_MESSAGE = new IntPtr(-3);
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
SetParent(this.Handle, HWND_MESSAGE);
}
I know this is 7.5 years old, but just in case anyone finds this, I thought I would respond. I used Microsoft's TimerNativeWindow code and removed the timer functionality. I ended up using this approach:
public class MyNativeWindow : NativeWindow
{
private readonly string _caption;
private const int WmClose = 0x0010;
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
private static readonly HandleRef HwndMessage = new HandleRef(null, new IntPtr(-3));
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
private static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.Process)]
private static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);
[DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.Process)]
private static extern int GetCurrentThreadId();
public MyNativeWindow(string caption)
{
_caption = caption;
}
public bool CreateWindow()
{
if (Handle == IntPtr.Zero)
{
CreateHandle(new CreateParams
{
Style = 0,
ExStyle = 0,
ClassStyle = 0,
Caption = _caption,
Parent = (IntPtr)HwndMessage
});
}
return Handle != IntPtr.Zero;
}
public void DestroyWindow()
{
DestroyWindow(true, IntPtr.Zero);
}
private bool GetInvokeRequired(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero) return false;
int pid;
var hwndThread = GetWindowThreadProcessId(new HandleRef(this, hWnd), out pid);
var currentThread = GetCurrentThreadId();
return (hwndThread != currentThread);
}
private void DestroyWindow(bool destroyHwnd, IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
hWnd = Handle;
}
if (GetInvokeRequired(hWnd))
{
PostMessage(new HandleRef(this, hWnd), WmClose, 0, 0);
return;
}
lock (this)
{
if (destroyHwnd)
{
base.DestroyHandle();
}
}
}
public override void DestroyHandle()
{
DestroyWindow(false, IntPtr.Zero);
base.DestroyHandle();
}
}
I believe that you'll need to also specify a window class.
I fear that you must derive from a Form, and force the window invisible.
Another approach (in the case the class library is modifiable) is to run a message pump without a window (see Application.Run and Application.AddMessageFilter, or if you prefer pinvokes using PeekMessage & Co).
In this case you can send messages using PostThreadMessage by having the thread id which as run Application.Run, but actually you cannot synch with the application message pump thread because it doesn't wait message acknowledge.