Si su aplicación es una aplicación de ventana, es decir, si se trata de un proceso con un bucle de mensaje, la forma estándar sería utilizar el método Process.WaitForInputIdle
.
Este método se bloqueará hasta que el proceso respectivo haya alcanzado el estado inactivo por primera vez. Este es el estado cuando se crea la ventana principal de la aplicación y está bien enviar mensajes a la aplicación .
El nombre del método es un poco confuso, debería 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);
}
}
Tenga en cuenta que puede haber más de inicialización pasando en un subproceso en segundo plano hasta que la aplicación está completamente listo. Sin embargo, ser capaz de manejar mensajes de ventana es probablemente la definición más clara de una aplicación que se inició completamente.
Actualización:
Si es necesario medir el tiempo de puesta en marcha de un servidor COM puede seguir utilizando Process.Start
y luego usar AccessibleWindowFromObject
para acceder al objeto COM real para la automatización. El procedimiento es un poco complicado y deberá conocer el nombre de la clase de ventana del objeto accesible.
A continuación se muestra un ejemplo de cómo se puede medir el tiempo de inicio de Word y obtener un objeto Word.Application
al mismo tiempo; consulte los comentarios sobre cómo debe ajustarlo para adaptarlo a su servidor 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));
}
}
Estoy investigando esto también. Como estoy usando una llamada para crear una instancia de una aplicación a través de ActiveX, es posible que pueda usar un comando simple que solo puede ser procesado por la aplicación una vez que se haya cargado. Eso me debería dar lo más cercano a un "tiempo de inicio de aplicación" que pueda obtener. – Brundle
Terminé usando una propiedad de la otra aplicación después de la "nueva" llamada. Luego paré el temporizador. De esa manera sabía que la creación estaba completamente terminada. Sí, puede haber un poco de tiempo extra debido a la llamada a la propiedad, pero esto no afectará mi objetivo final. – Brundle