I'm handling WndProc in my WPF application in order to respond to the event where some other application enters fullscreen. At this time, the main window of my application needs to hide. Here is the code I have written:
Hooking:
HwndSource source = HwndSource.FromHwnd(MyMainWindowHandle);
source.AddHook(this.WndProc);
WndProc:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Debug.Print("In wndproc");
QUERY_USER_NOTIFICATION_STATE state;
SHQueryUserNotificationState(out state);
if (MyMainWindow.Visibility == Visibility.Visible && state == QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY)
{
Debug.Print("hiding");
MyMainWindow.Hide();
}
else if (MyMainWindow.Visibility == Visibility.Hidden && state != QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY)
{
Debug.Print("showing");
MyMainWindow.Show();
}
return IntPtr.Zero;
}
This works as expected with applications like PowerPoint or Skype. However, when a web browser (Chrome) enters full screen while playing a video, the WndProc is not called when the user exits full screen. It is called and behaves as expected after the user does something else, like click the Windows task bar, etc. Does anyone know why / a workaround to this issue?
Related
I am trying to get the HWND of the On-Screen/Touch keyboard - the one that appears on the bottom of the screen. For that I first look for the Process ID according to the name, and then do EnumWindows to look through all windows and find one whose Process ID corresponds to the one I know:
doTheThing(int pid)
{
EnumWindows(new EnumWindowsProc(Report), (IntPtr)pid);
}
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
protected static bool Report(IntPtr hwnd, IntPtr lParam)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
return false;
}
return true;
}
Now Report is called several times, but the thing never actually matches. If I take another process, it works, but it seems like the OnScreenkeyboard "System.Diagnostics.Process (WindowsInternal.ComposableShell.Experiences.TextInput.InputApp)" does not show up with EnumWindows.
Using FindWindow does not work as the window does not have a Title and its class is the generic ApplicationFrameHost.
In AHK I was able to get the HWND by hovering over the keyboard with MouseGetPos,,, WinUMID, so a HWND definitely exists.
Is there a possibility that some windows are ignored by the EnumWindows? If so, how can I prevent that? What other solutions are there?
As a sidenote, it also does not show up in the UI Automation verify tool.
Note: Windows now has two keyboards apparently. I mean the one you can open via the taskbar.
To clarify why I need this:
I have the following AHK-Script that can make the window I point my mouse over window semi-transparent:
MouseGetPos,,, WinUMID
WinSet, Transparent, 100, ahk_id %WinUMID%
I noticed that when I point it over the Touch-Keyboard, it will make it transparent and that effect stays even if the keyboard disappears, until I restart my system. If I save the WinUMID variable I can even change the transparency while the keyboard is not used.
Now I want to make the Keyboard semi-transparent in C#, and that works as long as I provide the HWND. But I can't figure out a way to get the HWND inside of C#, without the help of AHK.
process.MainWindowHandle returns 0.
I have also tried
ProcessThreadCollection threads = process.Threads;
foreach (ProcessThread thread in threads)
{
EnumThreadWindows((uint)thread.Id, new EnumThreadDelegate(EnumThreadCallback), IntPtr.Zero);
}
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
protected delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
protected static bool EnumThreadCallback(IntPtr hwnd, IntPtr lParam)
{
MessageBox.Show("Try");
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
makeTransparent(lParam);
return false;
} else
MessageBox.Show("False: " + hwnd.ToString());
return true;
}
EnumThreadCallback was never called though, even though it works for any other process.
Edit:
I found out that the Keyboard process was not actually creating the Keyboard. Instead, it seems like the keyboard-UI is created by explorer.
The following code does work:
Process[] processes = Process.GetProcesses();
IntPtr hWnd = IntPtr.Zero;
while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "ApplicationFrameWindow", null)) != IntPtr.Zero)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hWnd, out lpdwProcessId);
foreach (Process process in processes)
{
if (process.ToString() == "System.Diagnostics.Process (explorer)")
{
if (process.Id == (int)lpdwProcessId)
{
doThing(hWnd);
}
}
}
}
However, multiple hWnds are used, and only one of them belongs to the keyboard. Now I need to find out how to filter out this specific one. There do not seem to be downsides to this method right now, but I don't feel comfortable releasing this without knowing if it can have any adverse effects because of this. Victim 1: The new Paste window.
Try this alternate solution:
var process = Process.GetProcessById(pid);
IntPtr hwnd = process.MainWindowHandle;
Here's the problem:
I wrote a WPF application that uses an Hwnd to host a drawing surface. That hwnd is responsible for sending back mouse events that happen. On my computer, everything works normally and I see the 522(0x020A) mouse wheel message without issue.
I installed this same software on another computer, and the event didn't fire. I went as far as logging all event messages to a file to see if 522 fired at all, and it never showed up.
Things I've tried:
-Making sure the Hwnd has focus. Not only did I make a thread that would re-focus it every second, I made sure that on my computer (working) "IsFocused" was true for that hwnd.
-Closing down any other program running. This included things normal to the computer running in the background, in case something was taking focus off.
-Switching the mouse. I used the mouse I'm using on my computer to be sure, and it still did not work on the new computer.
Here's the base code:
public abstract class Win32HwndControl : HwndHost
{
protected IntPtr Hwnd { get; private set; }
protected bool HwndInitialized { get; private set; }
private const string WindowClass = "HwndWrapper";
protected Win32HwndControl()
{
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
var wndClass = new NativeMethods.WndClassEx();
wndClass.cbSize = (uint)Marshal.SizeOf(wndClass);
wndClass.hInstance = NativeMethods.GetModuleHandle(null);
wndClass.lpfnWndProc = NativeMethods.DefaultWindowProc;
wndClass.lpszClassName = WindowClass;
wndClass.hCursor = NativeMethods.LoadCursor(IntPtr.Zero, NativeMethods.IDC_ARROW);
NativeMethods.RegisterClassEx(ref wndClass);
Hwnd = NativeMethods.CreateWindowEx(
0, WindowClass, "", NativeMethods.WS_CHILD | NativeMethods.WS_VISIBLE,
0, 0, (int)Width, (int)Height, hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, 0);
return new HandleRef(this, Hwnd);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case NativeMethods.WM_LBUTTONDOWN:
RaiseMouseEvent(MouseButton.Left, Mouse.MouseDownEvent);
break;
case NativeMethods.WM_LBUTTONUP:
RaiseMouseEvent(MouseButton.Left, Mouse.MouseUpEvent);
break;
case NativeMethods.WM_RBUTTONDOWN:
RaiseMouseEvent(MouseButton.Right, Mouse.MouseDownEvent);
break;
case NativeMethods.WM_RBUTTONUP:
RaiseMouseEvent(MouseButton.Right, Mouse.MouseUpEvent);
break;
case NativeMethods.WM_MOUSEWHEEL:
RaiseMouseWheelEvent(wParam.ToInt32(), Mouse.MouseWheelEvent);
break;
}
return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
}
There is more code to this class, but I've confirmed it is not hitting the WndProc method.
Any tips on why this would be happening on one computer and not the other?
I have an MFC application, but in some places we use WPF. In one case I am working on, we have a WPF Window shown modally, but we are trying to display an MFC based specialized edit control inside the WPF window.
I tried to follow the cookbook and derived my class from HwndHost. I am trying to intercept the WM_NOTIFY message of the control because it sends something like a EN_CHANGED notification through WM_NOTIFY.
I have done two things to try and intercept the method.
1) Override WndProc:
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
switch (msg)
{
case WM_COMMAND:
break;
case WM_NOTIFY:
++NOtifyCount;
break;
}
return IntPtr.Zero;
}
2) Tap into the MessageHook event of HwndHost and have a hook procedure like this:
IntPtr SyntaxEditHost_MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
switch (msg)
{
case WM_COMMAND:
break;
case WM_NOTIFY:
++NOtifyCount;
break;
}
return IntPtr.Zero;
}
If I set a breakpoint in either WM_NOTIFY handler, it never gets hit. If I set a breakpoint inside the C++ code before WM_NOTIFY is sent and then set a breakpoint in the above two methods looking for ANY message, no message is received.
If I look at WM_NOTIFY messages in Spy++, I can verify that the HwndHost is receiving the messages. Everytime I press a key in the editor, the WM_NOTIFY message is received by the HwndHost. However, somehow the WM_NOTIFY messages are getting handled before I get a chance to handle them myself in my override of WndProc or my implementation of the message hook.
Anybody have any ideas? Of course I googled for it and got nothing useful at all.
Is there a way to stop system from going to sleep/hibernate mode when user presses e.g. laptop's or tablet's power button or chooses sleep/hibernate from Windows' menu? I've been trying to implement this by using WndProc and Windows' power messages but no luck. Here's the code that I've been using. I also tried to return BROADCAST_QUERY_DENY as IntPtr but no luck.
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x218) // WM_POWERBROADCAST.
{
if (wParam.ToInt32() == 0x4) // PBT_APMSUSPEND.
{
handled = true;
}
}
return IntPtr.Zero;
}
I've also tried PowerCreateRequest and PowerSetRequest but no luck there either.
I have managed to prevent Windows from shutting down by using Microsoft.Win32.SystemEvents.SessionEnding and I thought that this handles power management messages too but I was wrong. :)
I'd like to hear any options for closing Win UI application from windows service.
My service runs under System account. UI application runs for every logged-in user, so there can be many app instances. I need to close them all. I know UI process name and can bind to each process instance and kill it. BUT UI application has tray icon which stays visible (ghost icon, disappears when hovered by mouse) after the process is killed. I'd like to close the UI application correctly, via managed or unmanaged code. Any ideas would be greatly appreciated.
ADDITION 1:
The UI application has no main window, but only tray icon (NotifyIcon component).
ADDITION 2:
I can modify source code of the UI application. But it is written in a way that prevents it from recieving CUSTOM window messages, only standard ones.
ADDITION 3:
The UI application does not show up any Form, it just creates ApplicationContext and executes NotifyIcon within the context.
Program.cs
public static class Program
{
[STAThread]
private static void Main()
{
ApplicationContext context = new TrayApplicationContext();
Application.Run(context);
}
}
TIA
Ivan
Call Process.CloseMainWindow instead of Kill.
If you have access to the source code of the WinUI app then in your main form (the one you start in Application.Run(mainFormGoesHere)) subscribe to the close event and make the notifyIcon.Visible = false; just before you exit. It is a known issue with the NotifyIcon and the system tray.
If it is a 3rd party app, then hope that they too have something implemented like this to properly clean up after being asked to close through CloseMainWindow()
Another approach would be to attempt to refresh the system tray from your service as described here
public const int WM_PAINT = 0xF;
[DllImport("USER32.DLL")]
public static extern int SendMessage(IntPtr hwnd, int msg, int character, IntPtr lpsText);
//Send WM_PAINT Message to paint System Tray which will refresh it.
SendMessage(traynotifywnd,WM_PAINT,0,IntPtr.Zero);
Try sending a WM_CLOSE message to the application:
const uint WM_CLOSE = 0x10;
[DllImport("user32.dll",EntryPoint="SendMessage", SetLastError=true)]
public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int
lParam);
SendMessage(hWnd, WM_CLOSE, 0, 0);
hWnd is the window handle of the process' main window you are trying to shut down. You possibly also need to send the same message to the window handle of the notify icon
UPDATE
As you can't receive custom messages, you might try this:
enumerate all windows
iterate over each window and retrieve the processID
if the processID matches the one you want to shutdown, send WM_CLOSE to it
retrieving process ID for hWnd:
[DllImport("user32")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
Maybe silly, and I'm not sure this can work within multiple sessions.
Define a custom message and send it to broadcast using PostMessage; within your app capture incoming messages and if you receive your custom one, close gracefully.
In service:
public const int HWND_BROADCAST = 0xffff;
[DllImport("user32")]
public static extern bool PostMessage(int hwnd, int msg, int wparam, int lparam);
int WM_MYMSG = WM_USER + 1;
When you need to send message:
PostMessage(HWND_BROADCAST,WM_MYMSG,0,0);
In your app:
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}
EDITED:
If you want to override WndProc you need to have a form, but that doesn't mean you have to show a form: in your app create a form and run it, while in form code you write:
private void Form1_Shown(object sender, EventArgs e)
{
// Show here tray icon
....
....
// Hide form
this.Hide();
}
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}