I am trying to destroy a child window created by a third party application. I tried to use WinApi to make this happen but it seems that DestroyWindow(hwnd) doesn't do the trick. I indeed get a handle to work with but nothing happens, the child window remains. I tried to use CloseWindow(hwnd) just to verify the child window can be modified and yes, CloseWindow(hwnd) minimizes the Child Window but I want to close it.
This is my Code:
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseWindow(IntPtr hWnd);
private static void CloseChildWindow();
{
IntPtr _hwnd = IntPtr.Zero;
if (WindowController.FindWindow(null, "System Alert") != IntPtr.Zero)
{
_hwnd = WindowController.FindWindow(null, "System Alert");
DestroyWindow(_hwnd); //Returns error code 5: Access Denied
CloseWindow(_hwnd); //Minimizes the window therefore _hwnd is correct
}
}
Any ideas on what is happening or a workaround would be appreciated, thanks in advance.
EDIT1: Destroywindow returns code error 5: Access Denied. Any ideas on a workaround?
The documentation for the win32 API DestroyWindow is pretty clear:
A thread cannot use DestroyWindow to destroy a window created by a
different thread.
You may be able to inject a DLL in the third party application, hooking the correct thread and then issuing the DestroyWindow call from here, but I will not recommand that, as it may have weird side effects (read: crash). Some applications don't react well when their windows are destroyed under their feet.
The proper way is probably to fake an user interaction, using regular Windows message (as WM_CLOSE), or SendInput, or UI Automation.
If the user has no way to close that window, you are out of luck.
Related
To explain my issue, I will try to screenshot this instead so that I can relay my message to everyone. Here on the picture, I have main project namely MainWinForm and and Solution Folder which contains two Windows Project namely FirstWinFormApp and SecondWinFormApp.
Here is the screenshot:
The MainWinForm project contains a Form1.cs which is an MDI Container set to true. I am trying to launch the form (First.cs) from FirstWinFormApp project using the button click event handler.
Everything should be working fine. I can launch the First.cs form inside the MDI Container of Form1.
Now here is the tricky part, I want to run the Program.cs of FirstWinFormApp instead the Program.cs of MainWinForm. The reason behind this is my requirement on my project (assemblies, connections, etc).
When I launch the application, the Program.cs of MainWinForm project is triggered. When I call the child app, its Program.cs is not called anymore. Is it possible that I can call the Program.cs of the child application (FirstWinFormApp) even though the Program.cs of the MainWinForm project was already called?
I suppose the MDI container and the child window would belong to different executables (different projects and all). This is not recommended in the first place, but it can be done. Basically you would need to mess with Windows API and not use Framework managed methods.
You would definitely need the SetParent API - which can put your window inside another even if the parent window is not a MDI container. You need some way to communicate the Handle property of the parent window, though, and the methods are either shaky (write the pointer down somewhere that both processes can access) or needlessly complicated (requiring inter-process communication).
The easiest, you can find the main window from the child by the title using FindWindow. This would fail if another program have the same title as your MDI window.
The code below is untested, but you would need it inside your child window's Program.cs:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(IntPtr className, string lpWindowName);
and in your child program's Main()
var hwnd = FindWindow(IntPtr.Zero, "My MDI Title");
SetParent(childWindow.Handle, hwnd);
In your case this would be something like
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(IntPtr className, string lpWindowName);
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var formObj = new First();
var hwnd = FindWindow(IntPtr.Zero, "My MDI Title");
SetParent(formObj.Handle, hwnd);
Application.Run(formObj);
}
Be sure to change the MDI form's title to "My MDI Title" as well. In the test app below I'm doing it in reverse - putting a Notepad window inside Form1, but you can do it any way you like.
Screenshot
We only want one instance of our app running at any one time. So on start up it looks to see if the app is running and if it is, it calls SetForegroundWindow on the Main Window.
This is all good and well ... for the most part..
When our app starts up it will show a Splash screen and a Logon form. Both of these forms have ShowInTaskBar = false.
Because of this, if you try to start up another copy of the app when the Logon form is showing, that Logon form is not brought to the front!
Especially as the user cant see anything in the taskbar as well, all they figure is that the app is duff and cannot start. There is no indication that there is another instance running.
Is there any way around this problem?
Well, code is here. Even if the ShowInTaskBar is false, you should be able to bring it to the front.
[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);
public static void bringToFront(string title) {
// Get a handle to the Calculator application.
IntPtr handle = FindWindow(null, title);
// Verify that Calculator is a running process.
if (handle == IntPtr.Zero) {
return;
}
// Make Calculator the foreground application
SetForegroundWindow(handle);
}
Note: you should FindWindow using the form's class and not by name as the splash screen forms sometimes do not have titles or even the controlbox. Use Spy++ to dig deeper.
Use FindWindow on splash. I think this is what you want to do - bring the splash screen in front while loading of the main form.
I think this is the better solution because its restores from minimized state:
public static class WindowHelper
{
public static void BringProcessToFront(Process process)
{
IntPtr handle = process.MainWindowHandle;
if (IsIconic(handle))
{
ShowWindow(handle, SW_RESTORE);
}
SetForegroundWindow(handle);
}
const int SW_RESTORE = 9;
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
}
Simple call:
WindowHelper.BringProcessToFront(process);
FindWindow(null, title);
Will find the first window that matches the query. This may lead to unexpected behavior if another window uses the same title.
Although the chances of this to happen may seem rare or impossible (single instance application) this can easily occur. The windows explorer for instance uses the name of the selected directory as window title (although invisible). Now if the window title is a common term or matches the name of the application directory this can be an issue.
Got curious when someone down-voted this code as a solution to running only a single instance of an application without stating why they did so.
int hWnd = FindWindow(null, "My Application Title");
if (hWnd > 0) //If found
{
Process.GetCurrentProcess().WaitForExit(600);
try
{
SetForegroundWindow(hWnd); //Activate it
ShowWindow(hWnd, 9);
Process.GetCurrentProcess().Kill();
}
catch (Exception ex)
{
//write to log
}
}
//Import the FindWindow API to find our window
[DllImport("User32.dll")]
public static extern int FindWindow(String ClassName, String WindowName);
//Import the SetForeground API to activate it
[DllImport("User32.dll")]
public static extern IntPtr SetForegroundWindow(int hWnd);
//Import the ShowWindow API to show it
[DllImport("User32.dll")]
public static extern bool ShowWindow(int hWnd, int nCmdShow);
Could someone be kind as to explain the drawbacks of this method to me? Thanks.
Because if the application is starting twice (accidental clicking), there is a small window of time where the test will fail. Both instances could be starting but neither has created a window yet.
The drawbacks as far as I can see are that it is overly complicated for what should be a simple solution. You do not need to crack into the windows api to force a single instance of an api. I would guess that is why you got downvoted.
If you follow the links in the Uwe's answer, you will see that you can remain in managed code, which should be your default unless there is some reason you MUST dig a little deeper.
I want my c# winform application to switch to another running instance if a certain event occurs.
For example if I have a application with just a button and three instances are running at the moment. Now if I
press the button in first instance, focus to second instance
press the button in second instance, focus to third instance
press the button in third instance, focus to first instance
How do i do that?
if you know the handle of the other instances you should just call the Windows API: SetForegroundWindow:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
you can use the FindWindow API call to get the handle of the other instances, for example:
public static int FindWindow(string windowName)
{
int hWnd = FindWindow(null, windowName);
return hWnd;
}
you can search for those api calls here in SO for more examples, for example found this one:
How do I focus a foreign window?
SetForegroundWindow is a great solution. An alternative is to use named Semaphores to send signals to other applications.
Lastly you could look for a Inter-Process Communication (IPC) solution which would allow you to send messages between processes.
I wrote a simple .Net XDMessaging library that makes this really easy. Using it you can send instructions from one application to other, and in the latest version even pass serilaized objects. It's a multicast implementation that uses a concept of channels.
App1:
IXDBroadcast broadcast = XDBroadcast.CreateBroadcast(
XDTransportMode.WindowsMessaging);
broadcast.SendToChannel("commands", "focus");
App2:
IXDListener listener = XDListener.CreateListener(
XDTransportMode.WindowsMessaging);
listener.MessageReceived+=XDMessageHandler(listener_MessageReceived);
listener.RegisterChannel("commands");
// process the message
private void listener_MessageReceived(object sender, XDMessageEventArgs e)
{
// e.DataGram.Message is the message
// e.DataGram.Channel is the channel name
switch(e.DataGram.Message)
{
case "focus":
// check requires invoke
this.focus();
break;
case "close"
this.close();
break;
}
}
I want to send a pressKey event to a certain application which is not the active application IN Windows so I have to use the sendMessage/postMessage api calls.
However, I need to know the exact child window that is active IN the application and send the pressKey message to it...
I was using GetTopWindow and GetWindow(GW_CHILD) api calls to get the top child window of the main window, and do it again with the obtained child window to get the top grandchildWindow, and keep doing it until I found a childwindow with no more childwindows. This works great for some applications but in some cases it doesn't. Sometimes the parent window is the active window, not one of its childwindows, so getting the parent's top child window will not work cause I will be sending a message to the wrong window.
The only way I found of doing this (getting the handler of the actual active window) was using the GuiThreadInfo api call but it only works if the target application is the active one IN Windows. As I mentioned in the beginning, it isn't so the handler comes null.
I can bring the application to the top using setForegroundWindow api call but I DON'T want to do this. I also tried the AttachThreadInput and GetFocus api calls, but again, they only work if the target application is the active application IN windows.
Any ideas? Thanks
I assume from the things that you have tried that you know how to get a handle to your main window, but if you don't just leave a comment and I will post a snippet for that.
I combined a few things that I found on the net to figure this out, but the main one is this one. I don't have a great app to test this with, but it works in a simple case. One exception is that I think if you use tool windows in your application it will not find that as it is coded because I think the GetLastActivePopup method doesn't include them (not sure about that, and didn't test that case).
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
static extern IntPtr GetAncestor(IntPtr hwnd, uint gaFlags);
const uint GA_PARENT = 1;
const uint GA_ROOT = 2;
const uint GA_ROOTOWNER = 3;
public static IntPtr GetAppActiveWindow(IntPtr hwnd)
{
IntPtr activeAppWindow = IntPtr.Zero;
if (hwnd != IntPtr.Zero)
{
//Get the root owner window (make sure we are at the app window
//if you already have a handle to the main window shouldn't have
//to do this but I put it in just in case
hwnd = GetAncestor(hwnd, GA_ROOTOWNER);
while ((activeAppWindow =
GetLastActivePopup(hwnd)) != activeAppWindow)
{
if (IsWindowVisible(activeAppWindow))
break;
hwnd = activeAppWindow;
}
}
return activeAppWindow;
}
If you know the Window title and the Window class name, take a look at FindWindow() and FindWindowEx() and see if those meet your needs.
FindWindow(): http://msdn.microsoft.com/en-us/library/ms633499.aspx
FindWindowEx(): http://msdn.microsoft.com/en-us/library/ms633500(VS.85).aspx