I'm building an IronPython module that initializes an instance of AutoCAD and I need to return 1 to the module calling it after AutoCAD has finished initializing, entered its message loop and is in a stable (not loading dependencies/anything) state. I've tried using System.Diagnostics.Process.WaitForInputIdle() with no luck.
Here's what I have so far:
import System.Diagnostics as sysdiag
def start_autocad(self):
print("\"C:\\Program Files\\Autodesk\\Autodesk AutoCAD Civil 3D 2014\\acad.exe\" /b \"C:\\Temp\\C3DAutoScript.scr\"")
for process in sysdiag.Process.GetProcessesByName("acad"):
process.Kill()
try:
acad_process = sysdiag.Process.Start("C:\\Program Files\\Autodesk\\Autodesk AutoCAD Civil 3D 2014\\acad.exe", " /b \"C:\\Temp\\C3DAutoScript.scr\"")
acad_process.WaitForInputIdle()
return 1
except:
return 0
Unfortunately, this function as it stands returns as soon as the process begins opening, not after it's done. Does anyone know a way to handle this either in classic cPython, IronPython or C# (without using an overkill sleep function to wait for it)?
A process can start processing messages almost immediately after start up. There is no requirement that the main window is displayed before this is done. This can be done to avoid being displayed as hanging, in case loading is slow.
You can either try interacting with the process after WaitForInputIdle returns — it may eventually respond even while "loading". If it doesn't work, wait for the main window to appear (using FindWindow). If the application is COM server, you try establishing COM connection.
P.S. Killing processes is not the best idea and may result in corrupted data or configuration. Try closing application properly be sending close event to its main window.
In the title of your question you ask for "a process". In the text of your question you ask specifically for Autocad.
I can tell you how to do it for a process in general, not specifically for Autocad.
I had the same problem and came to the solution to use the API
GetModuleFileNameEx(HANDLE h_Process, ....)
MSDN says:
If the module list in the target process is corrupted or is not yet
initialized, or if the module list changes during the function call as
a result of DLLs being loaded or unloaded, GetModuleFileNameEx may
fail or return incorrect information.
And in deed when you try to get the executable path of the process with this function while the process is still loading it's DLL's the function fails and GetLastError() returns ERROR_INVALID_HANDLE. This does not mean that the process handle that you pass to the function is invalid. But this is the error code you get when the process is still starting up.
I tested it with several applications. It works perfectly.
int WaitForProcess(HANDLE h_Process, int Timeout)
{
for (int T=0; T<=Timeout; T+=50)
{
if (GetModuleFileNameEx(h_Process, NULL, ...) > 0)
return 0;
int Err = GetLastError();
if (Err != ERROR_INVALID_HANDLE) // = 6
return Err;
Sleep(50);
}
return ERROR_TIMEOUT;
}
Why does it work? What GetModuleFileNameEx() does internally is to read the memory of the process (for that you must open the process with access permission PROCESS_VM_READ). But this is not allowed while the process is in the loader lock. The loader lock is active while a process is loading DLL's.
This code is general and works for any application. It waits until the application is ready with it's basic initialization. If this is not enough for you I recommend to wait for the main window of the application to appear.
Related
From MSDN:
The return value true indicates that a new process resource was
started. If the process resource specified by the FileName member of
the StartInfo property is already running on the computer, no
additional process resource is started. Instead, the running process
resource is reused and false is returned.
Trying something like this:
var info = new ProcessStartInfo {FileName = #"CMD"};
var p1 = new Process
{
StartInfo = info
};
var result = p1.Start(); //true
result = p1.Start(); //true
var p2 = new Process
{
StartInfo = info
};
result = p2.Start(); //true
Have the same result if I'm using FilePath = #"c:\myapp.exe" instead of CMD.
In what cases does it return false?
If you take a look at the reference source, you'll see how Process.Start works:
http://referencesource.microsoft.com/System/R/c50d8ac0eb7bc0d6.html
That is one of two methods called when you call Process.Start. Notice near the bottom where it returns the value true or false. False is only returned if, after starting the process, it cannot obtain the handle for the process that was started.
In order to start the process, it uses NativeMethods.CreateProcess which you can find the source of here: http://referencesource.microsoft.com/System/compmod/microsoft/win32/NativeMethods.cs.html#9c52a5ca5f3eeea3
Which is just the method prototype for Kernel32.CreateProcess, which the API is found here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
If you look at the return values, it says:
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
I can't find anything in the API for CreateProcess that says it fails if the requested process is already running, perhaps if the process failed to start because it is a single instance application (like Outlook) then it may fail, but for multiple instance applications like the command prompt, it shouldn't fail to create a handle to the process.
So, after saying all that, it is possible that the MSDN documentation is not entirely correct, I don't have the link you have, but for the Process.Start(StartInfo), MSDN says this about the return value:
A new Process that is associated with the process resource, or null if no process resource is started. Note that a new process that’s started alongside already running instances of the same process will be independent from the others. In addition, Start may return a non-null Process with its HasExited property already set to true. In this case, the started process may have activated an existing instance of itself and then exited.
(Emphasis added by me). It doesn't say the call will fail if its already running.
For Process.Start(), it says:
Return Value
Type: System.Boolean
true if a process resource is started; false if no new process resource is started (for example, if an existing process is reused).
So it says if an existing process is reused, this is entirely up to the application being started or the method of starting it.
You can technically get a false return when you use ProcessStartInfo.UseShellExecute = true (the default) and you launch the process by passing a document filename. And the shell is somehow able to figure out to pass the document open request to an already running instance of the process.
The only documented case of this is opening a web page in Internet Explorer. There might be other ones, probably having something to do with legacy DDE activation. That's a guess.
This is otherwise a specific case of a general problem with Process.Start(), there are lots of single-instance apps around. The Office apps as the most common example. The most typical behavior you'd observe is that the process very quickly terminates again. It just detected that the app was already running and used process-interop to ask the running instance to open the document. The kind of feature also supported in .NET.
You'll have no idea which specific process is showing the document unless you know it is a single-instance app and its process name so you have some hope of finding it back with Process.GetProcessesByName(). This is however not fail-safe, there might be an unrelated process running that happens to have the same name. Advantage of getting false is that you know there's no point in waiting for it to terminate.
I have a this code that calls a MATLAB function (displayFigure.m) from a C# client. Each time I update the .m file, the changes are not reflected when the C# program is rerun.
public void CallMLfunc(){
MLApp.MLApp matlab = new MLApp.MLApp();
matlab.Feval("displayFigure", 0, out result, input);
matlab.Quit();
}
Tried to terminate the MATLAB.exe process using
matlab.execute("quit;");
and will get the following an unhandled exception: System.Runtime.InteropServices.COMException.
After force quitting the program I rerun it again an displayFigure.m (version 2) is reflected. Is there another to quit the MATLAB process?
There's a MATLAB.exe in the process list for the COM object. Sometimes there's a visible window you can close. Sometimes there isn't. You may have more than one MATLAB.exe, especially if you are debugging and the MATLAB editor is open, you'll have that instance running as well. The matlab.Quit(); should terminate the MATLAB.exe associated with the COM object, but if there is an Exception during the matlab.Feval(), or breakpoint, etc., it might not execute. When I have that problem, I terminate the MATLAB.exe with the highest PID.
I am working on method in a DLL. In my method I have been writing to the event log to allow me to determine whether things are working as I expect. One of the tasks I do in the method is create a process and run a command line application and I want to be able to capture the output of this that is normally written to the console and write it to the eventlog.
Code:
Process getNextIons = new Process();
getNextIons.StartInfo.FileName = #"""C:\Program Files\OpenMS-1.6\PrecursorIonSelector.exe""";
getNextIons.StartInfo.Arguments = #"-ini ""C:\Program Files\OpenMS-1.6\precursorionselector.ini""";
getNextIons.StartInfo.UseShellExecute = false;
getNextIons.StartInfo.RedirectStandardOutput = true;
getNextIons.Start();
getNextIons.WaitForExit();
System.Diagnostics.EventLog.WriteEntry("FMANWiff", "IPS: " + getNextIons.StandardOutput.ReadToEnd());
I have a console test application that calls my method and when I do this I am able to see that the process was started and ran correctly, however when I actually try to make use of the DLL, not using the test application, all I end up seeing is an entry:
IPS: And none of the output. I can tell it is running, however, as I can see a number of output files being updated.
Why am I not getting any output and how I can rectify this?
According to the Process.StandardOutput documentation your code has a potential deadlock.
When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits on the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits on the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait on each other to complete an operation, and neither can proceed.
To avoid the possible deadlock, the last two lines of your sample code should be switched. You should also consider redirecting StandardError.
In your library, instead of writing directly to the location of choice you should be using
System.Diagnostics.Trace. By using Trace you can have your outside console application or whatever it be subscribe to the Trace event using a TraceListener.
By using Trace and TraceListeners, you can make you application adaptable to whatever logging situation you require without having to modify your library every time you want to change the logging system.
Below is a link to another Stack Overflow thread about trace logging with some good examples and information.
How can I add (simple) tracing in C#?
I am trying to run a JSFL script from within a C# project, where the JSFL script opens up a .fla file, modifies it n times, and exports n .swf files before closing. From the C# project, I am starting up the JSFL script through the Process class. I try to wait for the JSFL process to finish through the myProcess.WaitForExit() command, but it doesn't work. The remainder of my code C# executes before the JSFL process finishes exporting its .swf files. Is there any way I can throw something that the C# project can catch from the JSFL file or some other solution?
One solution (although most likely not the best one) would be for your C# code to look for the SWF files being created. Only once they've been updated/created will the JSFL script have finished, so you know that it will be safe for your C# to move on. In case there is no way of knowing how many SWF files the C# program needs to look for, you could instead let the JSFL script write to a log file of some sort, and to have the C# program read that log on a interval, looking for some sort of token to indicate that the JSFL script has completed it's task.
Granted, this may be far from the best method (and I don't know C# well enough to know whether what you're seeing is by design or a sign of something being wrong). But it may just be the case that running JSFL/Flash is completely asynchronous and if so, maybe the log file approach is the quickest path to solving the problem.
I have the same problem with another application that calls an external JSFL script.
What I do is write a file once finished to a target directory, and in the calling application, poll for that file. As soon as the file appears, I know the task has finished, and I delete the temp file, and carry on. Oh - and before starting I look for and delete any temp files so I don't get a false positive.
It's not so nice as events, but you have to work with what you've got.
Have you tried to set a custom Listener that will execute a function when the JSFL done. Don't forget that it's still based on ECMA which is a procedual language.
By the way, JSFL has a LOW-LEVEL C API.
C LEVEL API
This is absolutely possible, and I've already posted a solution here on stack overflow, complete with a detailed problem description and all the C# and JSFL source code necessary to implement it: Automating publishing of FLA files; calling Process.Start multiple times
To summarize... first of all, waiting on the JSFL script process is useless, because you're actually calling Flash.exe, which will remain open/running after the JSFL completes, so you'd be waiting on a process exit event that will never occur.
The trick is to use a JSFL function at the end of the JSFL script which executes a windows command line command, and it's that call which will signal your C# app that the JSLF script has completed. So basically you'll have your main instance of your C# app, but you want to modify your C# app so that a 2nd instance can be run from a command line with a particular switch. When you're C# app is called with a particular switch (from the JSLF file), then instead of running normally, you want your program to simply signal a wait handle in the main instance of your application and then close. I use a simple third-party open-source library called XDMessaging, which facilitates such inter-process communication. It lets you open a named channel in your C# app, which can receive a command from the 2nd instance, signaling the main instance that the JSFL script has finished and it's safe to continue.
But involving file watcher like this is not the best solution so I catch the Flash process and keep watch on the process title and tell the JSFL render some window title for the flash window after finish the execution.
I have some UI application that lives in the user's task bar that is written in C#. The EXE for the tool is checked in to our source control system on a number of projects that use it so we are able to update the version they run with by checking in updated EXE.
The problem is that when the users get the latest revision of the exe, the program is often running, and the sync fails on their machine. I want to fix it so the program doesn't lock the exe and any dependent DLL's when it runs so they can sync without having to shut down the program.
Currently, I have a program that takes an executable as a parameter and will launch it from memory by reading the assembly contents into memory ahead of time. Unfortunetly, this totally fails when it comes to the DLL's that the program requires.
The code I have right now looks something like this:
public class ExecuteFromMemory
{
public static void Main(string[] args)
{
//Figure out the name of the EXE to launch and the arguments to forward to it
string fileName = args[0];
string[] realArgs = new string[args.Length - 1];
Array.Copy(args, 1, realArgs, 0, args.Length - 1);
//Read the assembly from the disk
byte[] binary = File.ReadAllBytes(fileName);
//Execute the loaded assembly using reflection
Assembly memoryAssembly = null;
try
{
memoryAssembly = Assembly.Load(binary);
}
catch (Exception ex)
{
//Print error message and exit
}
MethodInfo method = memoryAssembly.EntryPoint;
if (method != null && method.IsStatic)
{
try
{
method.Invoke(null, new object[] { realArgs });
}
catch(Exception ex)
{
//Print error message and exit
}
}
else
{
//Print error message and exit
}
}
}
My question is, am I doing something totally stupid? Is there a better way to handle this? If not, what should I do to support handling external dependencies?
For example, the above code fails to load any dependent files if you try to run 'Foo.exe' that uses functions from 'Bar.dll', the 'Foo.exe' will be overwriteable, but 'Bar.dll' is still locked and can't be overwritten.
I tried getting the list of referenced assemblies from the 'GetReferencedAssemblies()' method on the loaded assmebly, but that doesn't seem to give any indication where the assemblies should be loaded from... Do I need to search for them myself? If so, what's the best way to do this?
It seems like other people might have come across this before, and I don't want to re-invent the wheel.
-
Update:
The EXE is checked in because thats how we distribute our in-house tools to the teams that use them. Its not optimal for this use-case, but I don't have the opportunity to change that policy.
Disclaimer: I don't use Windows, though I am familiar with its strange way of locking things.
In order to update your application while it is running, you'll likely need to have two processes: The executable itself, and an update “helper” application that will finish the update process. Let's say that your application is ProcessA.exe and your update helper is Updater.exe. Your main program will download a new copy of the executable, saving it with a random name. Then you run your updater program, which watches for the termination of your current process. When your process terminates, it displays a quick window showing the status of the update, moving the new executable into the place of the old one, and then restarting that program.
It'd be more elegant to be able to emulate POSIX filesystem semantics and be able to delete the currently-running process disk image and replace it with a new file, but I don't know if that is even possible on Windows. On a POSIX system, you can delete an in-use file and it won't actually be deleted until any remaining file handles are closed, though you can then reuse the filename.
You might want to check out an article written at CodeProject that talks about this. It also has a follow-up article.
Good luck!
Does the program need to keep running while updating?
Typically to update a program which is running you would copy over any of the files that are to be replaced to a temporary folder. Then shut down the old instance, delete it and move the new files over to the correct locations then re-launch it.
This allows for minimal down time of the application since the longest part is usually the copy and the file move is very fast if the temporary folder is on the same logical drive.
Although Michael's answer is one way of doing this, there are tools out there that are explicitly for managing what's installed on the desktop.
What you are doing with the exe being checked into source control is not normal. If you have a windows domain controller, you can use Group Policy to push programs down to the client. Alternatively, you could use something like Altiris to handle it.
If you must continue the way you are going then you have two options. One, using a helper / loader app which does a version check on launch. This is similar to how firefox works.
The second way is to build a helper service that sits in memory and polls every so often for updates. This is how Google Chrome, Adobe, etc work.