I need to show a modeless dialog in .NET 1.1. The following code works in .NET 2.0 or higher:
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetActiveWindow();
private void ShowModelessOwnedDialog()
{
IntPtr wHnd = GetActiveWindow();
NativeWindow parent = NativeWindow.FromHandle(wHnd);
MyForm f = new MyForm();
f.Show(parent);
}
The call Show(IWin32Window) was introduced in .NET 2.0. Do you know how to trick this code to work in .NET 1.1? Maybe any unmanaged call?
Here is an article from way back in 1999, showing how you call SetWindowLong to accomplish this. My condolences to you for having to use .NET version 1.
This is the way to assign an owner to a managed form in .NET 1.1. I extracted the following code from #Dave Markle answer and the Show(IWin32Window) implemantation of .NET 2.0.
private void AssignOwner()
{
AssignOwner(this, GetActiveWindow());
}
private void AssignOwner(Form f, IntPtr ownerHandle)
{
if (ownerHandle == IntPtr.Zero) return;
NativeWindow parent = NativeWindow.FromHandle(ownerHandle);
GetWindowLong(new HandleRef(f, f.Handle), -8);
SetWindowLong(new HandleRef(f, f.Handle), -8, new HandleRef(parent, ownerHandle));
}
public static IntPtr GetWindowLong(HandleRef hWnd, int nIndex)
{
if (IntPtr.Size == 4)
{
return GetWindowLong32(hWnd, nIndex);
}
return GetWindowLongPtr64(hWnd, nIndex);
}
public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, HandleRef dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, HandleRef dwNewLong);
MSDN states that after creating the window ownership cannot be transferred.
Because creating the window occurs in Form constructor, this poses a problem for you.
However Raymond Chen says:
Ownership is a concept that relates top-level windows. A top-level window can optionally have an owner, which is also specified when you call CreateWindowEx, and which you can change by a complicated mechanism described in my talk.
I assume the talk in question is from PDC 05 but I can't be certain.
Did you give SetParent a try?
static extern void SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
There is a MessageBox.Show overload taking and IWin32Window in .net 1.1
http://msdn.microsoft.com/en-us/library/aa335416(v=VS.71).aspx
public static DialogResult Show(
IWin32Window owner,
string text,
string caption,
MessageBoxButtons buttons,
MessageBoxIcon icon,
MessageBoxDefaultButton defaultButton
);
And this and example of getting an IWin2Window here
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'm trying to detect the horizontal scroll wheel movement globally in .net core 3.1 (or at least movement of any mouse wheel). I want to build this application to work for both, Windows 10 x64 and arm-linux. However, I only need the hook functionality on Windows. I have tried to use the WindowsHook which includes System.Windows.Forms MouseEvents and also the normal MouseKeyHook which it is based on. Both didn't work. The same goes for my implementations of SetWindowsHookEx and the Raw Input API. Trying them in .net core applications only results in a massive input lag as soon as the listeners are in place and no events actually getting triggered. I searched for like two days now and I start to believe you need to be a wizard to get such a global hook to work in .net core. Help would be highly appreciated!
Edit: This is my first time working with hooks and doing low level stuff so I don't understand all of it 100%. I only have this code snippet here atm. For the WindowsHook and MouseKeyHook I just use the examples which you can find on corresponding linked github projects.
Here is the SetWindowsHookEx version, I just call the constructor to get it running and it doesn't even capture hscroll I believe:
using System;
using System.Runtime.InteropServices;
class Intercept_Mouse
{
private static LowLevelMouseProc _proc = HookCallback;
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr _hookID = IntPtr.Zero;
public Intercept_Mouse()
{
_hookID = SetWindowsHookEx(WH_MOUSE_LL, _proc, IntPtr.Zero, 0);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam))
{
Console.WriteLine("Clicked");
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc 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);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
I developed C# application which responds to low level mouse clicks. I used mouse hook to do this. The application is working fine, whenever i click any window it responds and perform some task. But i want to do a modification here. I wanted to respond the mouse clicks whenever the click is performed on any buttons on the window. If i click on plain area of window it should not respond. Currently it responds wherever i click. I could not find how to identify whether i clicked on a button or not.
This is my code :
private IntPtr SetHook(MouseHookHandler proc)
{
using (ProcessModule module = Process.GetCurrentProcess().MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(module.ModuleName), 0);
}
private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
if (MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)
if (LeftButtonUp != null)
LeftButtonUp((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
if (MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam)
if (RightButtonUp != null)
RightButtonUp((MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)));
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
MouseHookHandler lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public 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);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
Please help me to solve this problem. Thank you.
It seems you are looking for something along the lines of WindowFromPoint.
Call that to see what is under the mouse cursor, then you can query it for whatever properties you are interested in.
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.
My application should perform some action whenever user pressed certain keys in windows.
Calling SetWindowsHookEx with WH_KEYBOARD_LL option seems to be standard way to achieve this. However in my case something is clearly wrong and callback in not fired.
Main method of my debugging console application:
static void Main(string[] args)
{
IntPtr moduleHandle = GetCurrentModuleHandle();
IntPtr hookHandle = IntPtr.Zero;
try
{
User32.HookProc hook = (nCode, wParam, lParam) =>
{
// code is never called :-(
if (nCode >= 0)
{
Console.WriteLine("{0}, {1}", wParam.ToInt32(), lParam.ToInt32());
}
return User32.CallNextHookEx(hookHandle, nCode, wParam, lParam);
};
hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);
Console.ReadLine(); //
}
finally
{
if (hoodHandle != IntPtr.Zero)
{
var unhooked = User32.UnhookWindowsHookEx(hookHandle);
Console.WriteLine(unhooked); // true
hookHandle = IntPtr.Zero;
}
}
}
GetCurrentModuleHandle method:
private static IntPtr GetCurrentModuleHandle()
{
using (var currentProcess = Process.GetCurrentProcess())
using (var mainModule = currentProcess.MainModule)
{
var moduleName = mainModule.ModuleName;
return Kernel32.GetModuleHandle(moduleName);
}
}
Imports from user32.dll and kernel32.dll:
public static class User32
{
public const int WH_KEYBOARD_LL = 13;
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
}
public static class Kernel32
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
Do you have any idea what I is my problem?
The SetwindowHook API cannot be called from a C# console application. You need to call it in a Windows DLL (it won't work in a .Net DLL).
There is certainly a way to work around this issue, and I did use it one of the applications I built a long while ago. But I don't remember it right now. You may be able to find out if you search long enough.
Is this a console app as Main(string[] args) and Console.ReadLine() would suggest?
if so then this might be the source of your problem
When you use the Win32 API like this, it is very important that you check for errors yourself. You no longer have the friendly .NET wrappers that will do it for you and throw an exception. This needs to be done in several places, but here's one:
hookHandle = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hook, moduleHandle, 0);
if (hookHandle == IntPtr.Zero) throw new Win32Exception();
The real problem is your use of mainModule.ModuleName. If only gives the name of the file, not the full path. GetModuleHandle() will fail and return IntPtr.Zero. As above, if you would have tested this then you'd have quickly found the problem.
There's an additional failure mode when you run this code in .NET 4.0. The CLR no longer fakes native modules for managed code. SetWindowsHookEx() needs a valid DLL handle but it doesn't actually use it since this is a low-level hook. The best way to get one is to ask for user32.dll, it is always loaded in a managed program:
IntPtr moduleHandle = LoadLibrary("user32.dll");
if (moduleHandle == IntPtr.Zero) throw new Win32Exception();
No need to release it.