Click here to Skip to main content
15,891,033 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi All,

I have a thread that is solely dedicated to starting a process and checking when the window has opened. I have tried using the WaitForInputIdle which in theory should work but it is almost as if it is skipped - it is about as useful anyway.

So I have tried to detect when there is a MainWindowHandle using the Process.MainWindowHandle method, however after starting the process I am receiving an error: "No process associated with this object". But the window of the started process still appears - assuring me that there is a process running and something fishy is up. I've given the code below:

C#
Process proc = new Process();
proc.StartInfo.FileName = link;
proc.StartInfo.UseShellExecute = true;

ThreadPool.QueueUserWorkItem(delegate
{
   proc.Start();
   proc.WaitForInputIdle();
   try
   {
      while(proc.MainWindowHandle == IntPtr.Zero)
      {
      }
   }
   catch(Exception e)
   {
      MessageBox.Show(e.Message);
   }
});


Any help? Thanks in Advance!
Posted
Updated 3-Oct-12 5:31am
v2
Comments
Sergey Alexandrovich Kryukov 3-Oct-12 11:33am    
Where the exception is thrown on the line of WaitForInputIdle, or MainWindowsHandle? What window of the starting process? In this is the regular system console, you cannot use WaitForInputIdle. Unfortunately, both calls can throw exceptions if called too early.
--SA

This won't work if you're launching a console application. Console applications will never have a MainWindowHandle.

If that's what you're trying to do. You never really specified this in your post.

There is no function call or method to get the window handle of a console app. It's possible to do, but you have to P/Invoke the FindWindow function to look for the console windows title.
 
Share this answer
 
v2
Comments
MitchG92_24 4-Oct-12 3:04am    
sorry, I'm running a WPF application. The process being ran is an external application e.g Internet Explorer. I'm only hoping to apply this to applications with a GUI
If the Start method returns false then there is no process available. The only time I have observed this is when UseShellExecute was true and StartInfo.FileName was a shortcut, i.e. something.lnk.

I wonder if this is your situation as I see that you assign a variable called 'link' to StartInfo.FileName.

The solution would be to assign the target of the shortcut to StartInfo.FileName.

Alan.
 
Share this answer
 
Comments
MitchG92_24 4-Oct-12 3:05am    
The link in question is the full file path to the .exe that launches the application
Please see my comments on what it can be. First of all, you need to be sure that the application started as a separate process will eventually show some main window of the Windowed application; not sure it can be the regular system console.

In view of my comments above, try this:
C#
Process proc = new Process();
proc.StartInfo.FileName = link;
proc.StartInfo.UseShellExecute = false;
// proc.StartInfo.UseShellExecute = true; // not helpful, in most cases should be false, please see above
proc.Start(); // why not right here? it's not a blocking call, spawns a separate process

ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback( (state) => {
   proc.WaitForInputIdle(); // no need
   while (true) {
       try {
           IntPtr handle = proc.MainWindowHandle;
           // do something with the handle, see below
           return;
       } catch (System.InvalidOperationException) {
           // do something (log or whatever), but not re-throw
           // attention! this is a rare case when you can block distribution of exception; "don't repeat at home" :-)
           System.Threading.Thread.Sleep(0);
       } catch (System.System.NotSupportedException){
           // do something (log or whatever), but not re-throw
           // attention! this is a rare case when you can block distribution of exception; "don't repeat at home" :-)
           System.Threading.Thread.Sleep(0);
       }
   } //loop
}));


Please see, pay attention for exceptions:
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx[^],
http://msdn.microsoft.com/en-us/library/8d7363e2.aspx[^],
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.mainwindowhandle.aspx[^].

This should explain why UseShellExecute = false:
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute.aspx[^].

This is not all. You should organize proper exchange of data between different threads. You share one variable, proc and probably need to share at least one more object, the handle to the separate process's main window, if found. As to proc, what you have done is not interlocked, so it's questionable, but I think is should work in this specific case. However, should you modify this code slightly, you can make it extremely dangerous. To stay away from the sin, you should interlock all shared object. The schema is simple: make those objects fields, add properties backed by these fields and use the lock statement. Here is the schema:

C#
IntPtr handle;
object handleLock = new object();

IntPtr Handle {
    get { lock(handleLock) { return handle; } }
    set { lock(handleLock) { handle = value; } }
}


Different objects can be interlocked with separate lock object or with the same: it depends what do you lock from what. You need to consider appropriate state diagram to decide. Shared lock can be slower but protect more, unless you "design" a deadlock, which is not likely with just the lock statements.

Same thing goes about the criterion for wait for completion of the pooled thread, if you use collaborative approach. If you simply wait for it in the blocking call, there are no benefits of an extra thread. You can use lazy approach: wait for the thread completion (Thread.Join) only when a handle is about to be used. Please see:
http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx[^],
http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx[^],
http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx[^],
http://en.wikipedia.org/wiki/Lazy_evaluation[^].

You can greatly improve and simplify inter-thread communications if you use my pattern of the thread wrapper. Please see my past answers with the code and explanations:
How to pass ref parameter to the thread[^],
change paramters of thread (producer) after it started[^] (this one with lock).

Finally, I should tell you that the whole idea of using a separate process is very questionable: processes are well isolated. You control of another process via main windows handle is limited; its hard to maintain in any reliable way. Also, this is a weird Windows-specific way of IPC created when Windows was not an operating system but worked over the single-process DOS. Such tricks are not portable, they won't work on any other system where CLI is implemented, for example, via Mono.
(http://en.wikipedia.org/wiki/Common_Language_Infrastructure[^],
http://en.wikipedia.org/wiki/Mono_%28software%29[^],
http://www.mono-project.com/Main_Page[^].)

The best way of integration is no integration. If you want to use collaboration between different process, the other process should be designed for it. One example, albeit not most reasonable on, is automation interfaces. Ideally, it should be a library, so you could use it "in-proc". I do understand: sometimes you don't need better choice. But it's important to understand: then, it's a bad choice.

Good luck,
—SA
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900