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);
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.
I need to show Task Manager app programatically, maximize it and minimize it as any other window but there is a problem and it simply doesn't respond to ShowWindow(int hWnd, int nCmdShow).
I am pretty sure I use the correct handle because I enumerated all of the windows with EnumWindows(PCallBack callback, int lParam) and the only window that didn't respond was the task manager window with title process.MainWindowTitle = "Task Manager", I even manually found its handle using spy++ but it still doesn't respond to SW_SHOWNORMAL or any other nCmdShow parameter. I tried running apps as administrator to see if it has something to do with the issue but they kept behaving like normal when proper handle was given to ShowWindow function;
private delegate bool PCallBack(int hWnd, int lParam);
private static void ShowWindows()
{
EnumWindows(new PCallBack(FindWindows), 0);
}
private bool FindWindows(int handle, int lparam)
{
Console.WriteLine("showing");
ShowWindow(handle, (int)SW.SHOWMINIMIZED);
ShowWindow(handle, (int)SW.SHOWNORMAL);
Thread.Sleep(3000);
return true;
}
static void Main(string[] args)
{
ShowWindows();
}
This code literally shows every window EnumWindows can find even if they are not visible and task manager was never shown which proved to me that the problem has nothing to do with wrong handle.
This is how I find it by the way.
// the correct handle of Task Manager window
var handle = (int)Process.GetProcessesByName("taskmgr").FirstOrDefault().MainWindowHandle;
Basically this is my problem. Need help.
it simply doesn't respond to ShowWindow(int hWnd, int nCmdShow).
I tested on Windows 10 and this works for me :
Manifest file with level="requireAdministrator"
Test :
IntPtr hWndTarget = FindWindow("TaskManagerWindow", null);
bool bRet = ShowWindow(hWndTarget, SW_SHOWMINIMIZED);
with declarations :
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int SW_HIDE = 0;
public const int SW_SHOWNORMAL = 1;
public const int SW_SHOWMINIMIZED = 2;
public const int SW_SHOWMAXIMIZED = 3;
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
}
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.
Basically, how do I tell if my program is layered above all the other ones?
A fairly simple way is to P/Invoke GetForegroundWindow() and compare the HWND returned to the application's form.Handle property.
using System;
using System.Runtime.InteropServices;
namespace MyNamespace
{
class GFW
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public bool IsActive(IntPtr handle)
{
IntPtr activeHandle = GetForegroundWindow();
return (activeHandle == handle);
}
}
}
Then, from your form:
if (MyNamespace.GFW.IsActive(this.Handle))
{
// Do whatever.
}
You can use:
if (GetForegroundWindow() == Process.GetCurrentProcess().MainWindowHandle)
{
//do stuff
}
WINAPI imports (at class level):
[System.Runtime.InteropServices.DllImport("user32.dll")] public static extern bool GetForegroundWindow();
Assign a property to hold the value, and add the check to the form's GotFocus event through IDE, or after InitializeComponent();
e.g.:
//.....
InitalizeComponent();
this.GotFocus += (myFocusCheck);
//...
private bool onTop = false;
private void myFocusCheck(object s, EventArgs e)
{
if(GetFore......){ onTop = true; }
}
If your window inherits form, you can check Form.Topmost property
A good solution is given by this answer to an identical question:
https://stackoverflow.com/a/7162873/386091
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
{
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero) {
return false; // No window is currently activated
}
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
The currently accepted solution by Euric doesn't work if your program shows a dialog box or has detachable windows (like if you use a windows docking framework).