Инструмент командной строки захвата экрана работает из командной строки, но при запуске изнутри пользовательского интерфейса
Быстрая версия: в моем проекте у меня есть инструмент командной строки, который делает скриншоты всех окон на компьютере (инструмент сбора ошибок для тестирования нашего продукта). Это часть командной строки CollectSystemLogs.exe
который собирает много вещей, скриншоты просто как один элемент.
У меня маленький интерфейс (CollectUSLogs.exe
что тестировщик / пользователь выбирает, какие элементы он хочет выбрать. Это просто интерфейс пользователя, с CollectSystemLogs.exe
командная строка делает всю реальную работу (с параметрами, чтобы сказать, что собирать).
Я могу запустить командную строку CollectSystemLogs.exe
из командной строки и все скриншоты собраны просто отлично.
Однако, когда я запускаю инструмент пользовательского интерфейса CollectUSLogs.exe и выбираю скриншоты, он захватывает только несколько, а затем кажется зависшим или что-то в этом роде. Это останавливается, и я не могу понять, почему. К сожалению, потому что это процесс, запускаемый пользовательским интерфейсом, я не могу отладить его, и если я запускаю только командную строку, он работает.
Соответствующий код:
Это код для сбора скриншотов (пожалуйста, не обращайте внимания на все подробные записи в журналы..... выполняйте отладку старой школы printf).
/// <summary>Gets images of each window on the screen.</summary>
public static void CaptureWindows(string savePath)
{
LogManager.LogDebugMessage($"Starting CaptureWindows({savePath})");
AutomationElementCollection desktopChildren = AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition);
int windowCount = 1;
LogManager.LogDebugMessage($"{desktopChildren.Count} desktopChildren (windows) found.");
foreach (AutomationElement window in desktopChildren)
{
LogManager.LogDebugMessageConsole($"Capturing window [{window.Current.Name}]");
Rect rect = window.Current.BoundingRectangle;
if (Double.IsInfinity(rect.Width) || Double.IsInfinity(rect.Height))
{
LogManager.LogErrorMessageConsole($"[{window.Current.Name}] has at leat one infinite dimension.");
LogManager.LogErrorMessageConsole($"w: {rect.Width}, h: {rect.Height}");
}
try
{
// TODO: Get rid of unneeded debug log prints
LogManager.LogDebugMessage("In try{}");
using (var bitmap = new Bitmap((int)rect.Width, (int)rect.Height))
{
LogManager.LogDebugMessage($"Bitmap Created {(int)rect.Width}x{(int)rect.Height}");
using (Graphics graphic = Graphics.FromImage(bitmap))
{
LogManager.LogDebugMessage($"Graphics created {graphic.ToString()}");
IntPtr handleDeviceContext = graphic.GetHdc();
var hwnd = (IntPtr)window.Current.NativeWindowHandle;
LogManager.LogDebugMessage($"hwnd created {hwnd.ToString()}");
if (hwnd == IntPtr.Zero) break;
NativeMethods.PrintWindow(hwnd, handleDeviceContext, 0);
LogManager.LogDebugMessage("PrintWindow() complete.");
graphic.ReleaseHdc(handleDeviceContext);
}
// Create File Name for image to be saved
string fileName = Path.Combine(savePath, windowCount++.ToString("Window00") + ".png");
LogManager.LogDebugMessage($"Saving {fileName}");
bitmap.Save(fileName, ImageFormat.Png);
LogManager.LogDebugMessage($"{fileName} saved");
}
LogManager.LogDebugMessage("End of try{}");
}
catch (Exception e)
{
LogManager.LogExceptionMessageConsole(e);
}
LogManager.LogDebugMessage("End of foreach");
}
LogManager.LogDebugMessage("Exiting CaptureWindows()");
}
Я вызываю командную строку с этим:
if (!ProcessHelpers.RunCommandProcessCollectOutput(command, args, out output))
{
MessageBox.Show($"Error running command line tool to collect system logs. Please save {Path.Combine(LogManagerConstants.LogFilePath,LogManagerConstants.LogFileBasename)} for analysis.",
@"CollectSystemLogs.exe Execution Error", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
И этот код здесь:
public static bool RunCommandProcessCollectOutput(string command, string args, out string output)
{
// cmd arg /c => run shell and then exit
// cmd arg /d => disables running of autorun commands from reg
// (may inject extra text into output that could affect parsing)
string localArgs = $"/d /c {command} {args}";
string localCommand = @"cmd";
if (command.StartsWith(@"\\"))
{
NetworkHelpers.CreateMapPath(Path.GetDirectoryName(command));
}
ProcessStartInfo procStartInfo = new ProcessStartInfo(localCommand, localArgs);
procStartInfo.UseShellExecute = false; // use shell (command window)
procStartInfo.CreateNoWindow = false; // Yes, create a window
procStartInfo.ErrorDialog = false; // Will not show error dialog if process can't start
procStartInfo.WindowStyle = ProcessWindowStyle.Normal; // Normal type window
procStartInfo.RedirectStandardOutput = true; // redirect stdout so we can capture
procStartInfo.RedirectStandardError = true; // redirect stderr so we can capture
return _RunProcessCollectOutput(procStartInfo, out output);
}
Последний кусок:
private static bool _RunProcessCollectOutput(ProcessStartInfo procStartInfo, out string output)
{
bool successful = true;
output = ""; // init before starting
LogManager.LogDebugMessage("_RunProcessCollectOutput");
try
{
// Create proc, assign ProcessStartInfo to the proc and start it
Process proc = new Process();
proc.StartInfo = procStartInfo;
// if collecting output, we must wait for process to end in order
// to collect output.
LogManager.LogDebugMessage($"Starting {procStartInfo.FileName} {procStartInfo.Arguments}");
LogManager.LogDebugMessage("[wait forever]");
successful = proc.Start();
string temp1 = proc.StandardOutput.ReadToEnd(); // return output if any
string temp2 = proc.StandardError.ReadToEnd(); // return error output if any
proc.WaitForExit(); // Wait forever (or until process ends)
if (temp1.Length > 0)
{
output += "[STDOUT]\n" + temp1 + "[/STDOUT]\n";
}
if (temp2.Length > 0)
{
successful = false;
output += "[STDERR]\n" + temp2 + "[/STDERR]\n";
}
}
catch (Exception e)
{
if (procStartInfo != null)
{
LogManager.LogErrorMessage($"Error starting the process {procStartInfo.FileName} {procStartInfo.Arguments}");
}
LogManager.LogExceptionMessage(e);
successful = false;
}
return successful;
}
Итак, как я уже сказал... он работает нормально, когда я запускаю из командной строки, но когда эта команда вызывается из интерфейса, таким образом, он, кажется, получает только первые несколько окон, а затем зависает.
Смотрите этот вывод из журнала. Он получает первые несколько (которые кажутся панелями задач на трех мониторах, которые у меня есть, затем он просто зависает. Кажется, он просто останавливается после четвертого, хотя и говорит мне:
Найдено 40 десктопов детей (windows).
Я предполагаю, что окно cmd в четвертом является тем, которое запускает инструмент, но я не понимаю, как это должно иметь значение.
[20190116164608|DBG|CollectUSLogs.exe]_RunProcessCollectOutput
[20190116164608|DBG|CollectUSLogs.exe]Starting cmd /d /c C:\XTT\UsbRoot\bin\CollectSystemLogs.exe -ss -dp D:\
[20190116164608|DBG|CollectUSLogs.exe][wait forever]
[20190116164608|DBG|CollectSystemLogs.exe]Argument: -ss
[20190116164608|DBG|CollectSystemLogs.exe]Argument: -dp
[20190116164608|DBG|CollectSystemLogs.exe]D:\
[20190116164608|ERR|CollectSystemLogs.exe]Could not find a part of the path 'e:\host\config\iu\systemoptions.xml'.
[20190116164608|ERR|CollectSystemLogs.exe] at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
[20190116164608|ERR|CollectSystemLogs.exe] at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRigh
[20190116164608|ERR|CollectSystemLogs.exe]ts, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Bo
[20190116164608|ERR|CollectSystemLogs.exe]olean bFromProxy, Boolean useLongPath, Boolean checkHost)
[20190116164608|ERR|CollectSystemLogs.exe] at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 buffe
[20190116164608|ERR|CollectSystemLogs.exe]rSize)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCac
[20190116164608|ERR|CollectSystemLogs.exe]hePolicy cachePolicy)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlTextReaderImpl.FinishInitUriString()
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlTextReaderImpl..ctor(String uriStr, XmlReaderSettings settings, XmlParserContext context
[20190116164608|ERR|CollectSystemLogs.exe], XmlResolver uriResolver)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlReaderSettings.CreateReader(String inputUri, XmlParserContext inputContext)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.XmlReader.Create(String inputUri, XmlReaderSettings settings, XmlParserContext inputContext
[20190116164608|ERR|CollectSystemLogs.exe])
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.Linq.XDocument.Load(String uri, LoadOptions options)
[20190116164608|ERR|CollectSystemLogs.exe] at System.Xml.Linq.XDocument.Load(String uri)
[20190116164608|ERR|CollectSystemLogs.exe] at XTT.USCartHelpers.get_SerialNumber() in C:\XTT\XTT_Tools\XTT\Helpers\USCartHelpers.cs:line 64
[20190116164608|ERR|CollectSystemLogs.exe]Cannot find Registry32 HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Philips\PHC\Ultrasound
[20190116164608|WRN|CollectSystemLogs.exe]GetOptionalRegValue: Unable to find ProductModel.
[20190116164608|ERR|CollectSystemLogs.exe]Cannot find Registry32 HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Philips\PHC\Ultrasound
[20190116164608|WRN|CollectSystemLogs.exe]GetOptionalRegValue: Unable to find ProductProgram.
[20190116164608|DBG|CollectSystemLogs.exe]Zipfilename: D:\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND).zip
[20190116164608|DBG|CollectSystemLogs.exe]ZipFolder = C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)
[20190116164608|WRN|CollectSystemLogs.exe]Can't empty C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND). It doesn't exist.
[20190116164608|INF|CollectSystemLogs.exe]C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND) created
[20190116164608|ERR|CollectSystemLogs.exe]Can't copy SystemOption*.xml in e:\host\config\iu. It doesn't exist.
[20190116164608|ERR|CollectSystemLogs.exe]ERROR collecting SystemOptions.
[20190116164608|ERR|CollectSystemLogs.exe]SystemOptions may not be included in zip file.
[20190116164609|DBG|CollectSystemLogs.exe]Starting CaptureWindows(C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND))
[20190116164624|DBG|CollectSystemLogs.exe]40 desktopChildren (windows) found.
[20190116164624|DBG|CollectSystemLogs.exe]Capturing window []
[20190116164624|DBG|CollectSystemLogs.exe]In try{}
[20190116164624|DBG|CollectSystemLogs.exe]Bitmap Created 1920x40
[20190116164624|DBG|CollectSystemLogs.exe]Graphics created System.Drawing.Graphics
[20190116164624|DBG|CollectSystemLogs.exe]hwnd created 2626768
[20190116164624|DBG|CollectSystemLogs.exe]PrintWindow() complete.
[20190116164624|DBG|CollectSystemLogs.exe]Saving C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window01.png
[20190116164624|DBG|CollectSystemLogs.exe]C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window01.png saved
[20190116164624|DBG|CollectSystemLogs.exe]End of try{}
[20190116164624|DBG|CollectSystemLogs.exe]End of foreach
[20190116164624|DBG|CollectSystemLogs.exe]Capturing window []
[20190116164624|DBG|CollectSystemLogs.exe]In try{}
[20190116164624|DBG|CollectSystemLogs.exe]Bitmap Created 1080x40
[20190116164624|DBG|CollectSystemLogs.exe]Graphics created System.Drawing.Graphics
[20190116164624|DBG|CollectSystemLogs.exe]hwnd created 66184
[20190116164624|DBG|CollectSystemLogs.exe]PrintWindow() complete.
[20190116164624|DBG|CollectSystemLogs.exe]Saving C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window02.png
[20190116164624|DBG|CollectSystemLogs.exe]C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window02.png saved
[20190116164624|DBG|CollectSystemLogs.exe]End of try{}
[20190116164624|DBG|CollectSystemLogs.exe]End of foreach
[20190116164624|DBG|CollectSystemLogs.exe]Capturing window []
[20190116164624|DBG|CollectSystemLogs.exe]In try{}
[20190116164624|DBG|CollectSystemLogs.exe]Bitmap Created 1920x40
[20190116164624|DBG|CollectSystemLogs.exe]Graphics created System.Drawing.Graphics
[20190116164624|DBG|CollectSystemLogs.exe]hwnd created 333194
[20190116164624|DBG|CollectSystemLogs.exe]PrintWindow() complete.
[20190116164624|DBG|CollectSystemLogs.exe]Saving C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window03.png
[20190116164624|DBG|CollectSystemLogs.exe]C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window03.png saved
[20190116164624|DBG|CollectSystemLogs.exe]End of try{}
[20190116164624|DBG|CollectSystemLogs.exe]End of foreach
[20190116164624|DBG|CollectSystemLogs.exe]Capturing window [C:\WINDOWS\SYSTEM32\cmd.exe]
[20190116164624|DBG|CollectSystemLogs.exe]In try{}
[20190116164624|DBG|CollectSystemLogs.exe]Bitmap Created 993x519
[20190116164624|DBG|CollectSystemLogs.exe]Graphics created System.Drawing.Graphics
[20190116164624|DBG|CollectSystemLogs.exe]hwnd created 269574
[20190116164624|DBG|CollectSystemLogs.exe]PrintWindow() complete.
[20190116164624|DBG|CollectSystemLogs.exe]Saving C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window04.png
[20190116164624|DBG|CollectSystemLogs.exe]C:\temp\20190116164608_TestCart00_(NOTFOUND)_(NOTFOUND)\Window04.png saved
[20190116164624|DBG|CollectSystemLogs.exe]End of try{}
[20190116164624|DBG|CollectSystemLogs.exe]End of foreach
Любые идеи или предложения будут высоко оценены.
Я создал фиктивную командную строку, которая вызывает ту же самую командную строку (CollectSystemLogs.exe), и она работает с использованием того же вызова метода RunCommandProcessCollectOutput().
1 ответ
Хорошо... Оказывается, @Adam Plocher был прав в первом предположении. Это флаг UseShellExecute.
Я использовал свою оболочку для запуска процесса, который имел UseShellExecute=false, потому что это было необходимо для перенаправления STDOUT и STDERR, которые я хотел, чтобы поместить их в мой журнал.
Я думал, что пытался использовать UseShellExecute=true раньше, но я думаю, что когда я пытался это сделать, я получил ошибку, потому что он все еще пытался перенаправить эти потоки, и я, должно быть, остановился на этом.
Я использовал свою другую обертку, которая имеет UseShellExecute=true, но не дает мне STDOUT и STDERR, и это работает. Я думаю, я могу с этим смириться, так как в любом случае достаточно подробного ведения журнала в моем коде.
Я до сих пор не знаю, ПОЧЕМУ это ведет себя так при запуске процесса с UseShellExecute=false;