Nếu ứng dụng của bạn là một ứng dụng cửa sổ, tức là nếu nó là một quá trình với vòng lặp thông báo thì cách tiêu chuẩn sẽ là sử dụng phương pháp Process.WaitForInputIdle
.
Phương pháp này sẽ chặn cho đến khi quá trình tương ứng đã đạt trạng thái không hoạt động lần đầu tiên. Đây là trạng thái khi cửa sổ chính của ứng dụng được tạo và bạn có thể gửi tin nhắn đến ứng dụng .
Tên của phương pháp hơi khó hiểu, cần 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);
}
}
Lưu ý rằng có thể có khởi tiếp tục xảy ra trong một thread nền đến khi ứng dụng là hoàn toàn sẵn sàng. Tuy nhiên, việc có thể xử lý các thông điệp cửa sổ có lẽ là định nghĩa rõ ràng nhất về một ứng dụng đang được khởi động hoàn toàn.
Cập nhật:
Nếu bạn cần để đo thời gian khởi động của một máy chủ COM bạn vẫn có thể sử dụng Process.Start
và sau đó sử dụng AccessibleWindowFromObject
để truy cập các đối tượng COM thực tế cho tự động hóa. Quy trình này hơi phức tạp và bạn sẽ cần phải biết tên lớp cửa sổ của đối tượng có thể truy cập.
Dưới đây là ví dụ về cách bạn có thể đo thời gian khởi động của Word và nhận đối tượng Word.Application
cùng một lúc, xem các nhận xét về cách bạn sẽ phải điều chỉnh nó cho phù hợp với máy chủ COM của bạn.
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));
}
}
Tôi cũng đang xem xét điều này. Vì tôi đang sử dụng một cuộc gọi để tạo một cá thể của một ứng dụng thông qua ActiveX, tôi có thể sử dụng một lệnh đơn giản mà chỉ có thể được ứng dụng xử lý khi nó được nạp. Điều đó sẽ cho tôi gần với "thời gian khởi động ứng dụng" như tôi có thể nhận được. – Brundle
Tôi đã kết thúc bằng cách sử dụng thuộc tính từ ứng dụng khác sau cuộc gọi "mới". Sau đó, tôi ngừng bộ hẹn giờ. Bằng cách đó tôi biết rằng việc tạo ra đã hoàn tất. Có, có thể có thêm chút thời gian ở đó do cuộc gọi tài sản, nhưng điều này sẽ không ảnh hưởng đến mục tiêu cuối cùng của tôi. – Brundle