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.
Related
I developed an add-on for SAP Business One, as a C# Windows Forms application.
In this add-on, using LoadLibraryEx, I load a native unmanaged DLL (correct me if I'm wrong) written in C++.
The add-on calls a method of the DLL, in this way:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _MyDllMethod();
//...
//load library
var handle = LoadLibraryEx(libPath, IntPtr.Zero, 0x00000008 /*LOAD_WITH_ALTERED_SEARCH_PATH*/);
//invoke method
Type delegateFunType = typeof(_MyDllMethod);
IntPtr funAddr = GetProcAddress(handle, delegateFunType.Name);
var fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);
int result = fun.Invoke(); //now a window appears
This method opens a window with which the user interacts.
Everything working fine, except that such window starts minimized in the task bar, while I need it to appear in the forground as the active window. How can I acieve this?
Finally I found this hack: launching a parallel thread that looks for the window that will be opened by the DLL and bring it to the foreground with Windows API functions:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _MyDllMethod();
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
[DllImport("User32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
private const int ALT = 0xA4;
private const int EXTENDEDKEY = 0x1;
private const int KEYUP = 0x2;
private const int SW_RESTORE = 9;
//...
//launch a parallel thread that looks for library window
//and brings it to the foreground
ThreadPool.QueueUserWorkItem(delegate {
bool windowFound = false;
int attempts = 0;
while (!windowFound && attempts < 10)
{
attempts++;
//check frequently
Thread.Sleep(200);
//look for the window using its class name
IntPtr handle = FindWindow("DllWindowClassName", null);
//check it is a running process
if (handle != IntPtr.Zero)
{
bool success = false;
//if window is minimized to icon
if (IsIconic(handle))
{
//then show it
success = ShowWindow(handle, SW_RESTORE);
}
//bring window to front
success = SetForegroundWindow(handle);
//once done, this thread can terminate
if (success)
{
windowFound = true;
}
else
{
//in case of failure, try this hack
//simulate a key press and release (hack for SetForegroundWindow to work)
keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);
//bring window to front
success = SetForegroundWindow(handle);
//once done, this thread can terminate
if (success)
{
windowFound = true;
}
}
}
}
}, null);
//load library
var handle = LoadLibraryEx(libPath, IntPtr.Zero, 0x00000008 /*LOAD_WITH_ALTERED_SEARCH_PATH*/);
//invoke method
Type delegateFunType = typeof(_MyDllMethod);
IntPtr funAddr = GetProcAddress(handle, delegateFunType.Name);
var fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);
int result = fun.Invoke(); //now a window appears
You can discover window class name using FindWindow, passing window title, and then with GetClassName. Class name is more reliable than the title, which can change with the language and in the time.
Here's the code I'm using:
private void Button_Click(object sender, RoutedEventArgs e) {
SendMessage(GetDesktopWindow(), LVM_ARRANGE, LVA_SNAPTOGRID, 0);
}
public const uint LVM_ARRANGE = 0x1000 + 22;
public const int LVA_SNAPTOGRID = 0x0005;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
Nothing happens when I run it. Code is borrowed from https://www.codeproject.com/Messages/1168961/Re-Auto-Arrange-desktop-icons.aspx
Tried different Windows versions too.
This code worked for me. Note that it doesn't appear to change the options, so although it arranges to the grid for me the View->Align Icons To Grid is still unchecked. The issue is outlined here: How do I get the window handle of the desktop?
Your problem results from a fairly widespread confusion over what the
desktop window actually is. The GetDesktopWindow function does
precisely what it's documented to do: it returns a handle to the
desktop window. This, however, is not the same window that contains
the desktop icons.
So I used the answer here: Get handle to desktop / shell window but with replacing or adding methods and p/invoke calls until I had everything necessary because that link only gives method calls and not the dll imports (I may have left some superfluous stuff in here).
static void Main()
{
SendMessage(GetDesktopWindow(DesktopWindow.SysListView32), LVM_ARRANGE, LVA_SNAPTOGRID, 0);
Console.ReadLine();
}
public const int LVM_ARRANGE = 4118;
public const int LVA_SNAPTOGRID = 5;
[DllImport("user32.dll")]
static extern IntPtr GetShellWindow();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
public enum DesktopWindow
{
ProgMan,
SHELLDLL_DefViewParent,
SHELLDLL_DefView,
SysListView32
}
public static IntPtr GetDesktopWindow(DesktopWindow desktopWindow)
{
IntPtr _ProgMan = GetShellWindow();
IntPtr _SHELLDLL_DefViewParent = _ProgMan;
IntPtr _SHELLDLL_DefView = FindWindowEx(_ProgMan, IntPtr.Zero, "SHELLDLL_DefView", null);
IntPtr _SysListView32 = FindWindowEx(_SHELLDLL_DefView, IntPtr.Zero, "SysListView32", "FolderView");
if (_SHELLDLL_DefView == IntPtr.Zero)
{
EnumWindows((hwnd, lParam) =>
{
if (GetWindowText(hwnd) == "WorkerW")
{
IntPtr child = FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (child != IntPtr.Zero)
{
_SHELLDLL_DefViewParent = hwnd;
_SHELLDLL_DefView = child;
_SysListView32 = FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView"); ;
return false;
}
}
return true;
}, IntPtr.Zero);
}
switch (desktopWindow)
{
case DesktopWindow.ProgMan:
return _ProgMan;
case DesktopWindow.SHELLDLL_DefViewParent:
return _SHELLDLL_DefViewParent;
case DesktopWindow.SHELLDLL_DefView:
return _SHELLDLL_DefView;
case DesktopWindow.SysListView32:
return _SysListView32;
default:
return IntPtr.Zero;
}
}
public static string GetWindowText(IntPtr hWnd)
{
int size = GetWindowTextLength(hWnd);
if (size > 0)
{
var builder = new StringBuilder(size + 1);
GetWindowText(hWnd, builder, builder.Capacity);
return builder.ToString();
}
return String.Empty;
}
I have a code
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
public void Click(string _btnTitle)
{
int hwnd = 0;
IntPtr hwndChild = IntPtr.Zero;
//Get a handle for the Calculator Application main window
// foreach (Process p in Process.GetProcesses())
//{
hwnd = FindWindow(null, FrmTitle);
if (hwnd != 0)
{
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", _btnTitle);
SendMessage((int)hwndChild, BN_CLICKED, 0, IntPtr.Zero);
}
}
I can't Click button "Yes" on MessageBox of application :(
Anyone got a tip? Tks
You aren't sending the correct message.
Try using BM_CLICK (0x00F5) in your call to SendMessage(). That should work provided that hwndChild is the window handle of the button, rather than the container dialog box.
BN_CLICKED doesn't work because that is a notification code, not a message.
I have been tasked with changing the text of a button in a window. I don't have and cannot access the source code as it's owned by a company we have a paid subscription with.
How can I change the button text with no source code? I'm trying with pInvoke and having problems. The window title changes depending on who you are working with:
"Order Entry Sheet - LASTNAME, FIRSTNAME"
So the window title may not be useable for me inside of the win32 call
FindWindow(string lpClassName, string lpWindowName);
I know both params are optional. I'm using Spy++ and I'm not sure what to use for lpClassName. The class name I see listed is #32770 (Dialog). I tried it and got a return of 0.
IntPtr windowHandle = FindWindow("#32770 (Dialog)", null);
How can I change the button text from another process?
UPDATE
According to MSDN I should be able to achieve this via SetWindowText.
Changes the text of the specified window's title bar (if it has one).
If the specified window is a control, the text of the control is
changed. However, SetWindowText cannot change the text of a control in
another application.
I can't use SetWindowText to do what I want. Is it possible to use something else?
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32", SetLastError = true)]
public static extern int EnumWindows(CallBack x, int y);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, CallBack lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
public const uint WM_SETTEXT = 0x000C;
public delegate bool CallBack(int hwnd, int lParam);
public static void Main()
{
CallBack windowsCallback = new CallBack(IterateWindows);
EnumWindows(windowsCallback, 0);
}
public static bool IterateChildren(int hwnd, int lParam)
{
string newButtonText = "Some text";
bool continueIteratingChildren = true;
//Console.WriteLine("Child handle: " + hwnd);
int length = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
//Console.WriteLine(sb);
if (sb.ToString().StartsWith("My Button Text "))
{
HandleRef hrefHWndTarget = new HandleRef(null, (IntPtr)hwnd);
SendMessage(hrefHWndTarget, WM_SETTEXT, IntPtr.Zero, newButtonText);
continueIteratingChildren = false;
}
return continueIteratingChildren;
}
public static bool IterateWindows(int hwnd, int lParam)
{
bool continueIteratingWindows = true;
int windowTextLength = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(windowTextLength + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
if (sb.ToString().StartsWith("My Window Caption"))
{
//Console.Write("Window handle is ");
//Console.WriteLine(hwnd);
//Console.WriteLine(sb);
//Console.WriteLine(Marshal.GetLastWin32Error());
var childrenCallback = new CallBack(IterateChildren);
EnumChildWindows((IntPtr)hwnd, childrenCallback, IntPtr.Zero);
continueIteratingWindows = false;
}
return continueIteratingWindows;
}
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