This relates to a VB6 app that shows a C# form via interop.
An event in the C# form causes one of the VB6 app forms to show.
Usually, when this VB6 form is made to be hidden (Form.Hide) the underlying C# form is brought to the front.
But if during its life-time the VB6 form has caused a MsgBox to be shown, then the underlying C# form will not be at the front when the VB6 form is made to be hidden.
Why does this happen? It's like the MsgBox is changing the Z-Order of the forms.
"How do I cause the C# form to show after the VB6 is hidden? Do I have to use window handles?"
Assuming you are ok with the orphaned msgbox being kept open. When the VB6 form is Hidden you need to fire an event to get the Window Handle:
public static int FindWindow(string windowName, bool wait)
{
int hWnd = FindWindow(null, windowName);
while (wait && hWnd == 0)
{
System.Threading.Thread.Sleep(500);
hWnd = FindWindow(null, windowName);
}
return hWnd;
}
Then bring the C# window to the Top:
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
private static void ForceForegroundWindow(IntPtr hWnd)
{
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(hWnd);
ShowWindow(hWnd, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(hWnd);
ShowWindow(hWnd, SW_SHOW);
}
}
Ref: SetForegroundWindow Win32-API not always works on Windows-7
I got it to work by using the NativeWindow class, following the last answer in this thread: http://social.msdn.microsoft.com/Forums/en-US/2692df26-317c-4415-816b-d08fe6854df8/vbnet-vb6-win32-api-problems?forum=vbinterop
That code uses FindWindowEx to get the handle of the VB6 window, which is unecessary because you can simply pass the handle of the VB6 window to the .NET form:
public void ShowDotNetForm(IntPtr hwndMain)
{
NativeWindow vb6Window = new NativeWindow();
vb6Window.AssignHandle(hwndMain);
f.Show(vb6Window);
}
The code in the VB6 form is:
dotNetObj.ShowDotNetForm Me.hWnd
Passing the window handle from VB6 is better because FindWindowEx requires you to know the text of the window title to get the handle.
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 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.
What I'm trying to do is activate another application and send a key input to it to trigger a button. However this code doesn't seem to work. It looks like it can find the application but it does not activate it, and does not send the key.
[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);
private void debugButton_Click(object sender, EventArgs e)
{
//GetProcess by Class
IntPtr rightNowHandle = FindWindow("WindowsForms10.Window.8.app.0.24dc298_r17_ad2", null);
//Get Handle by Process
Process proc = Process.GetProcessesByName("RightNow.CX")[0];
IntPtr ptrFF = proc.MainWindowHandle;
//Get a handle for the Calculator Application main window
if (rightNowHandle == IntPtr.Zero)
{
MessageBox.Show("Could Not Find Right Now");
return;
}
SetForegroundWindow(rightNowHandle); //Activate Handle By Class
//SetForegroundWindow(ptrFF); //Activate Handle By Process
SendKeys.SendWait("{F1}");
}
And here is what I pull with Window Spy
Any help would be greatly appreciated. Thanks.
Figured out my issue.
SetForegroundWindow does not show the application if it's minimized, which is what I was anticipating.
Next I used Windows Input Simulator, to send the input instead of SendKeys.
Here's the code I ended up using.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private const int SW_RESTORE = 9;
private void debugButton_Click(object sender, EventArgs e)
{
//GetProcess by Class
IntPtr rightNowHandle = FindWindow("WindowsForms10.Window.8.app.0.24dc298_r17_ad2", null);
//Get Handle by Process
Process proc = Process.GetProcessesByName("RightNow.CX")[0];
IntPtr ptrFF = proc.MainWindowHandle;
//Get a handle for the Calculator Application main window
if (rightNowHandle == IntPtr.Zero)
{
MessageBox.Show("Could Not Find Right Now");
return;
}
SetForegroundWindow(ptrFF); //Activate Handle By Process
ShowWindow(ptrFF, SW_RESTORE); //Maximizes Window in case it was minimized.
//SetForegroundWindow(rightNowHandle); //Activate Handle By Class
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.LMENU, VirtualKeyCode.VK_A); //Send Left Alt + A
}
I have an windows app on .net 2.0. On Form1, I open a PrintDialog. How can I get that dialog's handle from my code?
I have tried a lot of win32 functions: EnumWindows, EnumChildWindows, FindWindow, FindWindowEx but it cannot find my PrintDialog. All I can find is just Form1 and its children are controls on it. There's no way I can get PrintDialog's handle.
Some code that i have tried:
Import win32:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
calling win32 functions:
using (PrintDialog dlg = new PrintDialog
{
AllowCurrentPage = false,
AllowSomePages = true,
AllowSelection = false
})
{
IntPtr printHandle = CustomPrintDialog.FindWindow("#32770", "Print");
// some logic with printHandle go here
if (dlg.ShowDialog(this)==DialogResult.OK){
// some logic go here
}
}
I have checked with Spy++, there is still a PrintDialog window. That PrintDialog window has Parent Window's handle exactly same as Form1's handle.
Does anyone can help me to get a PrintDialog's handle from its parent window?
The problem is that underlying window for the PrintDialog is created during the execution of the ShowDialog method. It does not exist before this method's invocation, that's why you cannot find the window.
So you must inject your work on PrintDialog handle inside ShowDialog. This can be achieved with the help of Control.BeginInvoke method:
public partial class Form1 : Form
{
...
private ShowPrintDialog()
{
using (var pd = new PrintDialog())
{
BeginInvoke(new MethodInvoker(TweakPrintDialog));
if (pd.ShowDialog(this) == DialogResult.OK)
{
// printing
}
}
}
private void TweakPrintDialog()
{
var printDialogHandle = GetActiveWindow();
// do your tweaking here
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetActiveWindow();
}
Another problem is to find PrintDialog window. GetActiveWindow is really a straightforward way to accomplish this, because the dialog is expected to be active while ShowDialog is in action.
The more reliable solution may include enumerating top-level windows and analyzing their owners and/or other props. Here is one possible approach, assuming that print dialog is the only window at the moment that is owned by the form. The TweakPrintDialog method from the previous snippet is modified:
private void TweakPrintDialog()
{
uint processId;
var threadId = GetWindowThreadProcessId(this.Handle, out processId);
EnumThreadWindows(threadId, FindPrintDialog, IntPtr.Zero);
// printDialogHandle field now contains the found handle
// do your tweaking here
}
private IntPtr printDialogHandle;
private bool FindPrintDialog(IntPtr handle, IntPtr lParam)
{
if (GetWindow(handle, GW_OWNER) == this.Handle)
{
printDialogHandle = handle;
return false;
}
else
{
return true;
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private delegate bool EnumWindowProc(IntPtr handle, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool EnumThreadWindows(uint threadId, EnumWindowProc enumProc, IntPtr lParam);
private const uint GW_OWNER = 4;
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr handle, uint cmd);
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()