Process.CloseMainWindow() not working - c#

I start the Windows On-Screen-Keyboard like that:
s_onScreenKeyboard = new Process();
s_onScreenKeyboard.StartInfo = new ProcessStartInfo("osk.exe");
s_onScreenKeyboard.EnableRaisingEvents = true;
s_onScreenKeyboard.Exited += new EventHandler(s_onScreenKeyboard_Exited);
s_onScreenKeyboard.Start();
This works fine, but when I try to stop it using the following code, it does not work, i.e. the OSK keeps running and the method returns false:
s_onScreenKeyboard.CloseMainWindow();
if (!s_onScreenKeyboard.HasExited)
{
if (!s_onScreenKeyboard.WaitForExit(1000))
{
s_onScreenKeyboard.Close();
//s_onScreenKeyboard.Kill();
}
}
When uncommenting s_onScreenKeyboard.Kill(); it is closed, but the problem is that osk.exe obviously uses another process called "msswchx.exe" which is not closed if I simply kill the OSK process. This way, I would end up with hundreds of these processes which is not what I want.
Another strange thing is that the CloseMainWindow() call worked at some time, but then it suddenly did not work anymore, and I do not remember what has changed.
Any ideas?
EDIT: I have found a solution myself. Please see my answer for details.
Background:
I am implementing an On-Screen-Keyboard for my application because it should work with a touchscreen. It is important that the keyboard layout matches the layout which is configured in Windows since the application will be shipped to many different countries. Therefore, instead of implementing a custom keyboard control with approx. 537 keyboard layouts (exaggerating a little here...), I wanted to utilize the Windows built-in On-Screen-Keyboard which adapts to the selected keyboard layout automatically, saving a lot of work for me.

I have found the/a solution myself:
When I successfully retrieve the MainWindowHandle after the process has been started, the call to CloseMainWindow() is also successful later on. I do not understand the reason for this, but the important thing is: it works!
BTW, for others having the same problem: The MainWindowHandle is not available immediately after starting the process. Obviously, it takes some milliseconds until the MainWindow is started which is why I use the following code to retrieve the handle:
DateTime start = DateTime.Now;
IntPtr handle = IntPtr.Zero;
while (handle == IntPtr.Zero && DateTime.Now - start <= TimeSpan.FromSeconds(2))
{
try
{
// sleep a while to allow the MainWindow to open...
System.Threading.Thread.Sleep(50);
handle = s_onScreenKeyboard.MainWindowHandle;
}
catch (Exception) { }
}
In this code I continuously get the MainWindowHandle every ~50ms as long as it is still equal to IntPtr.Zero. If the handle could not be retrieved after 2 seconds, I quit the loop to avoid an endless loop in case of error.

You need to wait untill the process finishes initialization, run
Process.WaitForInputIdle Method in order to do that.

Related

Start more processes and get window handle for each right after start and without sleep? [duplicate]

This question already has answers here:
How to open an application on active display while using Process.Start()?
(1 answer)
Programmatically, how does this application detect that a program is ready for input
(3 answers)
Determining when a process has finalized initialization
(2 answers)
Check if Windows Application is running (not process)
(4 answers)
Closed 5 years ago.
I'm working on an application that starts other processes (some console applications, etc.). Each process has a user interface. When each process is started (via process.Start()), I want to move the process UI to some other location on the screen (also on the second monitor). I know how to do this later after a "sleep" or button click action. The problem is that I want the window to move right after the process starts. The window handle is still not "available" (value is 0).
I searched for a solution and came across the process.WaitForExit(100) method and the process.Exited event handler. After investigating these, I discovered that:
the process.Exited event is called when the process ends and not when the process "loads," and
process.WaitForExit(100) causes the program to "sleep" when it is invoked.
So, I need some architecture guidance. How can I solve my problem without "sleeping" (for example, via process.WaitForExit(100))? Should I consider an approach that involves one of the following techniques:
Mutex,
Multithreading, or
Async process start?
Or, is process.WaitForExit(100) really OK (not "dangerous") for a stable application (if I will run up to 15 processes)? Here is my code example:
private void startApplication(
int aApplicationId,
string aBrowserPath,
string aAppPath,
int aMid,
int aAppLeft,
int aAppTop,
int aAppWidth,
int aAppHeight) // Try to start application process
{
Process process = new Process();
try
{
process.StartInfo.FileName = aAppPath;
//process.EnableRaisingEvents = true;
//process.Exited += new EventHandler(myProcess_Exited);
//process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
process.WaitForExit(100);
mProcessMap.Add(aApplicationId, process); // Add to process map
// Move process window to right position
IntPtr windowHandle = process.MainWindowHandle;
this.moveAppToRightPosition(windowHandle, aMid, aAppLeft, aAppTop,
aAppWidth, aAppHeight);
}
catch (Exception exc)
{
Console.WriteLine("ERROR! Process start exception!");
}
}
You can't retrieve the MainWindowHandle immediately after the process is created because the creation of a process and of a window are two completely independent things (not to mention that a process may not even have a window). So there is nothing wrong with sleeping and waiting for the MainWindowHandle to appear. MSDN says you need to call Refresh to update it.
If you don't want to sleep you have two options:
Set a hook that will notify you when the process created a window. More details in this answer.
If you also created the process you are launching then you can insert the logic that will notify the master process via a socket or named pipe once the window is created.
Sleeping might not be the right way, because the process might need more time to start.
It may be possible to start a thread (if you want to asynchronously run the function startApplication) and in that thread check for the state of the process and when available set the position of window (looping with sleep at the end of loop).
I would use SendMessageTimeout to send WM_NULL messages in a loop, with a small timeout of say 50-100ms. If the function succeeds, that means that the new process is alive and going strong. In this way, you will block only for the timeout interval.

Don't execute next line until Process.Start is complete

I'm writing a simple C# console application which opens up four instances of Windows Explorer and uses Pinvoke's MoveWindow to place them around the screen. My problem is that the Process.Start command seems to run too slowly, and the MoveWindow function cannot find the process unless I deliberately slow the program down. Here is my code:
Process.Start(new ProcessStartInfo()
{
FileName = "explorer",
Arguments = location, //Defined elsewhere (for testing just ".")
UseShellExecute = false
});
int[] pos = GetPositions(position); //Little function which gets the positions I want for this window
System.Threading.Thread.Sleep(500);
IntPtr hnd = GetForegroundWindow();
bool ok = MoveWindow(hnd, pos[0], pos[1], pos[2], pos[3], false);
See how I'm having to Sleep for an (arbitrary) half second. It's not ideal, because I don't know the specifications of the machine it will be running on. It could end up being either inefficient on a fast machine or could break on a slower machine. Is there a more concrete way of waiting until a process has started where I don't have to arbitrarily wait for a set time?
Also, on a semi-related note, you may notice a bit of cowboy code which just gets the foreground window to grab the window I want moved. This is because I had an absolute mare trying to pick out specific Window Explorer processes. From what I can tell they just don't work the same way as anything else (like when I was testing with notepad or IE) and just passing a handle doesn't work. Anyone who's familiar with this problem, feel free to contribute, but if not don't spend any time on it - clunky as this solution is, it works, and for a little application I don't want to spend hours bashing my head against the keyboard trying to figure out the proper way of doing it.
In theory, you would be able to call Process.WaitForInputIdle() which waits for a message loop to be created and be up and running in the newly started process. However, this does not work with all applications (and most likely not with Windows Explorer).
A cheap trick is to poll for the main window of the process to be created (source):
public static bool WaitForMainWindow(this Process process)
{
while (!process.HasExited && process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(10);
}
return !process.HasExited;
}
You might want to make this more robust by adding a timeout:
public static bool WaitForMainWindow(this Process process, uint timeout)
{
var start = DateTime.Now;
while (!process.HasExited && process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(10);
if ((DateTime.Now - start).TotalMilliseconds >= timeout)
{
return false;
}
}
return !process.HasExited;
}
You can try something like this
while(!process.HasExited)
{
//process running
}
//Done

Why the Process Exited event raise automatic all the time?

In the top of Form1 i did:
private Process zipFileDirectoryProcess;
In the constructor i did:
zipFileDirectoryProcess = new Process();
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
zipFileDirectoryProcess.StartInfo.CreateNoWindow = true;
zipFileDirectoryProcess.EnableRaisingEvents = true;
zipFileDirectoryProcess.Exited += new EventHandler(zipFileDirectoryProcess_Exited);
Then i have a method i call it from a button click event:
private void Compress()
{
zipFileDirectoryProcess.StartInfo.Arguments = zipFileDirectoryProcess.StartInfo.Arguments = "/select," + Path.GetFullPath(t);
zipFileDirectoryProcess.Start();
zipFileDirectoryProcess.WaitForExit();
this.TopMost = true;
}
And then in the bottom the Exited event:
private void zipFileDirectoryProcess_Exited(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
this.TopMost = false;
}));
}
What i wanted to do is only when i close the process window after started it in the method only if closed the window/process then do the Exited event.
The problem is that once the process started after 2-3 seconds its jumping automatic to the Exited event.
How can i fix it ? Tried examples cant figure out.
Tried to add this line:
zipFileDirectoryProcess.WaitForExit();
But no effect.
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
Trying to start Windows Explorer again when it is already running, and it is always running, will have a disappointing outcome. It is a "heavy" process and it intentionally tries the minimize the number of running copies. Otherwise known as a "single-instance app". There are lots like that, the Microsoft Office programs are single instance apps for example.
So what really happens is that explorer.exe actually starts up, but sees that another instance is already running. And uses process interop to ask that first instance to do the job that you asked it to do. Since you didn't ask it to do anything, you just get another window, displayed by the first instance. The one that you started immediately quits, it doesn't have anything else to do.
So, yes, you'll see that the Exited event fires without you doing anything. Accurately telling you that the explorer.exe process you started did in fact quit. Easy to see in the Taskmgr.exe Processes tab btw. Waiting for that window to be closed is never going to work, it is displayed by the original instance of explorer.exe.
This will just not work the way you hope it will work. What you are actually trying to do is not quite obvious but can be guessed at. Creating a ZIP archive is not difficult, there are excellent libraries available for C# to get the job done, no point in asking another program to do it for you. DotNetZip and SharpZipLib are very popular. It got finally added to .NET as well in version 4.5, Microsoft finally getting over the lost Stacker lawsuit, about time. If you really, really want another program to do it for you then use a console mode zipper like 7-zip.
To show output folder in windows explorer to the user, it's simply enough to do this:
Process.Start("explorer.exe", OutputDir);

How to cancel the shutdown event of PC?

I am making an application in which i want to execute some database queries just before system get shut down. I am using this code -
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
e.Cancel = true;
MessageBox.Show("Shut down canceled");
}
I did execute this application and tried to shut down the system and this code captured the shut down event also but the problem is after showing message box it shows this screen also- [I can't post the image as i don't have 10 points.]
it shows the name of my application that stopped the system to get shut down and it also provide "Force Shut down button" , i dont want this screen to be displayed as user can forcefully shut down the system before completion of execution of my queries.
Need expert advices on this, thanks a lot in advance.
The Short Reliable Answer:
On any recent Windows version, you can try to cancel shutdown but Windows may decide to ignore you; this is sadly by design. If you absolutely have to finish operations before your process is terminated, the right place to do this is in the SessionEnded handler. If you have tasks that must complete before your process terminates, you must not return from your SessionEnded handler until all your work is done (so your queries, etc. have finished.)
So instead of (or as well as, if you prefer) handling SessionEnding, handle SessionEnded and do you work there:
static void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
WaitForQueriesToFinishOrSaveState(); // new code
}
How you implement that waiting will depend on your application; if you need to run queries afresh you may be able to do them therein, or you may need to Thread.Join() or otherwise wait for background tasks to complete; but it must be a blocking wait (so you need to not return from the function until you're done).
Since you can't absolutely stop shutdown, there's perhaps little point in attempting the cancellation in this case, so I'd recommend not setting e.Cancel in SessionEnding at all. On older Windows versions this was more meaningful, very much less so now unfortunately.
It's also recommended by the API docs not to do any significant work in SessionEnding (including message boxes), but to set any flags you need to return immediately and then do the work in SessionEnded. (Unproven aside: I'm suspicious that if you don't return quickly enough, this may hasten the appearance of the "programs are misbehaving, do you want to kill them" screen for the user, as Windows believes you're not playing nice any more.)
Behind the Scenes:
Setting e.Cancel indicates to Windows that you'd like the session not to end; but the user still gets a look in; and Windows may decide to ignore your request for any reason it feels pertinent. That's just the way the cookie crumbles. You may find hacks that work situationally, but there's no API or approach which is Microsoft-approved and therefore likely to work consistently now and in the future.
Under the covers, Windows is sending your process' windows a WM_QUERYENDSESSION message, which .NET receives for you and translates into the SessionEnding event) and will pass your cancellation (or lack of) back to Windows; returning TRUE if you don't cancel, FALSE if you do.
After this, Windows takes a look at all process' requests and depending on the cause of the shutdown and other factors may well still decide to go ahead despite such requests. It may also alert the user if processes are not cooperating and give them the option of killing the process.
Whether you handle WM_QUERYENDSESSION (SessionEnding) or not, you always get one last chance to clean up: you're sent a WM_ENDSESSION message (translated into SessionEnded). The tricky part is that you have to do all your vital tasks before all your SessionEnded handlers have returned!
Once Windows hears back from its WM_ENDSESSION (SessionEnded) call, all bets are off as far as your application's lifetime is concerned and Windows can terminate your process at any point.
Raymond Chen covered this quite expertly and quite recently.
http://blogs.msdn.com/b/oldnewthing/archive/2013/06/27/10429232.aspx
As a footnote, SystemEvents.SessionEnded is a convenience; if yo have a top level application window you can bypass it entirely and achieve the same via:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x16) // WM_ENDSESSION
{
WaitForQueriesToFinishOrSaveState();
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
in shutdown command there's a switch for abort shutdown. you have to call this command by your c# code
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine(#"shutdown -a");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());

Getting process ID from a shell executed file?

I am making a program for handheld PDAs using .net 2.0 compact framework and I have this one part which I'm not proud of and I was hoping for a more elegant solution.
Basically the problem is another process using my file in this case its Windows Media Player. I start the process by passing the file location to Process.Start but it seems the process returned is short lived and it is spawning another process? So I tried looking up how to get child process information but had some problems with that (i think no processes were being returned for some reason).
So i currently do this dodgy fix
string processName = item.Text;
Process proc = Process.Start(processName, null);
if (!proc.Start())
MessageBox.Show("Failed to start process", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1);
else
{
IntPtr newWindow = IntPtr.Zero;
TimeSpan limit = TimeSpan.FromSeconds(3);
DateTime start = DateTime.Now;
DateTime now = start;
// do while the following:
// window is not null
// window is not ourself
// under 3 seconds
do
{
newWindow = Win32.GetForegroundWindow();
now = DateTime.Now;
// taking too long
if (now - start > limit)
break;
}
while (newWindow == IntPtr.Zero || newWindow == this.Handle);
if (newWindow != IntPtr.Zero && newWindow != this.Handle)
{
uint processID = 0;
if (Win32.GetWindowThreadProcessId(newWindow, out processID) != 0)
{
//const int stringSize = 1024;
//StringBuilder sb = new StringBuilder(1024);
//Win32.GetWindowText(newWindow, sb, stringSize);
m_processes.Add(new ProcessIDWithName(processID, processName));
}
}
}
As you can see I don't like it and it's unreliable however it does work for now (i needed a solution whether it was bad or not).
Why do I need the process ID? Because windows media player is keeping the file open on me and I cannot move/delete the file and therefore I need to kill the process off before I do so. I could do a similar fix with FindWindow but I was thinking more generically as it might not be a media file opened in windows media player.
So basically I would like a better solution if possible!
Also if you wondering why I'm not using a Stopwatch its because it doesn't seem to exist in .net 2.0 cf, also I don't need accuracy to that extent.
There are loads of questions that pop up here.
Why aren't you executing media player itself instead of shellexecuting the name of the target file?
How do you know when the media is done playing in order to close the file?
Why not use the toolhelp APIs to simply enumerate processes instead of the wacky GetForegroundWindow/GetWindowsThreadProcessId shenanigans?
Why aren't you just using the Media Player ActiveX control instead of this kludge so you'd actually have control over things?
If you intend to make this generic for any file (i.e. not just media, but maybe something like the Word viewer, etc) then you're really out of luck and need to rethink whatever it is you're trying to do (you've not told us what you're trying to achieve, only how you['ve decided to implement it). Applications don't normally close in WinMo, they typically just lose focus of get minimized, so you don't really know when a user is "done" with the file.
The application associated with the file may already be running, so terminating it yourself is an unfriendly thing to do.
The target application really is not designed to give you a callback when it's done with any particular file.
I have no experience with PDA programming, bu you can try to use Job objects (see http://msdn.microsoft.com/en-us/library/ms684847.aspx#job_object_functions). With respect of CreateJobObject you can create a new job. Then you create a suspended process and use AssignProcessToJobObject to assign the new process to th job object. Then you can resume the process.
The advantage of job object is, that you can receive full control of all child processes of the job. You can use TerminateJobObject to terminate all processes. If you create creates an I/O completion port to wait for the end of the direct started process and all it's child processes or monitor of all child processes created and much more. If you need I could post some code examples of links to code examples.

Categories