I have a GUI application which starts two command line applications ( no window, just processes ) on Windows Mobile 6.5. I am using following code to start process:
Process service = new Process();
var pi = new ProcessStartInfo(exePath, null);
pi.UseShellExecute = false;
service.StartInfo = pi;
//start the process
service.Start();
Everything works fine, except, when service.Start() is called, my GUI application is covered by a circle thingy rotating in the center of the screen. Same Icon that rotates while waiting for a program to start, means OS is busy. Now since these processes will run for the life time of the GUI and even after GUI will quit, this circle does not go away. Is there a way to make it go away, via code? i tried Invalidating gui to repaint and changing cursor to normal. Dosnt work?
Any ideas?
What does exePath launch?
My guess is that Simon McKenzie has already nailed this one on the head. That is probably exactly what is going on.
However, the OS will also throw up a WaitCursor (the circle thingy rotating in the center of the screen) whenever it starts up some intensive CPU process. If that is the case, you may want to rethink how to approach this process you are launching - particularly if you are the one who wrote the process.
Consider the hypothetical application below to poll the network and compiled to your exePath:
while (true) {
if (networkDataAvailable()) {
processData();
}
}
That hypothetical application would be eating away at the processor's idle time.
If the process you are launching does something like that, you'll need to find another way to approach the problem like calling a simpler process in a timer tick event.
The modified process identified by the exePath would now be:
if (networkDataAvailable()) {
processData();
}
And from your Windows application, you could call this as needed like so:
private void timer1_Tick(object sender, EventArgs e) {
Process service = new Process();
var pi = new ProcessStartInfo(exePath, null);
pi.UseShellExecute = false;
service.StartInfo = pi;
//start the process
service.Start();
}
It sounds like the call to service.Start() may be blocking the main thread. Perhaps you could try starting your process in a new thread:
private void StartProcess()
{
Process service = new Process();
var pi = new ProcessStartInfo(exePath, null);
pi.UseShellExecute = false;
service.StartInfo = pi;
//start the process
service.Start();
}
...
Thread t = new Thread(StartProcess);
t.Start();
This is difficult to diagnose without understanding more about what the process you are starting up in exePath is doing. If that process throws up a wait cursor and monopolizes the CPU it may explain the behaviour you are seeing.
Related
I have a long running method that must run on UI thread. (Devex - gridView.CopyToClipboard())
I do not need the UI to be responsive while copying and I added a splash screen so the user isn't bored out of his mind.
When I run this program all is well.
Trouble starts when I run a different program which in turn starts a new process and runs the program on it.
After a few seconds of copying the title reads (Not Responding) and the mouse cursor shows busy, it of course clears up within a few seconds but I'd like to get rid of it since it gives the user the misconstrued feeling that the program is faulty.
Is there any way to set the "Time out" of a process I create?
EDIT:
The main program calls the following code:
fillsProcess = new Process();
fillsProcess.StartInfo.FileName = Application.ExecutablePath;
fillsProcess.Start();
In the fillsProcess, when a certain button is clicked the following code is called:
gridViewToCopy.CopyToClipboard();
This line of code takes a while to process and after a few seconds the window of the fillsProcess looks unresponsive since this method runs on the UI thread..
EDIT The 2nd:
Apparently (and really quite understandably)
gridViewToCopy.CopyToClipboard();
Is not the only method causing this problem. Many Devex methods have to run on UI thread (e.g. Sorting of data, Filtering of data)
So thanks to anyone who offered specific solution (that either worked or didn't) but my original question pops right back up again:
Is there any way to change the time-out time or somehow control the whole "Not Responding" fiasco?
You can use DisableProcessWindowsGhosting win32 function:
[DllImport("user32.dll")]
public static extern void DisableProcessWindowsGhosting();
This actually doesn't prevent the window from freezing, but prevents the "Not Respongind" text in the title.
I am afraid the simplest solution is to make your own CopyToClipboard() where you in your for loop, every now and then, do an Application.DoEvents, which keeps the ui thread responsive.
I guess most licenses of DevExpress have the source code available, so you can probably copy paste most if it.
Since you know the data you can probably make a much simpler procedure than the generic that DevExpress uses.
like this:
const int feedbackinterval = 1000;
private void btnCopy_Click(object sender, EventArgs e)
{
StringBuilder txt2CB = new StringBuilder();
int[] rows = gridView1.GetSelectedRows();
if (rows == null) return;
for (int n = 0; n < rows.Length; n++)
{
if ((n % feedbackinterval) == 0) Application.DoEvents();
if (!gridView1.IsGroupRow(rows[n]))
{
var item = gridView1.GetRow(rows[n]) as vWorkOrder;
txt2CB.AppendLine(String.Format("{0}\t{1}\t{2}",
item.GroupCode, item.GroupDesc, item.note_no??0));
}
}
Clipboard.SetText(txt2CB.ToString());
}
This is because you call a long running method synchronously in your main application thread. As your applicaton is busy it does not respond to messages from windows and is marked as (Not Responding) until finished.
To handle this do your copying asynchronously e.g. using a Task as one simplest solution.
Task task = new Task(() =>
{
gridView.Enabled = false;
gridView.CopyToClipboard();
gridView.Enabled = true;
});
task.Start();
Disable your grid so nobody can change values in the GUI.
The rest of your application remains responsive (may has side effects!).
You could start the process hidden and then check if responding and bring it back into view when complete....your splash screen would show its still "responding".
Process proc = new Process();
proc.StartInfo.FileName = "<Your Program>.exe"
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Edit:
You could also create a Timer event watching the other process and roll your own timeout logic
DateTime dStartTime = DateTime.Now;
TimeSpan span = new TimeSpan(0, 0, 0);
int timeout = 30; //30 seconds
private void timer1_Tick(Object myObject, EventArgs myEventArgs)
{
while (span.Seconds < timeout)
{
Process[] processList = Process.GetProcessesByName("<YourProcess.exe>");
if (processList.Length == 0)
{
//process completed
timer1.Stop();
break;
}
span = DateTime.Now.Subtract(dStartTime);
}
if (span.Seconds > timeout)
{
Process[] processList = Process.GetProcessesByName("<YourProcess.exe>");
//Give it one last chance to complete
if (processList.Length != 0)
{
//process not completed
foreach (Process p in processList)
{
p.Kill();
}
}
timer1.Stop();
}
}
Edit2
You could use pInvoke "ShowWindow" to accomplish hiding and showing the window too after its started
private const int SW_HIDE = 0x00;
private const int SW_SHOW = 0x05;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
There are several possible approaches
Hide the main form for the period of the operation
Somehow clone/serialize the control and pass it to a thread with another UI dispatcher
Get the selected cells via gridView.GetSelectedCells() and then put their contents to the clipboard asynchronously
It would be more helpful if you've uploaded the GridView library somewhere, so that we could look inside.
I'm unclear whether your user needs to see the screen that is "unresponsive". If it unnecessary, you might try letting the application run in the background after the main thread for this app is closed; or you may minimize the app.
If it is necessary to see the application and for it to appear to be working, can you segment your "copy to clipboard" function such that is is threaded and takes in either an array or the gridview and an index range. The advantage to this is that your main thread on your subordinate process would never hang. The disadvantage is that people don't like to work with threading and delegates in C#.
Okay, the 'Not Responding' and window artifacting you've described are just symptoms of running a long term activity on your UI thread. The UI thread is blocked, so the UI is frozen. There is no avoiding this. To be honest, it's just 'lucky' that your application appears as responsive as it does.
As far as I can see every workaround that has been described here is just a hack to fudge the fact that your UI thread is frozen. Don't do this. Fix your program so the UI thread isn't frozen.
Ask yourself: do my users really need to copy all the rows from this view? Can the data be filtered in some way to limit the rows? If not, there is a property called MaxRowCopyCount which limits the number of rows that are copied - can this be leveraged without breaking your workflow?
Finally, if all else fails, is there some other medium you can use (perhaps an intermediate file), to which data can be copied on a background thread?
The timeout, documented in IsHungAppWindow, cannot be changed. Don't use global state to manage a local problem.
You have to optimize the part that causes unresponsiveness. For example use caching, virtual grid (DevExpress calls it "server mode"), paging, delegate the sorting to an ibindinglistview filter that does a database query (uses database index) instead of in-memory sorting (no indexing) or implement IAsyncOperation on your clipboard data so you would only need to populate the data when the user does a paste.
Im working on a wpf application using blend 4.
under certain conditions, i need to restart the app.
Im currently using the following code:
System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
System.Threading.Thread.Sleep(1000);
Application.Current.Shutdown();
which works.
My problem is that the current instance closes before the new one is loaded, making it look like the program crashed. I used the thread.sleep to stall the shutdoen, but the timing is different.
Is there any way to wait for the new process to start before shutting down the current one?
something along the lines of:
System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
if (newProcess.IsLoaded == true)
{
Application.Current.Shutdown();
}
Edit:
The context of this being when settings are changed, i need to restart the application to apply the changes, and I would use a splash screen to say (applying new settings please wait) and this would display until the new process is loaded
What about that: Pass old process id as a start parameter to new instance and let new instance to kill old one when it's loaded.
Use Process.GetCurrentProcess method to read old instance pid. Pass the parameter to new instance using Arguments property in ProcessStartInfo. Then use Process.GetProcessById in new instance to get and kill old instance when the argument is passed.
Simply call WaitForInputIdle on the newly created Process:
Process p = Process.Start(...);
p.WaitForInputIdle();
Application.Current.MainWindow.Close(); // perhaps better than Shutdown
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Process[] myProcess = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
foreach (Process process in myProcess)
{
if (process.Id != Process.GetCurrentProcess().Id)
{
process.Kill();
}
}
}
I work on a application where i want to find out which files on my filesystem is used by a Process.
After trying around with the System.Diagnostics.Process class, and didn´t get the resulst i wanted i find the application called OpenedFileView from Nirsoft.
http://www.nirsoft.net/utils/opened_files_view.html
Basically it does exactly what i want but i have some problems with the implimication in my project.
The option wich “OpenedFileView” gives you is to start it with some arguments that it creates you an txt file with all the information i want.
But for my case i want to whach the processes in realtime, and if i start the application repetitively i always have the hourglass at my mouse cursor.
So after this i tryed some ways to get rid of it, tryed out to put it in a BackgroundWorker Thread. But this changed nothing at all. I also looked for a way to force the Process not to exit, and sending new arguments to it, but this also didn´t worked.
So is there any way to use this application in the way I want, or does this didn´t work at all?
I hope somebody can help me either with getting away this annoying mouse cursor hourglass, or with a better implimication of this application so i can use it in realtime!
Thanks alot!
private void start()
{
_openedFileView = new Process();
_openedFileView.StartInfo.FileName = "pathToApp\\OpenedFilesView.exe";
_openedFileView.EnableRaisingEvents = true;
_openedFileView.Exited += new EventHandler(myProcess_Exited);
_openedFileView.StartInfo.Arguments = "/scomma pathToOutputFile";
_openedFileView.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
start();
}
You'll have to tweak your "real-time" requirement. A process can open and close hundreds of files in the time it takes to start that program. Given that you can never get a truly accurate view of file usage, you might as well reduce the rate at which you start the program. One immediate benefit is that you'll greatly reduce the CPU load.
A better mousetrap is the Sysinternals' Handle utility. It's a much lighter weight program. And is a console mode app, you can redirect its output so you don't have to use a file. And there's no wait cursor. It also allows selecting a specific process with the -p command line argument, an option you really want to use to keep execution time down to a reasonable level. This sample code worked pretty well:
using System;
using System.Diagnostics;
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
for (int ix = 0; ix < 100; ++ix) {
var psi = new ProcessStartInfo(#"c:\bin\handle.exe", #"-p consoleapplication1");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
var prc = Process.Start(psi);
var txt = prc.StandardOutput.ReadToEnd();
Console.WriteLine(txt);
System.Threading.Thread.Sleep(1000);
}
Console.ReadLine();
}
}
}
Oh, and resist the temptation to use its "close file" option. That's only good if you are looking for a way to permanently destroy data on your hard drive.
I'm using the data ready events of the Process class to get information from the standard output and standard error of a running process.
It works great on the first run, but after calling Stop() then Start() to force a restart of the application, I no longer recieve data. I've tried CancelErrorRead() but no luck there.
I'm considering just re-instantiating the object every time I need to re-run the app, but it seems silly to need to do that.
Any advice on how to re-use a Process object to restart a stopped process?
Relevant code chunks:
Constructor:
ProcessStartInfo objStartInfo = new ProcessStartInfo();
objStartInfo.CreateNoWindow = true;
objStartInfo.RedirectStandardInput = true;
objStartInfo.RedirectStandardOutput = true;
objStartInfo.RedirectStandardError = true;
objStartInfo.UseShellExecute = false;
objClient = new Process();
objClient.StartInfo = objStartInfo;
objClient.EnableRaisingEvents = true;
objClient.OutputDataReceived += new DataReceivedEventHandler(read);
objClient.ErrorDataReceived += new DataReceivedEventHandler(error);
Start:
objClient.StartInfo.FileName = strAppPath;
objClient.StartInfo.Arguments = strArgs;
start();
objClient.BeginErrorReadLine();
objClient.BeginOutputReadLine();
Stop:
objClient.Close();
objClient.CancelErrorRead();
objClient.CancelOutputRead();
Your Process object is not associated with a process until you call Start() (or use one of the static methods off Process). A stopped/closed process is functionally the same as no process at all. Given that, it's hard to believe there's any overhead to creating a new Process object, when compared to the (relatively enormous) cost of creating processes on Windows. Just create new Process objects as needed.
According to msdn you should call BeginOutputReadLine and BeginErrorReadLine to enable asynchronous reads from StandardOutput or StandardError using events.
Have a look at the remarks section on
BeginOutputReadLine
I want to kill a process programmatically in vista/windows 7 (I'm not sure if there's significant problems in the implementation of the UAC between the two to make a difference).
Right now, my code looks like:
if(killProcess){
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
myProcess = System.Diagnostics.Process.Start(psi);
}
I have to do this because I need to make sure that if the user crashes the program or exits abruptly, this secondary process is restarted when the application is restarted, or if the user wants to change the parameters for this secondary process.
The code works fine in XP, but fails in Windows 7 (and I assume in Vista) with an 'access is denied' message. From what the Almighty Google has told me, I need to run my killing program as administrator to get around this problem, but that's just weak sauce. The other potential answer is to use LinkDemand, but I don't understand the msdn page for LinkDemand as it pertains to processes.
I could move the code into a thread, but that has a whole host of other difficulties inherent to it that I really don't want to discover.
You are correct in that it's because you don't have administrative priveleges. You can solve this by installing a service under the local system user and running a custom command against it as needed.
In your windows form app:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "SERVICE_NAME");
scp.Assert();
System.ServiceProcess.ServiceController serviceCon = new System.ServiceProcess.ServiceController("SERVICE_NAME", Environment.MachineName);
serviceCon.ExecuteCommand((int)SimpleServiceCustomCommands.KillProcess);
myProcess = System.Diagnostics.Process.Start(psi);
In your service:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
protected override void OnCustomCommand(int command)
{
switch (command)
{
case (int)SimpleServiceCustomCommands.KillProcess:
if(killProcess)
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
}
break;
default:
break;
}
}
I'll add the code for Simon Buchan's suggestion. It makes sense and should work as well, assuming your windows form is what launched the process in the first place.
Here's where you create the process. Notice the variable myProc. That's your handle on it:
System.Diagnostics.Process myProc = new System.Diagnostics.Process();
myProc.EnableRaisingEvents=false;
myProc.StartInfo.FileName="PATH_TO_EXE";
myProc.Start();
Later, just kill it with:
myProc.Kill();