2010-09-09 135 views
5

我正在編寫一個C#應用程序,需要能夠告訴打開某個應用程序需要多長時間。我使用Stopwatch類作爲我的計時器。開始時間很簡單,因爲我完全用調用來運行.exe。問題在於程序完成打開時如何計時。我唯一能想到的就是使用PerformanceCounter對象並使用while循環檢查CPU%何時小於某個數。如何獲取應用程序啓動所需的時間?

目前我使用PerformanceCounter,但我沒有運氣顯示CPU%(它總是顯示0)。我的猜測是,應用程序的打開速度比PerformanceCounter可以檢查CPU%的速度快,或者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

在您的代碼中使用已知位置(所有初始化工作完成時調用的特定方法)而不是依賴CPU使用率作爲啓發式可能會更好。

+0

我也在看這個。由於我正在使用調用來通過ActiveX創建應用程序的實例,因此我可能會使用一個簡單的命令,只有在應用程序加載後才能處理該命令。這應該讓我儘可能接近「應用程序啓動時間」。 – Brundle 2010-09-09 20:01:54

+0

在「新建」調用之後,我結束了使用另一個應用程序的屬性。然後我停下了計時器。這樣我就知道創作完成了。是的,由於房產通話可能會稍微增加一些時間,但這不會影響我的最終目標。 – Brundle 2010-09-09 22:26:02

2

如果您擁有您的應用程序和應用程序的代碼,您可以使用System.Threading.MutexmyApplicationotherApplication之間進行通信。這樣,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); 
    } 
} 

注意,可能會有進一步的初始化在後臺線程怎麼回事,直到應用程序是完全準備好了。但是,能夠處理窗口消息可能是完全啓動的應用程序的最清晰定義。

更新:

如果你需要測量一個COM服務器的啓動時間,你仍然可以使用Process.Start,然後使用AccessibleWindowFromObject訪問實際COM對象自動化。該過程有點複雜,您需要知道可訪問對象的窗口類名稱。

下面是一個示例,您可以如何測量Word的啓動時間並同時獲得一個對象,請參閱評論如何調整它以適合您的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會爲我工作。但是如果在Windows任務管理器中使用帶有進程名稱的Process類來執行此操作,那將會很有趣。 – Brundle 2010-09-09 19:59:39

+1

@Brundle:使用'Process.GetProcessById(int processId)'或'Process.GetProcessesByName(string processName)',您可以使用您在任務管理器中看到的信息獲取進程。但是,如果您想測量啓動性能,這將毫無用處,因爲應用程序已經需要啓動以顯示在任務管理器中。 – 2010-09-09 20:27:34

+0

我剛剛嘗試Process.GetProcessesByName(myApplication)與進程[0] .WaitForInputIdle(),它似乎工作。儘管當我把這兩行代碼拿出來的時候,我有相似的時間,所以很難說。 – Brundle 2010-09-09 20:54:16

1

這是一個經典的海森堡問題,您通過測量影響測試結果。

託管應用程序的啓動時間通常由冷啓動時間控制。文件系統找到所有程序集所需的時間。與熱啓動時間相反,當組件存在於文件系統緩存中時,會看到更小的數字。然後您只能看到JIT編譯器編譯程序啓動代碼所需的時間。由於運行測試代碼會將.NET程序集放入文件系統緩存中,因此您的測試始終是熱身啓動。幸福的數字,可以肯定,但不準確。

您將不得不在非託管代碼中編寫測試。您熟悉的一些腳本語言。通過在主窗體的Shown事件中調用Application.Exit()來幫助它,以便在第一個窗體可見時立即退出程序。您現在只需要測量過程執行的時間。在運行測試之前

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

重新啓動機器,所以你可以肯定的是海森堡教授不在身邊:可能是爲.bat文件看起來像這樣簡單。並且請記住,您測量的確切數字不可能在客戶機器上重現。因爲它依賴於硬盤的狀態。僅用它來衡量代碼的增量改進是否成功。

+0

好點,但從OP的示例中,'otherApplication.Application()'指的是外部COM服務器(如Word或Excel)也可能是這種情況。 – 2010-09-09 20:24:57

+0

@ 0xA3 - 真的,沒有考慮到這一點。雖然沒有多少意義,但不能讓Office程序更快。 – 2010-09-09 20:44:52

相關問題