I'm creating a thread that looks for a window. When it finds the window, it overrides its windowproc, and handles WM_COMMAND and WM_CLOSE.
Here's the code that looks for the window and subclasses it:
public void DetectFileDialogProc()
{
Window fileDialog = null;
// try to find the dialog twice, with a delay of 500 ms each time
for (int attempts = 0; fileDialog == null && attempts < 2; attempts++)
{
// FindDialogs enumerates all windows of class #32770 via an EnumWindowProc
foreach (Window wnd in FindDialogs(500))
{
IntPtr parent = NativeMethods.User32.GetParent(wnd.Handle);
if (parent != IntPtr.Zero)
{
// we're looking for a dialog whose parent is a dialog as well
Window parentWindow = new Window(parent);
if (parentWindow.ClassName == NativeMethods.SystemWindowClasses.Dialog)
{
fileDialog = wnd;
break;
}
}
}
}
// if we found the dialog
if (fileDialog != null)
{
OldWinProc = NativeMethods.User32.GetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC);
NativeMethods.User32.SetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(new WindowProc(WndProc)).ToInt32());
}
}
And the windowproc:
public IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
lock (this)
{
if (!handled)
{
if (msg == NativeMethods.WM_COMMAND || msg == NativeMethods.WM_CLOSE)
{
// adding to a list. i never access the window via the hwnd from this list, i just treat it as a number
_addDescriptor(hWnd);
handled = true;
}
}
}
return NativeMethods.User32.CallWindowProc(OldWinProc, hWnd, msg, wParam, lParam);
}
This all works well under normal conditions. But I am seeing two instances of bad behavior in order of badness:
If I do not close the dialog within a minute or so, the app crashes. Is this because the thread is getting garbage collected? This would kind of make sense, as far as GC can tell the thread is done? If this is the case, (and I don't know that it is), how can I make the thread stay around as long as the dialog is around?
If I immediately close the dialog with the 'X' button (WM_CLOSE) the app crashes. I believe its crashing in the windowproc, but I can't get a breakpoint in there. I'm getting an AccessViolationException, The exception says "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." Its a race condition, but of what I don't know. FYI, I had been reseting the old windowproc once I processed the commands, but that was crashing even more often!
Any ideas on how I can solve these issues?
Two points of observation that I can make....
In your DetectFileDialogProc, you are comparing wnd to null, that is an IntPtr type yes? if so, that check for the comparison should be if (wnd > IntPtr.Zero){ .... }
In your WndProc, you are using the this variable for the lock which is a bad thing to do...you should do something like this private readonly object objLock = new object(); and within your WndProc use this lock (objLock){....}
and see if that resolves the issue....
Finally came up with a solution, attacking the problem from a different angle. I was able to set a system-wide hook in managed code using SetWinEventHook, and the option WINEVENT_OUTOFCONTEXT, which amazingly has the property: The callback function is not mapped into the address space of the process that generates the event.
I trap the event EVENT_SYSTEM_DIALOGSTART to receive notifications whenever a dialog is created, and EVENT_SYSTEM_DIALOGEND when its destroyed.
Related
I'm still quite new to how handles works and need some help to understand this a bit more.
I have this method in my class, "cbxWebsite" is a combobox.
public IntPtr GetCurrentHandle()
{
foreach (Process process in Process.GetProcesses())
{
if (process.ProcessName.Contains("chrome")
&& process.MainWindowTitle.Contains(cbxWebsite.SelectedValue.ToString()))
{
return process.MainWindowHandle;
}
}
return IntPtr.Zero;
}
And I'm trying to implement this: Capture screen of Window by handle
My question is about these lines:
IntPtr hwnd = GetCurrentHandle();
User32.GetWindowRect(new HandleRef(null, hwnd), ref rc);
When I call GetWindowRect, the HandleRef() complains about "Cannot convert from HandleRef to IntPtr".
MS docs says this about process.MainWindowHandle: The system-generated window handle of the main window of the associated process.
So I guess I don't understand from here. I thought I was returning the handle of the current window I want to work with from GetCurrentHandle().
But is it returning the handle of the process of that window and not the actual window-handle itself?
Can someone please clarify.
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?
I am trying to get an Outlook C# add-in to scroll the left navigation pane using SendMessage. It seems to have no effect. Does an add-in run under a lower privilege level than Outlook, therefore preventing SendMessage from working thanks to UIPI?
I iterate using EnumChildWindows (which is recursive) to find the left Navigation Pane child window's handle, as identified using Spy++, using GetWindowText to find it by class name. "workerBeeFunc" is my delegate called by EnumChildWindows. Spy++ tells me the navigation Pane window is the child of the second window having a class name "NUIDocumentWindow" (I've left all the WinAPI DLLImport statements out here):
private const int WM_VSCROLL = 277; // Vertical scroll
private const int SB_PAGEDOWN = 3; // Scrolls one page down
static StringBuilder childWindowNames = new StringBuilder();
private int NUIWindowCounter = 0;
private stopIterating as bool = false;
public delegate int EnumChildProc(IntPtr hWnd, IntPtr lParam); //Declare the delegate
public int workerBeeFunc(IntPtr hwndChild, IntPtr Param) //The function the delegate of EnumChildWindows goes to to get the work done.
{
int a;
a = WinAPIs.GetWindowText(hwndChild, childWindowNames, 100); //childWindowNames gets cleared on each iteration of this function
if (childWindowNames.ToString() == "NUIDocumentWindow")
{
NUIWindowCounter++;
if (NUIWindowCounter == 2) //The NEXT window is the Navigation Pane
{
stopIterating = true;
}
return 1; //Keep iterating
}
if (stopIterating == true)
{
SendMessage(hwndChild, WM_VSCROLL, (IntPtr)SB_PAGEDOWN, IntPtr.Zero);
return 0; //Stop iterating
}
}
Will SendMessage silently fail because of UIPI?
Thanks.
Addins run in the same security context as Outlook itself.
There is no requirement for any control to use the WM_VSCROLL message. Do you actually see it in Spy++?
I am attempting to process the windows messages for keyboard events and pass them into an awesomium webview. The webview has native handling for windows events, but the host program must capture them, and send them on.
I am using .net and the com interop to capture and send on the messages. The key events are passed on to the best of my knowledge, but the input is variable. For example, there are varying numbers of characters being entered in a text box for a single press.
Here is a sample of the code I am using (credit to Sekhat on the awesomium site) :
private void CreateHook()
{
uint threadId = Win32.GetWindowThreadProcessId(hWnd, IntPtr.Zero);
//hHook = Win32.SetWindowsHookEx(Win32.HookType.WH_CALLWNDPROC, wndProcDelegate, IntPtr.Zero, threadId);
hHook = Win32.SetWindowsHookEx(Win32.HookType.WH_GETMESSAGE, wndProcDelegate, IntPtr.Zero, threadId);
}
private int WndProcHook(int nCode, IntPtr wParam, ref Win32.Message lParam)
{
if (nCode >= 0)
{
Win32.TranslateMessage(ref lParam);
WndProc(ref lParam);
}
return Win32.CallNextHookEx(hHook, nCode, wParam, ref lParam);
}
wndProcDelegate = WndProcHook;
Am I handling the events wrong, leading to the variable number of inputs? I beleive the relevant parts are above, but can post the rest if needed.
On a side note, when I use WH_KEYBOARD instead of WH_GETMESSAGE an AccessViolationException is thrown. Is the KEYBOARD event the right one to use? Thank you for any assistance.
EDIT: I have done some further investigation and discovered that multiple messages of type 0x102 (WM_CHAR) are being passed to my program. The number passed matches the number of characters that awesomium is then outputting. Should I limit this to only the first message? While this may work, I would still like to know why this is happening.
I have managed to devise a working fix:
if (message.msg == 256)
{
accept = true;
}
if (accept && message.msg == 258)
{
view.InjectKeyboardEventWin((int)message.msg, (int)message.wparam, (int)message.lparam);
accept = false;
}
if (message.msg != 258)
{
view.InjectKeyboardEventWin((int)message.msg, (int)message.wparam, (int)message.lparam);
}
This code checks for a key down, and if found, set accept to true. The char message is only processed if a key down has been received since the last one. This allows text entry, while preserving other messages. I am still interested in discovering the root cause however.
I want to cause all open popups (with StaysOpen == false) to close from code. Basically I want to simulate the user clicking the mouse (which would close the popups) from code.
I don't need to actually simulate the click, I just need the resulting behavior. I've thought about just going through the visual tree looking for popups and closing each one, but that doesn't seem like the cleanest approach.
Thanks in advance for any help or opinions.
A WPF popup actually creates a new window (a Win32 window, not a WPF Window instance). So you can't find it in the Application.Windows collection, but you can probably find it using a Win32 API like EnumChildWindows.
Once you have the handle, you can retrieve the associated HwndSource. I think the RootVisual of the HwndSource is the Popup (didn't check, you might have to look deeper in the visual tree).
So the code should be similar to this (completely untested):
public static class PopupCloser
{
public static void CloseAllPopups()
{
foreach(Window window in Application.Current.Windows)
{
CloseAllPopups(window);
}
}
public static void CloseAllPopups(Window window)
{
IntPtr handle = new WindowInteropHelper(window).Handle;
EnumChildWindows(handle, ClosePopup, IntPtr.Zero);
}
private static bool ClosePopup(IntPtr hwnd, IntPtr lParam)
{
HwndSource source = HwndSource.FromHwnd(hwnd);
if (source != null)
{
Popup popup = source.RootVisual as Popup;
if (popup != null)
{
popup.IsOpen = false;
}
}
return true; // to continue enumeration
}
private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
}
Going through the visual tree is the way to do it that isn't dependent in any way on how you've created them in the first place. There may be much cleaner ways of doing it, but they all really depend on your implementation.
For instance, all of the Popups in my application are bound to view model objects that expose some kind of IsOpen property that the Popup binds to. If I needed to implement this functionality in my project, I'd just maintain a collection of these objects (or a generator method) that I could iterate over, and set IsOpen to false on each. This obviously won't work if you aren't building your UI the way I am, though.
The accepted answer (https://stackoverflow.com/a/3886139/12885902) wasn't doing the trick for me, because source.RootVisual was never of type Popup but of the internal type PopupRoot. The following tweak got the code to work:
private void CloseAllPopups()
{
foreach (Window window in Application.Current.Windows)
{
IntPtr handle = new WindowInteropHelper(window).Handle;
EnumThreadWindows(handle, ClosePopup, IntPtr.Zero);
}
}
private static bool ClosePopup(IntPtr hwnd, IntPtr lParam)
{
HwndSource source = HwndSource.FromHwnd(hwnd);
if (source?.RootVisual?.GetType().Name == "PopupRoot")
{
if (LogicalTreeHelper.GetParent(source.RootVisual) is Popup popup)
{
popup.IsOpen = false;
}
}
return true; // to continue enumeration
}
private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumThreadWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
Please also keep in mind that the CloseAllPopups() method has to be called by the main UI thread (e.g. Application.Current.Dispatcher.Invoke())!
My previous answer was also not working all the time. It only worked when the Visual Studio debugger was attached to the process. What ended up working in any case is the following:
Application.Current.Dispatcher.Invoke(() =>
{
PresentationSource.CurrentSources.OfType<HwndSource>()
.Select(h => h.RootVisual)
.OfType<FrameworkElement>()
.Select(f => f.Parent)
.OfType<Popup>()
.Where(popup => popup.IsOpen)
.ToList()
.ForEach(popup => popup.SetCurrentValue(Popup.IsOpenProperty, false));
});
Declare somewhere static array of opened popups:
static List<Popup> openedPopups = new List<Popup>();
Before open popup close all previsiously opened popups:
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// close all before opene popup
openedPopups.ForEach(p => p.IsOpen = false);
openedPopups.Clear(); // clear opened popus's collection, because they were closed
Popup1.IsOpen = true; // open popup I need open now
openedPopups.Add(Popup1); // remember it for future close
}