Se l'applicazione è un'applicazione finestra, vale a dire se si tratta di un processo con un loop di messaggi, il modo standard sarebbe utilizzare il metodo Process.WaitForInputIdle
.
Questo metodo bloccherà fino a quando il rispettivo processo ha raggiunto lo stato di inattività per la prima volta. Questo è lo stato in cui viene creata la finestra principale dell'applicazione e è possibile inviare messaggi all'applicazione .
Il nome del metodo è un po 'confuso, dovrebbe essere 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);
}
}
noti che ci potrebbe essere ulteriormente inizializzazione in corso in un thread in background finché l'applicazione è completamente pronto. Tuttavia, essere in grado di gestire i messaggi della finestra è probabilmente la definizione più chiara di un'applicazione avviata completamente.
Aggiornamento:
Se è necessario misurare il tempo di avvio di un server COM è comunque possibile utilizzare Process.Start
e quindi utilizzare AccessibleWindowFromObject
per accedere all'oggetto COM effettivo per l'automazione. La procedura è un po 'complicata e dovrai conoscere il nome della classe della finestra dell'oggetto accessibile.
Di seguito è un esempio di come è possibile misurare il tempo di avvio di Word e ottenere un oggetto Word.Application
allo stesso tempo, vedere i commenti come si dovrebbe regolare per soddisfare il server 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));
}
}
Sto esaminando anche questo. Poiché sto utilizzando una chiamata per creare un'istanza di un'applicazione tramite ActiveX, potrei essere in grado di utilizzare un semplice comando che può essere elaborato dall'applicazione solo dopo averlo caricato. Ciò dovrebbe darmi il più vicino possibile a un "tempo di avvio dell'applicazione". – Brundle
Ho finito per utilizzare una proprietà dall'altra applicazione dopo la "nuova" chiamata. Poi ho fermato il timer. In quel modo sapevo che la creazione era completamente finita. Sì, potrebbe esserci un po 'di tempo in più a causa della chiamata di proprietà, ma questo non influirà sul mio obiettivo finale. – Brundle