0

I have a C# form (WPF/XAML) that triggers a new process to open a file using a 3rd party application (SAS JMP) by the default file association:

System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
myProcess.StartInfo.FileName = @"C:\temp\test.jsl";
myProcess.Start();

I'm looking for a way to report back to my application that the process has started and the file has finished opening, or at least, the application window for the new process has appeared.

I tried to wait for myProcess.Responding to return true, but that happens instantly before I even see the application window appear.

Solution based on input from Marcos Vqz de Rdz:

bool alreadyOpen = Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero && p.ProcessName == "jmp").Count() > 0;

Process myProcess = new Process();
myProcess.StartInfo.FileName = @"C:\temp\test.jsl";
myProcess.Start();

if (!alreadyOpen)
{
    bool wait = true, timeout = false;
    DateTime start = DateTime.Now;
    while (!timeout && wait)
    {          
        timeout = (DateTime.Now - start).TotalSeconds > 10;
        var window = Process.GetProcesses().Where(p => p.Id == myProcess.Id).FirstOrDefault();
        if (window != null)
            wait = string.IsNullOrEmpty(window.MainWindowTitle);                    
    }
}
Community
  • 1
  • 1
Paul
  • 3,725
  • 12
  • 50
  • 86
  • possible duplicate of [C# - Making a Process.Start wait until the process has start-up](http://stackoverflow.com/questions/6390030/c-sharp-making-a-process-start-wait-until-the-process-has-start-up) – GSerg Jul 10 '14 at 17:50
  • Thanks for the link. 1. `WaitForExit()` won't do, because I want the application to stay open. 2. `WaitForInputIdle()` is on the right track, but still triggers before I actually see the new window. 3. I'd prefer not to hardcode a sleep. If the user already has an instance of the application open, then it will open very quickly and I'd be causing unnecessary delay. – Paul Jul 10 '14 at 17:55
  • If you need "finished opening the file" I'm afraid the only way (assuming generic application you have no way to automate) is to check if your process can open file after making sure there is main window for the other app is present. Even that may not be enough is app periodically re-opens file or keep it open all the time. – Alexei Levenkov Jul 10 '14 at 17:56
  • @Paul There are other solutions in the other answers. – GSerg Jul 10 '14 at 19:54
  • @GSerg Thanks, I posted the solution that I went with. This takes into account if the user already has the application open, in which case we "reuse" that window. – Paul Jul 10 '14 at 19:57

1 Answers1

1

Have you tried using Process.WaitForInputIdle Method?

myProcess.WaitForInputIdle();

UPDATE

OK, the only thing I can think of right now, is search for the new application window to show up.

You can find something useful here.

UPDATE

Found this:

var openWindowProcesses = System.Diagnostics.Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero);

Use that with a timer right before you start the process to wait for the 3rd party app window to show up.

UPDATE

foreach (var item in openWindowProcesses)
{
    Console.WriteLine(GetWindowTitle(item.MainWindowHandle));
}

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);

private static string GetWindowTitle(IntPtr windowHandle)
{
    uint SMTO_ABORTIFHUNG = 0x0002;
    uint WM_GETTEXT = 0xD;
    int MAX_STRING_SIZE = 32768;
    IntPtr result;
    string title = string.Empty;
    IntPtr memoryHandle = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(MAX_STRING_SIZE);
    System.Runtime.InteropServices.Marshal.Copy(title.ToCharArray(), 0, memoryHandle, title.Length);
    SendMessageTimeout(windowHandle, WM_GETTEXT, (IntPtr)MAX_STRING_SIZE, memoryHandle, SMTO_ABORTIFHUNG, (uint)1000, out result);
    title = System.Runtime.InteropServices.Marshal.PtrToStringAuto(memoryHandle);
    System.Runtime.InteropServices.Marshal.FreeCoTaskMem(memoryHandle);
    return title;
}

Source: c# Get process window titles

Community
  • 1
  • 1
MJVC
  • 497
  • 4
  • 7
  • `WaitForInputIdle()` comes close, but still triggers before I actually see the new window. It's better than before though. – Paul Jul 10 '14 at 17:55