How can I tell if a process has a graphical interface?
I'm using automation to test an application, but sometimes I want to start the application via a batch file. When I run "process.WaitForInputIdle(100)" I get an error:
"WaitForInputId开发者_运维问答le failed. This could be because the process does not have a graphical interface."
How can I tell if the process has a graphical interface or not?
See Environment.UserInteractive. That will identify whether the process has an interface at all, e.g. services are not user interactive.
You could also look at Process.MainWindowHandle which will tell you whether there is a graphical interface.
A combination of these two checks should cover all the possibilities.
You can simply try and catch the exception:
Process process = ...
try
{
process.WaitForInputIdle(100);
}
catch (InvalidOperationException ex)
{
// no graphical interface
}
I was think along the lines of this, Still ugly but trys to avoid exceptions.
Process process = ...
bool hasUI = false;
if (!process.HasExited)
{
try
{
hasUI = process.MainWindowHandle != IntPtr.Zero;
}
catch (InvalidOperationException)
{
if (!process.HasExited)
throw;
}
}
if (!process.HasExited && hasUI)
{
try
{
process.WaitForInputIdle(100);
}
catch (InvalidOperationException)
{
if (!process.HasExited)
throw;
}
}
As well as a MainWindowHandle
check, one can enumerate the process threads and check if any of them reference a visible window via P/Invokes. This seems to do a good job catching any windows that the first check misses.
private Boolean isProcessWindowed(Process externalProcess)
{
if (externalProcess.MainWindowHandle != IntPtr.Zero)
{
return true;
}
foreach (ProcessThread threadInfo in externalProcess.Threads)
{
IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id);
if (windows != null)
{
foreach (IntPtr handle in windows)
{
if (IsWindowVisible(handle))
{
return true;
}
}
}
}
return false;
}
private IntPtr[] GetWindowHandlesForThread(int threadHandle)
{
results.Clear();
EnumWindows(WindowEnum, threadHandle);
return results.ToArray();
}
private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
private List<IntPtr> results = new List<IntPtr>();
private int WindowEnum(IntPtr hWnd, int lParam)
{
int processID = 0;
int threadID = GetWindowThreadProcessId(hWnd, out processID);
if (threadID == lParam)
{
results.Add(hWnd);
}
return 1;
}
[DllImport("user32.Dll")]
private static extern int EnumWindows(EnumWindowsProc x, int y);
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
[DllImport("user32.dll")]
static extern bool IsWindowVisible(IntPtr hWnd);
Besides the Process.MainWindowHandle, you can improve your solution by reading the PE header of the process main module to detect the process module subsystem. If the subsystem is IMAGE_SUBSYSTEM_WINDOWS_GUI, then the process can have a graphical interface.
Use this class to read the PE header of the process main module: https://gist.github.com/ahmedosama007/bfdb8198fe6690d17e7c3db398f6d725
Use the following code to detect the process module subsystem:
Dim peReader = New PEHeaderReader("C:\Windows\notepad.exe")
Dim subsystem As PEHeaderReader.ImageSubSystem
If peReader.Is32BitHeader Then '32-bit
subsystem = peReader.OptionalHeader32.Subsystem
Else '64-bit
subsystem = peReader.OptionalHeader64.Subsystem
End If
'https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32
If subsystem = PEHeaderReader.ImageSubSystem.IMAGE_SUBSYSTEM_WINDOWS_GUI Then
Console.WriteLine("GUI")
ElseIf subsystem = PEHeaderReader.ImageSubSystem.IMAGE_SUBSYSTEM_WINDOWS_CUI Then
Console.WriteLine("Console")
Else
Console.WriteLine("Other Subsystem")
End If
Console.ReadLine()
精彩评论