Find if process is responding without using System.Diagnostics.Process.Responding - c#

Good day everyone.
This problem was part of another one which it as been solved, i realized that what i thought it was the problem after all, it wasn't. Still thanks to that I've learned a couple things.
My application does loads of work with IE and from time to time, IE is redirected to a website with some bad Javascript code that ends up blocking IE interface. And consequently blocking my application too once everything on my application is running on the same Thread.
To counteract that problem, at startup my application runs a static method in another Thread that every 15 seconds does a simple check if IE is responding or not, and if IE isn't responding, he closes all its process's, liberating the lock on my application main Thread and then my application can resume its work.
To find if IE process's are responding i had a simple code like this:
bool terminate = false;
foreach (System.Diagnostics.Process exe in System.Diagnostics.Process.GetProcesses())
{
if (exe.ProcessName.StartsWith("iexplore"))
{
if (exe.Responding == false)
{
terminate = true;
break;
}
}
}
// Code to close all IE process's...
In order to the Process.Responding property finds if the process is responding, and according to information on MSDN, this property needs another property named MainWindowHandle to be available in order to complete the process of checking. And if MainWindowHandle isn't available Process.Responding always returns true even if the process isn't responding.
And for some reason which i don't know. In Windows XP MainWindowHandle isn't available there so Responding isn't accurate.
Thats why i need to know another way to find if a specific process is responding or not in Windows XP.
Any help is appreciated, thanks.
PS: If your looking for a website to freeze IE here goes: http://aboutmycollege.com/
EDIT: Following 0xA3 suggestion:
I went through all IE process's checking if they had the MainWindowHandle property, those who had that property i send they Responding property to a MessageBox and they report correctly when IE isn't responding on Windows 7 but not on XP.
I executed this code every 15 seconds:
foreach (System.Diagnostics.Process exe in System.Diagnostics.Process.GetProcesses())
{
if (exe.ProcessName.StartsWith("iexplore"))
{
if (exe.MainWindowHandle == IntPtr.Zero)
{
System.Windows.Forms.MessageBox.Show("Process doesn't have MainWindowHandle");
}
else
{
System.Windows.Forms.MessageBox.Show("Process Responding: " + exe.Responding.ToString());
}
}
}
In Windows 7 and Xp he reports the Process's of IE that don't have the MainWindowHandle property, and in Windows 7 he also reports correctly when IE isn't responding. But in XP all IE process's with MainWindowHandle are always responding even when they aren't.

IE is special because each tab has its own process plus there is an additional parent IE process. In fact, only the parent process will have a valid MainWindowHandle.
Did you check whether MainWindowHandle is null for all these processes? If it isn't I think your code should work as expected on XP as well.
Update
Since checking all IE instances didn't help, the next thing I would try is to modify the timeout that is used by Process.Responding. The property internally calls the SendMessageTimeout api function and then checks the return value whether a timeout occurred. If so, the process is assumed to be hanging. The timeout is a hard-coded value of 5 seconds.
You can call SendMessageTimeout yourself using P/Invoke and vary the timeout. Possibly a shorter value would give better results on Windows XP:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern IntPtr SendMessageTimeout(
HandleRef hWnd,
int msg,
IntPtr wParam,
IntPtr lParam,
int flags,
int timeout,
out IntPtr pdwResult);
const int SMTO_ABORTIFHUNG = 2;
bool IsResponding(Process process)
{
HandeRef handleRef = new HandleRef(process, process.MainWindowHandle);
int timeout = 2000;
IntPtr lpdwResult;
IntPtr lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
timeout,
out lpdwResult);
return lResult != IntPtr.Zero;
}

Related

Is it possible to close a process without forcing, killing it? [duplicate]

I have an application that I would like to close from my current C# application. The problem is that the application I want to close has an exit confirmation that requires the user to confirm the closing of the application.
When I use this code:
foreach (Process process in runningProcesses)
{
if (process.ProcessName == "ProcessName")
process.CloseMainWindow();
}
the exit confirmation popup still appears on the other application.
When questions similar to this are asked elsewhere, all I can find are people suggesting process.Kill() to get past the exit confirmation. This is not an option for me as I need the other application to close down gently.
Is there a way to send a closing message to the other application that will force it to start its shutdown process without killing the process abruptly?
If you have to close the application gently and it displays a confirmation when trying to close it, then you'll have to handle it as well.
The actual way to do that depends on what exactly the popup is. If it's a standard dialog, something like the following could suffice:
SendMessage(hDlg, WM_COMMAND, IDOK, 0);
If it's a less standard dialog, but still using standard Windows components (like MFC or WinForms or something), you'll have to inspect its window structure (using Spy++ for example), get the handle of the button you need to press and use something like:
SendMessage(hBtn, BM_CLICK, 0, 0);
If however the dialog doesn't use standard windows (like Qt or WPF), you'll need a lot more specialized code. I'd suggest hooking into the parent dialog and pressing the button yourself to see what events are triggered (Spy++ can do that) and mimicking them with SendMessage.
You can send a WM_ENDSESSION message, which in most applications cause all forms to gracefully close.
PostMessage(process.MainWindowHandle, WM_ENDSESSION, IntPtr.Zero, new IntPtr(ENDSESSION_CLOSEAPP));
Definitions:
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_ENDSESSION = 0x16;
const int ENDSESSION_CLOSEAPP = 0x1;

GetGuiResources returns 0 (error 87) or nonsense value

I'm currently coding a windows service (installed as LocalSystem) that monitors several things on a pc/server including processes. For the processes, I'm watching the memory usage and also "try" to get the number of GDI Objects per process (as can be seen in task manager).
Sadly, C# Process objects don't have the gdi count built-in so I'm using the GetGuiResources method from 'user32.dll' as shown in this example:
https://www.pinvoke.net/default.aspx/user32.getguiresources.
Basically I have a list of executable names, for each of them I use GetProcessesByName to retrieve all process instances, and then for each unique process I take the handle and send it to the function to get the Gdi objects count back.
When I try this on my local machine as a simple console app (feeding a name through Console.ReadLine), it works no problem as long as the console app is launched as administrator; i get the same numbers as task manager.
However, when the monitoring service calls this function, I get either 0s (returning error code 87) or worse: processes tied to services (no gui) return me some random numbers (12, 7, 4, etc.) when the task manager actually shows 0 (and last error = 0).
So in summary, every process that shows some GID objects in Task Manager returns 0 (error 87), and each process who has 0 returns me a number (no error, or error 183 for the monitoring service itself).
I've tried this on Windows 10, Windows Server 2012, Windows Server 2008, Windows Server 2003, Windows Server 2016. On windows 10 (my machine) I get 0s everywhere, on other OS I get the mentionned results.
Here's a shortened version of the code I use:
// Monitoring processes exeName example: ssms, sqlbrowser
List<Process> result = Process.GetProcessesByName(exeName).ToList();
if (processes != null)
{
for (int i = 0; i < processes.Count; i++)
{
int gdiCount = processes[i].GetGDIObjectsCount(); // extension method
// logging and doing stuff with gdi count here (but i get 0s or random numbers as I told)
}
}
// Process extension method
public static class CProcessExtensions
{
[DllImport("User32", SetLastError = true)]
extern private static int GetGuiResources(IntPtr hProcess, int uiFlags);
private static int GetGDICount(IntPtr processHandle)
{
if (processHandle == IntPtr.Zero)
{
return -1;
}
int count = GetGuiResources(processHandle, 0);
// Logging Marshal.GetLastWin32Error() here
return count;
}
public static int GetGDIObjectsCount(this Process process)
{
IntPtr handle;
process.Refresh();
try
{
handle = process.Handle;
}
catch (Exception ex)
{
handle = IntPtr.Zero;
}
return GetGDICount(handle);
}
}
I've also tried getting the process handles with the OpenProcess dll method but had the same results.
Anyone faced this kind of problem before?
So, thanks to Jeremy Thompson's comment leading me to info about the session 0, and with further research, I was able to solve my problem.
References:
Application Loader to launch process in another session
Wait for process exit (ProcessWaitHandle)
Get Exit code
What I did is modify the sample code from the first reference to provide a process ID (the one I want the GDI objects count of) and launch my little console app (which takes the same process ID also, and returns the GDI count as exit code) in the same session by duplicating the token of the provided process and call CreateProcessAsUser.
By launching the console app in the same session I was able to retrieve the correct info on GDI objects on every OS I previously tested except Win Server 2003, which I can totally live without.

SendMessage disappears into the ether

I'm attempting to pass messages between two applications - one of them is a plugin, and the other is a standalone configuration utility. When my plugin detects an event, I want to send a message to my utility and prompt the user to reconfigure.
The code I'm using is as follows:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int MESSAGE_UNAUTH = 0x401;
[... misc logic here, function def, etc]
Process[] processes = Process.GetProcessesByName("MyConfigurationApplication");
if (processes.Length > 0)
{
foreach (Process p in processes)
{
SendMessage(p.MainWindowHandle, MESSAGE_UNAUTH, IntPtr.Zero, IntPtr.Zero);
}
}
And then in my receiving process, I have the following code (I also defined MESSAGE_UNAUTH in this class):
protected override void WndProc(ref Message message)
{
if (message.Msg == MESSAGE_UNAUTH)
{
MessageBox.Show("Message received");
}
base.WndProc(ref message);
}
Things I have already verified with the debugger:
The message is getting sent. All the code in the Sender, including the SendMessage call, is executing.
The message is not getting received (obviously).
The WndProc() function is not getting called at all when the message is sent. It is, however, getting called a whole bunch of times when the configuration utility is launched (I'm assuming this is Windows' behavior).
I've gone through enough online tutorials to need eyedrops, and as far as I know, everything here is syntax-correct and "proper," but for some reason, between when I send the message and when the receiver's WndProc() should be called, black magic is happening.
Any ideas would be greatly appreciated.
Update: Using Marshal.GetLastWin32Error(), I am getting an error #1400, which appears to correspond to an invalid window handle. However, I don't see why that would be the case, since the process was found and we entered the for each loop successfully. The one caveat I can think of is that my configuration utility is presented as a taskbar icon, and doesn't necessarily have a visible window at all times - would this prevent p.MainWindowHandle from being valid? If so, how can I work around this to pass a message to the process instead of the window?
Update: Process.MainWindowHandle is 0, so it looks like that is indeed the problem - when the form in my configuration utility is not visible, no valid window handler is returned, even though my utility icon is visible in the notification bar. Is there any way I can send a message to the process, or even to the taskbar icon?
You can try to enumerate all windows associated with the process. See How to enumerate all windows belonging to a particular process using .NET?
Depending on the .NET framework you are using, this will help resolve your issues.
There was a bug in the old .NET frameworks (2.0 I think) where calling to Process.MainWindowHandle when the process starts up returns 0. Any subsequent call will also result in a 0. This is due to caching the main window handle, which should have been fixed in .NET 3.0 and later.
You might also try giving full trust to your WndProc which might help. Something like:
[System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
//...
}
On a side note, if you can change your implementation then I strongly suggest you go for better inter process communication means such as sockets, TCPChannel (which I think is replaced by WCF), named pipes...
The message might not be sent, it might be blocked. See here: When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).
Use Windows Registermessage in bothe sender and receiver end will resolve the problem
Problem was that the process I was sending the message to only existed as a tooltip icon and not as an active, open window. Turns out the windows message functionality is designed for window-to-window messages, not process-to-process messages.
Solution was aforementioned kludgy system of file IO handlers.

Why can't get the main window handle for a started process?

I have a situation where I'm starting a process in my code in order to set up an IPC channel. The process I'm starting is an MFC application with no CLR support. The application from which I am starting this process is a C# module in a WPF application (thought I don't think that that is consequential to my problem). This works with a version of the application that does support CLR, and it works on every computer except the deployment target, a touch screen computer with Windows 7. But for some reason, when I try it with this exact scenario, the Process object never resolves a main window handle (Process.MainWindowHandle). Is there another (perhaps even pinvoke) method of doing this? Is this a security thing? I'm the one staring the process. The process's main window handle does exist. I don't see what could be wrong.
If it helps, here is my code.
_applicationProcess = new Process();
_applicationProcess.StartInfo.FileName = _strProcessPath;
_applicationProcess.StartInfo.Arguments = _strProcessArguments;
_applicationProcess.Start();
long nTicks = Environment.TickCount;
if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
{
try
{
do
{
// Don't let total processing take more than 1 minute(s).
if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");
_applicationProcess.Refresh();
}
while (_applicationProcess.MainWindowHandle.ToInt32() == 0);
_applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
else
{
// Do exception handling.
}
The ApplicationException is hit after a minute of trying to get a main window handle other than zero.
The value you get out of Process.MainWindowHandle is unfortunately a guess. There is no API function available to a program that lets it tell Windows "this is my main window". The rule it uses is documented, it is the first window that's created by a process when it gets started. That causes trouble if that first window is, say, a login window or a splash screen.
Not much you can do about this, you have to know more about how the program behaves to find that real main window. Enumerating windows with EnumThreadWindows() could help you find it, as long as the first window was created on the same thread as the main window. A more elaborate EnumWindows() will be necessary if that is not the case.
My habit is to call EnumWindows in a loop combined with GetWindowThreadProcessId to find the window handle.
C Code, adapt to your language
DWORD TargetHWND;
//...
while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
Sleep(100);
}
//...
BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
DWORD pid = (DWORD)-1;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == (DWORD)lParam) {
TargetHWND = hWnd;
return FALSE;
}
return TRUE;
}
In order to get MainWindowHandle by means of your process, please make sure your WPF application is shown on the taskbar i.e ShowInTaskbar="True" and set Application.Current.MainWindow property to the window that you'd like to set as your main window.
If I execute code below in my WPF main window without setting ShowInTaskbar="True" I always got 0 as the MainWindowHandle because my WPF window was full screen and not displayed on the taskbar.
Application.Current.MainWindow = this;
var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");
if (Query.Any())
{
Query.FirstOrDefault().Refresh();
MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
}
I don't know why it could be different, but after you create the process, try doing:
Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");
and see if any of the processes returned have a MainWindowHandle.

Determine which application is preventing shutdown

Unlike Windows Vista, when Windows XP is shutting down - it doesn't tell you which program is requesting not to be shutdown. This leaves Windows still running and not telling you what program is preventing shutdown.
As I understand it, Windows sends WM_QUERYENDSESSION to all applications. If any of the applications return 0 to the function, shutdown is halted.
I am wondering is there a method to capture the return values from programs and determine why Windows XP is not shutting down.
Thanks
Phil
Enumerate all top-level windows (use EnumWindows()) and send WM_QUERYENDSESSION to each. For the window that returns a preventing value detect which process it is. To do the latter call GetWindowThreadProcessId(), enumerate all processes and find the process with that id.
Yes. I posted code in this question, but this is an improved version of the code:
void CQes_testDlg::OnBtnTest()
{
// enumerate all the top-level windows.
m_ctrl_ListMsgs.ResetContent();
EnumWindows (EnumProc, 0);
}
BOOL CALLBACK EnumProc (HWND hTarget, LPARAM lParam)
{
CString csTitle;
CString csMsg;
CWnd * pWnd = CWnd::FromHandle (hTarget);
BOOL bRetVal = TRUE;
DWORD dwPID;
if (pWnd)
pWnd->GetWindowText (csTitle);
else
csTitle = TEXT("<unknown>");
GetWindowThreadProcessId (hTarget, &dwPID);
if (pWnd->SendMessage (WM_QUERYENDSESSION, 0, ENDSESSION_LOGOFF))
{
csMsg.Format ("window 0x%X (PID=%d, Title='%s') returned TRUE",
hTarget, dwPID, csTitle);
}
else
{
csMsg.Format ("window 0x%X (PID=%d, Title='%s') returned FALSE *******",
hTarget, dwPID, csTitle);
bRetVal = FALSE;
}
mg_pThis->m_ctrl_ListMsgs.AddString (csMsg);
return bRetVal;
}
mg_pThis is a module-global pointer to the dialog object, so the enumerator can get access to it's control.

Categories

Resources