2010-09-09 4 views
5

Я пишу приложение C#, которое должно быть в состоянии сказать, сколько времени требуется для открытия определенного приложения. Я использую класс секундомера как мой таймер. Начать время легко, так как я установил его именно с вызовом для запуска .exe. Проблема заключается в том, как определить время, когда программа открывается. Единственное, что я мог придумать, чтобы проверить это, - это использовать объект PerformanceCounter и проверять, когда процессор% меньше определенного числа, используя цикл while.Как получить время, необходимое для запуска приложения?

В настоящее время я использую PerformanceCounter, но мне не повезло, что он показывает CPU% (он всегда отображает 0). Мое предположение заключается в том, что приложение открывается быстрее, чем PerformanceCounter может проверить на процессор% или что PerformanceCounter не видит имя процесса, потому что я его слишком быстро вызываю (я очень сомневаюсь в последнем из-за того, что Думаю, я бы ошиблась, если бы это произошло).

Есть ли другие способы решить эту проблему? Есть ли что-то, что я могу делать неправильно, что дает мне 0 CPU% все время? Я не ищу внешние инструменты вне моего приложения. Вот пример моего кода:

otherApp = new otherApplication.Application(); 
PerformanceCounter cpuCounter = new PerformanceCounter("Process", 
"% Processor Time", "otherApplication"); 
//This next line is to check if PerformanceCounter object is working 
//MessageBox.Show(cpuCounter.NextValue().ToString()); 
stopwatch.Start(); 
while (cpuCounter.NextValue() > 10) 
{ 
} 
stopwatch.Stop(); 

Отредактировано: Изменен код сказать otherApp и otherApplication вместо MYAPP и MyApplication, так что это может быть легче понять.

ответ

2

Возможно, вам лучше использовать известное место в вашем коде (конкретный метод, который вызывается, когда все операции инициализации завершены), а не полагаться на использование ЦП в качестве эвристики.

+0

Я тоже изучаю это. Поскольку я использую вызов для создания экземпляра приложения через ActiveX, я мог бы использовать простую команду, которая может быть обработана приложением только после ее загрузки. Это должно дать мне как можно ближе к «времени запуска приложения». – Brundle

+0

Я закончил использование свойства из другого приложения после «нового» вызова. Затем я остановил таймер. Таким образом, я знал, что создание полностью закончено. Да, там может быть немного дополнительного времени из-за вызова собственности, но это не повлияет на мою конечную цель. – Brundle

2

Если у вас есть код как для вашего приложения, так и для приложения, которое вы запускаете, вы можете использовать System.Threading.Mutex для связи между myApplication и otherApplication. Таким образом, otherApplication может сообщить вам, когда он завершил инициализацию.

3

Если ваше приложение является оконным приложением, то есть если это процесс с контуром сообщения, тогда стандартным способом будет использовать метод Process.WaitForInputIdle.

Этот метод блокируется до тех пор, пока соответствующий процесс не достигнет состояния простоя в первый раз. Это состояние, когда создается главное окно приложения, и нормально отправлять сообщения в приложение .

Название метода несколько путано, оно должно really be called WaitForProcessStartupComplete.

using System; 
using System.Diagnostics; 

class StartupWatch 
{ 
    static void Main() 
    { 
     string application = "calc.exe"; 
     Stopwatch sw = Stopwatch.StartNew(); 
     Process process = Process.Start(application); 
     process.WaitForInputIdle(); 
     Console.WriteLine("Time to start {0}: {1}", application, sw.Elapsed); 
    } 
} 

Обратите внимание, что там может быть в дальнейшем инициализация происходит в фоновом потоке, пока приложение не будет полностью готова. Тем не менее, возможность обработки оконных сообщений, вероятно, является самым ясным определением полностью запущенного приложения.

Update:

Если вам необходимо измерить время запуска СОМ-сервер, вы можете использовать Process.Start, а затем использовать для доступа AccessibleWindowFromObject реального объекта COM для автоматизации. Процедура немного сложна, и вам нужно будет узнать имя класса окна доступного объекта.

Ниже приведен пример того, как вы можете измерить время запуска Word и одновременно получить объект Word.Application, см. Комментарии относительно того, как вам придется настраивать его в соответствии с вашим COM-сервером.

using System; 
using System.Diagnostics; 
using System.Reflection; 
using System.Runtime.InteropServices; 
using System.Text; 
using Word = Microsoft.Office.Interop.Word; 

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")] 
public interface IDispatch 
{ 
} 

class StartupWatch 
{ 
    [DllImport("user32.dll", SetLastError = true)] 
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

    [DllImport("Oleacc.dll")] 
    static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint dwObjectID, byte[] riid, out IDispatch ptr); 

    public delegate bool EnumChildCallback(IntPtr hwnd, ref IntPtr lParam); 

    [DllImport("User32.dll")] 
    public static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildCallback lpEnumFunc, ref IntPtr lParam); 

    [DllImport("User32.dll")] 
    public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); 

    public static bool EnumChildProc(IntPtr hwndChild, ref IntPtr lParam) 
    { 
     StringBuilder buf = new StringBuilder(128); 
     GetClassName(hwndChild, buf, 128); 
     if (buf.ToString() == "_WwG") 
     { 
      lParam = hwndChild; 
      return false; 
     } 
     return true; 
    } 

    static Word.Application GetWordApplicationObject(Process process) 
    { 
     Word.Application wordApp = null; 
     if (process.MainWindowHandle != IntPtr.Zero) 
     { 
      IntPtr hwndChild = IntPtr.Zero; 

      // Search the accessible child window (it has class name "_WwG") 
      // as described in http://msdn.microsoft.com/en-us/library/dd317978%28VS.85%29.aspx 
      // 
      // adjust this class name inside EnumChildProc accordingly if you are 
      // creating another COM server than Word 
      // 
      EnumChildCallback cb = new EnumChildCallback(EnumChildProc); 
      EnumChildWindows(process.MainWindowHandle, cb, ref hwndChild); 

      if (hwndChild != IntPtr.Zero) 
      { 
       // We call AccessibleObjectFromWindow, passing the constant OBJID_NATIVEOM (defined in winuser.h) 
       // and IID_IDispatch - we want an IDispatch pointer into the native object model. 
       // 
       const uint OBJID_NATIVEOM = 0xFFFFFFF0; 
       Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}"); 
       IDispatch ptr; 

       int hr = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out ptr); 
       if (hr >= 0) 
       { 
        // possibly adjust the name of the property containing the COM 
        // object accordingly 
        // 
        wordApp = (Word.Application)ptr.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ptr, null); 
       } 
      } 
     } 
     return wordApp; 
    } 

    static void Main(string[] args) 
    { 
     Stopwatch sw = Stopwatch.StartNew(); 
     Process process = Process.Start(@"C:\Program Files (x86)\Microsoft Office\Office12\WINWORD.EXE"); 
     process.WaitForInputIdle(); 
     Console.WriteLine("Time to start {0}: {1}", "Word", sw.Elapsed); 
     Word.Application wordApp = GetWordApplicationObject(process); 
     Console.WriteLine(string.Format("Word version is: {0}", wordApp.Version)); 
    } 
} 
+0

Я сейчас смотрю на это. Я фактически использую объект ActiveX для создания экземпляра приложения, поэтому я не думаю, что Process.Start будет работать для меня. Но если есть способ использовать класс Process с именем процесса из диспетчера задач Windows, чтобы это сделать, это было бы интересно. – Brundle

+1

@Brundle: использование 'Process.GetProcessById (int processId)' или 'Process.GetProcessesByName (string processName)' вы можете получить процесс, используя информацию, которую вы видите в диспетчере задач. Однако это будет бесполезно, если вы хотите измерить производительность запуска, просто потому, что приложение уже должно появиться в диспетчере задач. –

+0

Я только что попробовал Process.GetProcessesByName (myApplication) с процессом [0] .WaitForInputIdle() и, похоже, работает. Хотя у меня были подобные времена, когда я просто взял эти две строки кода, так что это очень сложно сказать. – Brundle

1

Это классическая проблема Гейзенберга, вы влияете на результат теста своими измерениями.

Время запуска управляемого приложения, как правило, характеризуется временем холодного запуска. Время, необходимое файловой системе для поиска всех сборок. В отличие от времени теплого старта, гораздо меньшее количество вы видите, когда сборки присутствуют в кеше файловой системы. Затем вы видите только время, необходимое компилятору JIT для компиляции кода запуска вашей программы. Ваш тест всегда будет теплым, потому что запуск тестового кода помещает сборки .NET в кеш файловой системы. Счастливые цифры, конечно, но не точно.

Вам нужно будет написать свой тест в неуправляемом коде. Некоторые скриптовые языки, с которыми вы знакомы. Помогите ему, вызвав Application.Exit() в событии Shown основной формы, чтобы ваша программа сразу же закрылась, когда первая форма видна. Теперь вам нужно только измерить, как долго выполнялся процесс. Может быть так же просто, как BAT-файл, который выглядит следующим образом:

time /t 
start /wait yourapp.exe 
time /t 

перезагружать машину, прежде чем запустить тест, так что вы можете быть уверены, что профессор Гейзенберг не вокруг. И имейте в виду, что точное число, которое вы измеряете, вряд ли будет воспроизводиться на машине вашего клиента. Потому что он так зависит от состояния жесткого диска. Используйте его только для оценки успеха дополнительных улучшений кода.

+0

Хорошие точки, но из примера OP также может быть случай, когда 'otherApplication.Application()' ссылается на внешний COM-сервер, такой как Word или Excel. –

+0

@ 0xA3 - правда, не считал это. Не так много смысла в этом, но не может сделать программы Office быстрее. –

Смежные вопросы