I have a C# wrapper for some Win32 operations involving window handles, but I am experiencing an unexpected crash, with no details, when I call a Win32 function.
Interestingly, this whole code sample works fine when the class is constructed (at application init time), but later fails when the same buildCache() method is called.
Here is the relevant code:
public delegate bool CallBack(int hWnd, int lParam);
public class Win32Interop {
private Dictionary<int, string> windowCache = new Dictionary<int, string>();
public Win32Interop() {
buildCache();
}
public void buildCache() {
windowCache.Clear();
CallBack hWndCacher = new CallBack(saveHWndHandler);
EnumWindows(hWndCacher, 0);
}
public void doThings(string title, uint message, bool rebuildCache = false) {
//Use the window title to get its handle
int hWnd = titleToHWnd(title, rebuildCache);
SendMessage(hWnd, message, 0, 0);
}
private bool saveHWndHandler(int hWnd, int lParam) {
if(IsWindow(hWnd) != 0) { / ***** CRASHES HERE ***** /
int length = GetWindowTextLength(hWnd);
StringBuilder title = new StringBuilder(length + 1);
GetWindowText(hWnd, title, title.Capacity);
string formatted = title.ToString().Trim();
windowCache.Add(hWnd, formatted);
}
return true;
}
private int titleToHWnd(string title, bool rebuildCache = false) {
if(rebuildCache)
buildCache();
if(windowCache.ContainsValue(title)) {
return windowCache.FirstOrDefault(x => x.Value.Contains(title)).Key;
} else {
throw new KeyNotFoundException(string.Format("\"{0}\" is not a window title which is available in the cache.", title));
}
}
#region Win32 API Functions
[DllImport("user32.dll")]
private static extern int EnumWindows(CallBack lpEnumFunc, int lParam);
[DllImport("user32.dll")]
private static extern int GetWindowText(int hWnd, StringBuilder lpString, int maxCount);
[DllImport("user32.dll")]
private static extern int GetWindowTextLength(int hWnd);
[DllImport("user32.dll")]
private static extern int IsWindow(int hWnd);
[DllImport("user32.lib")]
private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
#endregion
}
Inside of the saveHWndHandler() method, I've marked the line were the debugger shows execution stops. Interestingly, there are usually ~300 window handles returned by EnumWindows(), and it always crashes on iteration number 45 or 46. The window handle it crashes on is a reasonable value, such as 12345.
According to MSDN, IsWindow() should return 0, if a window was not associated with the handle, not crash the thread.
Does anyone know why this is happening? There isn't a thrown exception, or any details in the Windows event log.
Thank you.
For those who don't want to figure out the buildCache() process works:
(1.) When buildCache() is called, the dictionary of <HWnd, Title> values is cleared.
(2.) The Win32 function EnumWindows() is called, which calls the saveHWndHandler() method for each window handle.
(3.) saveHWndHandler() will check if the current window handle still exists, the call another Win32 to function to get the window title from the handle.
(4.) The title and window handle are added to the dictionary.
I can't reproduce your issue, but a likely problem is that all of your P/Invoke signatures are wrong. HWND, LPARAM, and WPARAM data types need to at least be mapped to IntPtr in P/Invoke code.
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam,
IntPtr lParam);
You'll need to change your corresponding instance method signatures and usages to match these correct signatures.
Related
I have a WPF custom virtual keyboard application. I need to host this WPF application as standalone and user should be able to use this to enter values for any window application.
How to get the current focused application window? ex: notepad, notepad++ or any other window applications which takes in input.
I have tried below code to get the current active window, but it returns the WPF virtual keyboard application itself since it is also in active state
var window = System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(w => w.IsActive);
The below code works fine however, this requires windowname as param to set the foregroundwindow
public partial class MainWindow : Window{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
private void button1_Click(object sender, RoutedEventArgs e){
SetForegroundWindow(FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad"));
}
}
To get the current active window handle, you could use GetForegroundWindow from the user32.dll.
In C# it should look something like
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
Or in case your application window will be the active window, you could use GetWindow
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint {
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
So you could pass your own window handle as first argument and GW_HWNDNEXT as second. As a result you get the handle for the window that is below your current window.
EDIT
As IInspectable mentioned, the above won't work within an WPF application.
But you could try using the following using EnumWindows:
// Get own hWnd
WindowInteropHelper windowHwnd = new(this);
IntPtr ownHwnd = windowHwnd.Handle;
// Find target hWnd
IntPtr targetHWnd = IntPtr.Zero;
NativeMethods.EnumWindows((hWnd, lParam) =>
{
if (hWnd == ownHwnd) { return true; } // Ignore own window
if (!NativeMethods.IsWindowVisible(hWnd)) { return true; } // Ignore hidden windows
if (GetClassName(hWnd).Equals("Shell_TrayWnd", StringComparison.OrdinalIgnoreCase)) { return true; } // Ignore taskbar window
if (string.IsNullOrEmpty(GetWindowTitle(hWnd))) { return true; } // Ignore windows without a title
targetHWnd = hWnd; // Found target hWnd
return false; // Stop iterating
}, IntPtr.Zero);
// Used functions
private string GetWindowTitle(IntPtr hWnd)
{
int textLength = NativeMethods.GetWindowTextLength(hWnd);
StringBuilder sb = new(textLength + 1);
NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
private string GetClassName(IntPtr hWnd)
{
StringBuilder sb = new(256);
NativeMethods.GetClassName(hWnd, sb, sb.Capacity);
return sb.ToString().Trim();
}
internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}
Edit 2
To ensure the WPF hWnd is created, you could use WindowInteropHelper.EnsureHandle() method instead of the Handle property. This is important if the WPF window has not been displayed yet.
I have a keyboard hook that works great but has one issue I can't resolve. I want the hook to run on async and I'm not able to do it. Normally I use it like this
private void button1_Click(object sender, EventArgs e)
{
var res = new InterceptKeys();
res.startHook();
}
When I click button 1 the hooks starts and works correctly. But when i click the button 2 ...
private void button2_Click(object sender, EventArgs e)
{
Thread.Sleep(100000) //simulates an operation that keeps form busy
}
Now the hook doesn't work anymore and have to wait for the sleep to finish. My question is, it is possible to make the hook async? I tried to call it like a task, but the hook doesn't work.
public class InterceptKeys
{
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hwnd, StringBuilder ss, int count);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private IntPtr _hookID = IntPtr.Zero;
public void startHook()
{
if(_hookID == IntPtr.Zero)
{
_hookID = SetHook();
}
}
public void endHook()
{
UnhookWindowsHookEx(_hookID);
}
private IntPtr SetHook()
{
return SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, IntPtr.Zero, 0);
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
int cont = 0;
Dictionary<cKeyLogger.VK, Tuple<int, string>> combinacions = new Dictionary<cKeyLogger.VK, Tuple<int, string>> { { cKeyLogger.VK.F3, new Tuple<int, string>(2,"{r}{UP}") }, { cKeyLogger.VK.F4, new Tuple<int, string>(2, "{R}{DOWN}") } };
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
cKeyLogger.VK vkCode = (cKeyLogger.VK)Marshal.ReadInt32(lParam);
string window = ActiveWindowTitle();
if(wParam == (IntPtr)WM_KEYDOWN)
{
Console.WriteLine(vkCode + " _ " + window);
if (window.ToLower().Contains("excel")) //just applies to excel to do some testing
{
//Do something
}
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private static string ActiveWindowTitle()
{
//Create the variable
const int nChar = 256;
StringBuilder ss = new StringBuilder(nChar);
//Run GetForeGroundWindows and get active window informations
//assign them into handle pointer variable
IntPtr handle = IntPtr.Zero;
handle = GetForegroundWindow();
if (GetWindowText(handle, ss, nChar) > 0) return ss.ToString();
else return "";
}
}
I'd be wary of trying to tackle this problem by moving the hook to a separate thread. If you use a separate thread for the keyboard hook, when your code eventually hits the point where it needs to do something here:
if (window.ToLower().Contains("excel")) //just applies to excel to do some testing
{
//Do something
}
If there is a chance that the UI thread is still going to be blocked at this point, the code to "do something" is still going to be delayed. That delay in calling CallNextHookEx will actually get your keyboard hook removed by the OS.
I try to visualise hooks as chain links, when your application installs a hook it has a duty to do what it needs to as fast as possible before calling CallNextHookEx so that the next application can process the hook and Windows messages can keep flowing. If the OS detects an application is destabilising this it will remove the hook.
My question to you is: can move the blocking process from the UI thread into a separate thread? Rather than trying to move the hook into a separate thread. I've written many applications which use hooks and this is the way I've always done it.
Hope this helps.
I am trying to use CoInternetSetFeatureEnabled API for blocking prompt dialog as it show below in application that hosts WebBrowser control, but it does not work. Updating registry does not work either, following MSDN - Internet Feature Controls (I..L). I am using IE9 on Win7 64 bit.
FEATURE_BLOCK_INPUT_PROMPTS Internet Explorer 7. Enable or disable the
pop-up blocker to show input prompt dialog boxes. Used pop-up blocker
to mitigate the risk of spoofing.
private const int FEATURE_BLOCK_INPUT_PROMPTS = 27;
[DllImport("urlmon.dll")]
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
public static extern int CoInternetSetFeatureEnabled(
int FeatureEntry,
[MarshalAs(UnmanagedType.U4)] int dwFlags,
bool fEnable);
public static int disableInputPrompts(bool state)
{
return CoInternetSetFeatureEnabled(
FEATURE_BLOCK_INPUT_PROMPTS, SET_FEATURE_ON_PROCESS, state);
}
Update Just have tried in WinXP with IE8 and this feature is working. Does it depends on something else?
Update According to msdn this feature is by default enabled in IE9 but it does not work. Clicking on "Try It" button in w3schools sample initiates the prompt dialog, is that a security bug?
By default, this feature is enabled for Internet Explorer and disabled for
applications hosting the WebBrowser Control.
Any working alternative on how to block prompt dialog in WebBrowser control?
Can this be done by implementing custom security manager IInternetSecurityManager?
The only option I have for now is to use SetWindowsHookEx, and is based on Suppressing Hosted WebBrowser Control Dialogs sample.
internal static class WindowsInterop
{
private const Int32 WM_COMMAND = 0x0111;
private const Int32 WM_INITDIALOG = 0x0110;
private const Int32 WM_SYSCOMMAND = 0x0112;
private const Int32 SC_CLOSE = 0xF060;
private static IntPtr _pWH_CALLWNDPROCRET = IntPtr.Zero;
private static HookProcedureDelegate _WH_CALLWNDPROCRET_PROC =
new HookProcedureDelegate(WindowsInterop.WH_CALLWNDPROCRET_PROC);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(Int32 hooktype,
HookProcedureDelegate callback, IntPtr hMod, UInt32 dwThreadId);
[DllImport("user32.dll")]
private static extern IntPtr UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
private static extern Int32 CallNextHookEx(IntPtr hhk, Int32 nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern Int32 GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern Int32 GetWindowText(IntPtr hWnd,
StringBuilder text, Int32 maxLength);
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg,
IntPtr wParam, IntPtr lParam);
// Hook Types
private const Int32 WH_CALLWNDPROCRET = 12;
[StructLayout(LayoutKind.Sequential)]
private struct CWPRETSTRUCT
{
public IntPtr lResult;
public IntPtr lParam;
public IntPtr wParam;
public UInt32 message;
public IntPtr hwnd;
};
// Delegate for a WH_ hook procedure
private delegate Int32 HookProcedureDelegate(Int32 iCode,
IntPtr pWParam, IntPtr pLParam);
// Delegate for the EnumChildWindows method
private delegate Boolean EnumerateWindowDelegate(IntPtr pHwnd, IntPtr pParam);
// Add a Hook into the CALLWNDPROCRET notification chain
internal static void Hook()
{
if (WindowsInterop._pWH_CALLWNDPROCRET == IntPtr.Zero)
{
WindowsInterop._pWH_CALLWNDPROCRET = SetWindowsHookEx(
WH_CALLWNDPROCRET,
WindowsInterop._WH_CALLWNDPROCRET_PROC,
IntPtr.Zero,
(uint)AppDomain.GetCurrentThreadId());
}
}
// Remove the Hook into the CALLWNDPROCRET notification chain
internal static void Unhook()
{
if (WindowsInterop._pWH_CALLWNDPROCRET != IntPtr.Zero)
{
UnhookWindowsHookEx(WindowsInterop._pWH_CALLWNDPROCRET);
}
}
// Hook proceedure called by the OS when a message has been processed by the target Window
private static Int32 WH_CALLWNDPROCRET_PROC(Int32 iCode,
IntPtr pWParam, IntPtr pLParam)
{
if (iCode < 0)
return CallNextHookEx(WindowsInterop._pWH_CALLWNDPROCRET,
iCode, pWParam, pLParam);
CWPRETSTRUCT cwp = (CWPRETSTRUCT)
Marshal.PtrToStructure(pLParam, typeof(CWPRETSTRUCT));
Console.WriteLine(cwp.message);
if (cwp.message == WM_INITDIALOG)
{
// A dialog was initialised, find out what sort it was via it's Caption text
Int32 iLength = GetWindowTextLength(cwp.hwnd);
StringBuilder sb = new StringBuilder(iLength + 1);
GetWindowText(cwp.hwnd, sb, sb.Capacity);
var title = sb.ToString();
if (String.IsNullOrEmpty(title) == false &&
title.IndexOf("prompt", StringComparison.OrdinalIgnoreCase) >= 0)
{
// just close it
SendMessage(cwp.hwnd, WM_SYSCOMMAND, new IntPtr(SC_CLOSE), IntPtr.Zero);
return 1;
}
}
// Call the next hook in the chain
return CallNextHookEx(WindowsInterop._pWH_CALLWNDPROCRET, iCode, pWParam, pLParam);
}
}
I have a situation like this.
I have the window handle of an application. I need to activate it. I tried all these functions but is not working always.(most of the time , it doesn't work the first time and I'll have to manually click on it to activate it. Second attempt onwards it works fine)
The reason why I am doing this is because I have code written in the Form.Activate event of the form which I need to execute.
Application is a single instance application. When a new instance is created , it first checks for the existence of any other process, If found, the handle of old process is passed to the these functions so that user can work on the old form.
Application is called from a different C application.
[DllImport("user32.dll")]
public static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
SetForgroundWindow only works if its process has input focus. This is what I use:
public static void forceSetForegroundWindow( IntPtr hWnd, IntPtr mainThreadId )
{
IntPtr foregroundThreadID = GetWindowThreadProcessId( GetForegroundWindow(), IntPtr.Zero );
if ( foregroundThreadID != mainThreadId )
{
AttachThreadInput( mainThreadId, foregroundThreadID, true );
SetForegroundWindow( hWnd );
AttachThreadInput( mainThreadId, foregroundThreadID, false );
}
else
SetForegroundWindow( hWnd );
}
You need to find the window using something like Window title and then active it as follows:
public class Win32 : IWin32
{
//Import the FindWindow API to find our window
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindowNative(string className, string windowName);
//Import the SetForeground API to activate it
[DllImport("User32.dll", EntryPoint = "SetForegroundWindow")]
private static extern IntPtr SetForegroundWindowNative(IntPtr hWnd);
public IntPtr FindWindow(string className, string windowName)
{
return FindWindowNative(className, windowName);
}
public IntPtr SetForegroundWindow(IntPtr hWnd)
{
return SetForegroundWindowNative(hWnd);
}
}
public class SomeClass
{
public void Activate(string title)
{
//Find the window, using the Window Title
IntPtr hWnd = win32.FindWindow(null, title);
if (hWnd.ToInt32() > 0) //If found
{
win32.SetForegroundWindow(hWnd); //Activate it
}
}
}
You have to get the form using FromHandle:
f = Control.FromHandle(handle)
then you can can call Activate on the result:
f.Activate()
I'm trying to use SendMessage to Notepad, so that I can insert written text without making Notepad the active window.
I have done something like this in the past using SendText, but that required giving Notepad focus.
Now, first I'm retrieving the Windows handle:
Process[] processes = Process.GetProcessesByName("notepad");
Console.WriteLine(processes[0].MainWindowHandle.ToString());
I've confirmed it's the right handle for Notepad, the same shown within Windows Task Manager.
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
From here, I haven't been able to get SendMessage to work in all my experimentation. Am I going in the wrong direction?
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
private void button1_Click(object sender, EventArgs e)
{
Process [] notepads=Process.GetProcessesByName("notepad");
if(notepads.Length==0)return;
if (notepads[0] != null)
{
IntPtr child= FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, textBox1.Text);
}
}
WM_SETTEXT=0x000c
You first have to find the child window where the text is entered. You can do this by finding the child window with the window class "Edit".
Once you have that window handle, use WM_GETTEXT to get the text which is already entered, then modify that text (e.g., add your own), then use WM_SETTEXT to send the modified text back.
using System.Diagnostics;
using System.Runtime.InteropServices;
static class Notepad
{
#region Imports
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
//this is a constant indicating the window that we want to send a text message
const int WM_SETTEXT = 0X000C;
#endregion
public static void SendText(string text)
{
Process notepad = Process.Start(#"notepad.exe");
System.Threading.Thread.Sleep(50);
IntPtr notepadTextbox = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessage(notepadTextbox, WM_SETTEXT, 0, text);
}
}