user32 GetClassName isn't correct - c#

I have a routine that get's all open windows (processes) and then searches for it's classname with the GetClassName method in user32. But when for example Teamviewer is on the classnames of all applications get the teamviewer classname.
Example: Notepad is open and TeamViewer on classname: 'TeamViewer_TitleBarButtonClass'
Notepad is open and TeamViewer off classname: 'Notepad'
I looked how this came and found out that Teamviewer puts a control on top of some application windows.
So how can i find the real classname of the applications and not from Teamviewer?
Process[] processes = Process.GetProcesses();
StringBuilder className = new StringBuilder(100);
For (int i = 0; i < processes.Length; i++)
{
if (processes[i].MainWindowHandle != IntPtr.Zero)
{
list.Add(processes[i]);
GetClassName(processes[i].MainWindowHandle, className, className.Capacity);
}
}

The heuristic that the Process class uses to guess which window is the "main" window is not perfect. There isn't any way for an app to mark the windows it creates as "this is the main one". So it punts at the best guess: the first window. This certainly can go wrong, you may find a hidden login window for example.
An alternative is to enumerate the threads in the process from Process.Threads, then for each thread to enumerate the windows it owns with EnumThreadWindows(), calling GetClassName() on each. You'll get to see all of the windows that way and should run across the one you are looking for. Using EnumWindows() is an alternative when can't be selective about the process. That also avoids the crash your current code suffers from when it happens to enumerate the "System" process too early.
The best to deal with intrusive software like this "TeamViewer" is to just uninstall it.

Related

ShowWindowAsync not work in all case

ShowWindowAsync won't work in all case. It works when I try it with notepad, task managger, or visual studio, it's just do the job, restore them when they minimized, but when I try it with windows explorer or another external process, it won't work. In all case, the GetProcessesByName find them, just won't come back from minimized.
PInvokeFunctions class
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(HandleRef hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr WindowHandle);
Method
public static void FocusProcess(string procName)
{
Process[] objProcesses = Process.GetProcessesByName(procName);
if (objProcesses.Length > 0)
{
IntPtr hWnd = IntPtr.Zero;
hWnd = objProcesses[0].MainWindowHandle;
PInvokeFunctions.ShowWindowAsync(new HandleRef(null, hWnd), Constants.PInvokeConstants.SW_RESTORE); // SW_RESTORE = 9
PInvokeFunctions.SetForegroundWindow(hWnd);
}
}
What you want is actually not possible. You are trying to find the 'main window' of a process and that is an non-existing concept. The topic is discussed at length here: There can be more than one (or zero): Converting a process to a window
When you try to explain this to people, you sometimes get stuck in the Why won't you answer my question? cycle.
"I have a thread ID. How do I get the corresponding window?"
You can use the EnumThreadWindows function to get all the windows on the thread.
"Yes, I know about EnumThreadWindows, but how do I get the window that I want?"
Well, you haven't said what you wanted yet.
"I want the window that corresponds to the thread."
But which one? How will you decide among all the windows?
"That's what I'm asking you!"
But you haven't yet described what you want.
"I want the window corresponding to the thread. Why won't you answer my question?"
Note that saying, "I am looking for the top-level unowned window" is a step forward, but it still doesn't uniquely identify a window. There can be multiple top-level unowned windows in a process. For example, Explorer typically has lots of top-level unowned windows. There's the desktop, the taskbar, your open folder windows, and property sheets. If you ask for "the" top-level unowned window of Explorer, which one do you want?
And then Raymond ads the coup de grâce:
Perhaps people are getting the idea that there is a way to uniquely specify "the" window for a process because the System.Diagnostics.Process object has a property called MainWindowHandle. The documentation for that property doesn't do anything to dispel the notion, either. I have no idea how that property decides among multiple top-level unowned windows.
So, as you see, you need to unask the question. What you want is fundamentally incorrect, since there is no 'main' window for a process like Chrome (remember, all modern browsers isolate browsing tabs in their own process). If you followed the discussion above, you'll know what to do.

C# Find and handle child processes of a process

i need help finding the child processes (they are 2) of a process and handle them in order to set each of them foreground when needed.
The main process is Java Platform SE binary whose indicative name is javaw.
Using this code:
Process[] arrProcesses = Process.GetProcessesByName("javaw");
I can get the main process, so with:
IntPtr ipHwnd = arrProcesses[0].MainWindowHandle;
SetForegroundWindow(ipHwnd);
I can set it foreground.
My problem is that the handled process is the "last used" process...
Explaning better: on application bar of Windows I have 2 application(A, B) using java, if i click on A, when i use the code above, the application A gets foreground, insted, if i click on application B, with that code the B gets foreground.
What i want to do is decide which of two set foreground.
Anyone can help me?

How to close Internet Explorer when it has been ran with Process.Start?

I'm have a WPF application that is starting Internet explorer (Version 9, on Win7 X64) using Process.Start method.
I save the ProcessID in order to close it when the application is closed. However, when the application exits, the Internet Explorer is still visible in the task manager.
Here is the code I'm using :
public class MyClass : IDisposable
{
Process _process;
public void Go()
{
ProcessStartInfo psi = new ProcessStartInfo {
FileName = "iexplore.exe",
Arguments = "-noframemerging -private \"http://whathaveyoutried.com\""
};
_process = Process.Start(psi);
_process.WaitForInputIdle();
}
private void Close(object sender, RoutedEventArgs e)
{
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
public void Dispose()
{
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
}
}
I double checked, the call to _process.Close() is actually done.
What can cause this behavior?
PS: I'm suspecting this is due to Internet Explorer internal. Running the exe won't necessary create a new process, but can use some IPC to control other instances. I use the -noframemerging command line argument, but it does not look to solve the issue.
[Edit] This is the continuation of another question I asked few days ago. Actually, I'm Pinvoking SetParent function to embbed the spawned IE in my application. I can't use the WebBrowser control because it does not support the isolation I'm looking for. So it's OK to close IE when my app closes.
Every tab of Internet Explorer is a process. If you open IE with multiple tabs or user opens another tabs, it won't be enough to kill process.
But
_process.Close();
Frees all resources belongs to _process. You can use _process.Kill() method instead of it.
For better security and stability, IE8 uses the Loosely Coupled Internet Explorer (LCIE) architecture and runs the browser frame and tabs in separate processes. LCIE prevents glitches and hangs from bringing down the entire browser and leads to higher performance and scalability. I read this on Wikipedia.
You can disable LCIE, but I am not sure why you would want to do that. I would consider using the a solution that #Damien_The_Unbeliever mention above.
I have an IE application in my win32 Application and i'm using Win32Api - SendMessage with WM_CLOSE = 0x0010
SendMessage(handle,0x0010,IntPtr.Zero, IntPtr.Zero)}
This closes the IE, how ever you need to have the specific handle of the Browser (Class IEFrame).
There is one drawback, in case you have more then one tab, the IE opens confirm close dialog which prevent the close process.
One way to overcome the dialog opening is to set it check box to (always close), but for me itţs not an option.

How can I run another application within a panel of my C# program?

I've been reading lots on how to trigger an application from inside a C# program (Process.Start()), but I haven t been able to find any information on how to have this new application run within a panel of my C# program. For example, I'd like a button click to open a notepad.exe WITHIN my application, not externally.
Using the win32 API it is possible to "eat" another application. Basically you get the top window for that application and set it's parent to be the handle of the panel you want to place it in. If you don't want the MDI style effect you also have to adjust the window style to make it maximised and remove the title bar.
Here is some simple sample code where I have a form with a button and a panel:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start("notepad.exe");
Thread.Sleep(500); // Allow the process to open it's window
SetParent(p.MainWindowHandle, panel1.Handle);
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
}
}
I just saw another example where they called WaitForInputIdle instead of sleeping. So the code would be like this:
Process p = Process.Start("notepad.exe");
p.WaitForInputIdle();
SetParent(p.MainWindowHandle, panel1.Handle);
The Code Project has a good article one the whole process: Hosting EXE Applications in a WinForm project
I don't know if this is still the recommended thing to use but the "Object Linking and Embedding" framework allows you to embed certain objects/controls directly into your application. This will probably only work for certain applications, I'm not sure if Notepad is one of them. For really simple things like notepad, you'll probably have an easier time just working with the text box controls provided by whatever medium you're using (e.g. WinForms).
Here's a link to OLE info to get started:
http://en.wikipedia.org/wiki/Object_Linking_and_Embedding
Another interesting solution to luch an exeternal application with a WinForm container is the follow:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void Form1_Load(object sender, EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
psi.WindowStyle = ProcessWindowStyle.Minimized;
Process p = Process.Start(psi);
Thread.Sleep(500);
SetParent(p.MainWindowHandle, panel1.Handle);
CenterToScreen();
psi.WindowStyle = ProcessWindowStyle.Normal;
}
The step to ProcessWindowStyle.Minimized from ProcessWindowStyle.Normal remove the annoying delay.
Adding some solution in Answer..**
This code has helped me to dock some executable in windows form. like NotePad, Excel, word, Acrobat reader n many more...
But it wont work for some applications. As sometimes when you start process of some application.... wait for idle time... and the try to get its mainWindowHandle.... till the time the main window handle becomes null.....
so I have done one trick to solve this
If you get main window handle as null... then search all the runnning processes on sytem and find you process ... then get the main hadle of the process and the set panel as its parent.
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "xxxxxxxxxxxx.exe";
info.Arguments = "yyyyyyyyyy";
info.UseShellExecute = true;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Maximized;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = false;
info.RedirectStandardError = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForInputIdle();
Thread.Sleep(3000);
Process[] p1 ;
if(p.MainWindowHandle == null)
{
List<String> arrString = new List<String>();
foreach (Process p1 in Process.GetProcesses())
{
// Console.WriteLine(p1.MainWindowHandle);
arrString.Add(Convert.ToString(p1.ProcessName));
}
p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
//p.WaitForInputIdle();
Thread.Sleep(5000);
SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
}
else
{
SetParent(p.MainWindowHandle, this.panel2.Handle);
}
I notice that all the prior answers use older Win32 User library functions to accomplish this. I think this will work in most cases, but will work less reliably over time.
Now, not having done this, I can't tell you how well it will work, but I do know that a current Windows technology might be a better solution: the Desktop Windows Manager API.
DWM is the same technology that lets you see live thumbnail previews of apps using the taskbar and task switcher UI. I believe it is closely related to Remote Terminal services.
I think that a probable problem that might happen when you force an app to be a child of a parent window that is not the desktop window is that some application developers will make assumptions about the device context (DC), pointer (mouse) position, screen widths, etc., which may cause erratic or problematic behavior when it is "embedded" in the main window.
I suspect that you can largely eliminate these problems by relying on DWM to help you manage the translations necessary to have an application's windows reliably be presented and interacted with inside another application's container window.
The documentation assumes C++ programming, but I found one person who has produced what he claims is an open source C# wrapper library: https://bytes.com/topic/c-sharp/answers/823547-desktop-window-manager-wrapper. The post is old, and the source is not on a big repository like GitHub, bitbucket, or sourceforge, so I don't know how current it is.
If you want to run notepad inside your app you would probably be better of with a text editor component. There's obviously a basic text box that comes with WinForms, but I suspect more advanced components that offer Notepad functionality (or better) can be found on the interweb.
I know this is possible if the other application can attach itself to a win32 window handle. For example, we have a separate C# application that hosts a DirectX application inside one of its windows. I'm not familiar with the exact details of how this is implemented, but I think just passing the win32 Handle of your panel to the other application is enough for that application to attach its DirectX surface.
Short Answer:
No
Shortish Answer:
Only if the other application is designed to allow it, by providing components for you to add into your own application.

.NET (C#): Getting child windows when you only have a process handle or PID?

Kind of a special case problem:
I start a process with System.Diagnostics.Process.Start(..)
The process opens a splash screen -- this splash screen becomes the main window.
The splash screen closes and the 'real' UI is shown. The main window (splash screen) is now invalid.
I still have the Process object, and I can query its handle, module, etc. But the main window handle is now invalid.
I need to get the process's UI (or UI handle) at this point. Assume I cannot change the behavior of the process to make this any easier (or saner).
I have looked around online but I'll admit I didn't look for more than an hour. Seemed like it should be somewhat trivial :-(
If you don't mind using the Windows API, you could use EnumWindowsProc, and check each of the handles that that turns up using GetWindowThreadProcessId (to see that it's in your process), and then maybe IsWindowVisible, GetWindowCaption and GetWindowTextLength to determine which hWnd in your process is the one you want.
Though if you haven't used those functions before that approach will be a real pain, so hopefully there's a simpler way.
#ageektrapped is on the right track, however FindWindow will not search child windows.
For that you will need to use FindWindowEx
Thank you for your answers. Thanks to you here, I figured out how to know if the main window of a process is in front or not:
N.B : of course this needs System.Diagnostic and System.Runtime.Interrop
public bool IsWindowActive(Int32 PID)
{
return IsWindowActive(Process.GetProcessById(PID));
}
[DllImport("user32.dll")]
private static extern
IntPtr GetForegroundWindow();
public bool IsWindowActive(Process proc)
{
proc.Refresh();
return proc.MainWindowHandle.Equals(GetForegroundWindow());
}
You may find that if you call .Refresh() that you get the new top-level window.
If you know the window's title, you can use the Win32 call, FindWindow, through P/Invoke.
You can find the signature here on pinvoke.net
From what I understand MainWindowHandle property of the process you are starting is not valid. If that's the case, you can use FindWindow function (from Win32 SDK) which returns the window handle you need. All you need is the class name of target application's main window. You can obtain it using Spy++ or Winspector. You also need to ensure you have the right window by checking that window's process id using GetWindowThreadProcessId.
At last, I have to say I am not an expert on Win32 and there might be a better solution for your case.
Use Process.GetProcessById(proc.Id); where proc was your splash screen.
Works for me.
Now, how do you get to main window properties in System.Windows.Forms to give it focus w/o using win32?
After all .net is supposed to be a one-stop solution - is it not?
Somewhere in the code, the "real" main window is created. You can just save the window handle at that time and then after the splash screen closes you can set Application.MainWindow to the real window.
The MainWindowHandle property is cached after it is first accessed which is why you don't see it changing even after the handle becomes invalid. GregUzelac's information is correct. Calling Proces.Refresh will causes the next call to Process.MainWindowHandle to re-do the logic to find a new main window handle. Michael's logic also works because the new Process doesn't have a cached version of the MainWindowHandle.

Categories

Resources