When start another process using Process() class, newly created process is still associated with the one invoked the start command, which is in turn causing issues with monitoring software or even within the stirted application itself.
So the question is if it is possible to start an external binary detachad from the caller, as a fully standalone instance? (Like started directly)
Update:
There are some processes expected to be a subprocesses of a specific process.
Like IE usually a subprocess of explorer.exe.
But started from C# app.exe with Process().start(#"path/to/iexplere.exe") it is a subprocess of app.exe
So is it possible to attach it to expected explorer.exe?
The most common approach is to start an intermediate process (let's call it the child) that in turn starts the binary you want to detach (the grandchild).
The intermediate process is then terminated, leaving the grandchild without direct parents.
You are not showing any code, so we can't help you more than that. You can start from here.
I've found a workaround for it.
It includes two applications. Lets call them handle.exe and detacher.exe
handle code:
static void Main(string[] args)
{
Process.Start(AppDomain.CurrentDomain.BaseDirectory + "detacher.exe");
Console.ReadLine();
}
detacher code:
static void Main(string[] args)
{
Process.Start("IExplore.exe", "google.com");
}
handle starts detacher and proceeds.
detacher starts IE and terminates.
IE drops to process root and proceeds.
Related
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.
I wrote a main program in C#, and I worte also a small tool program also in C#.
I want that the tool program will be able to execute under some conditions:
When called from my main program.
When called from open some suffix of file (e.g. "*.abc")
But if the user open my program directory he can run the tool program, and I do not want him to be able to do it. I want him to be able to run the program under the conditions above. And if he ran the program manually, the program automatically shut.
Is there any way to do that?
To check if your main program called it, you could pass the main program's ProcessID as a commandline argument, then in your small program, check if that ProcessID exists and if its process name is the name of your main program. This isn't spoof-proof, but might be a bit trickier to fake than just passing a static number/string.
In addition, you could encrypt the number and pass that, then decrypt it and check the above. It's pretty much impossible to prevent a determined hacker from running your program on its own, but you can raise the bar of how tricky it is to do it. You'd also want to obfuscate your code, otherwise a quick Reflector call will show exactly what characters are being passed.
Alternatively, if possible, you could just make the small program a DLL and call it from your main program like that. This would need a bit of refactoring, but would force your program to be open. As for opening a *.abc file, your program can check the command line arguments to see if a filename was passed through. This can then be processed automatically by your app and the DLL calls can be made.
There is nothing exposed to the process/executing environment that tells it how it was invoked, so there is no foolproof way to do this.
You can have your main program pass in a flag on the command line - this and the suffix are things that you can check for and if either one does not exist you terminate immediately.
However, if the user ever guesses the flag, they can still call the application directly.
Other mechanisms could involve writing a value to a file from your main program just before invoking the second program and checking that file from your second program (and deleting it after execution), as a messaging mechanism. There are other messaging mechanisms that would do similar things (private MSMQ queues for instance).
You can use a named mutex with some hard-to-guess name to make sure that the "small tool" program was called by the main program. So, you create a Mutex in your parent program:
bool requestInitialOwnership = true;
bool mutexWasCreated;
Mutex m = new Mutex(requestInitialOwnership,
"MyMutex",
out mutexWasCreated);
Then in your child program you write the exact same code to check if the parent program executed it. If mutexWasCreated==true, then it was not called by the parent program. Otherwise, it was.
Checking the arguments passed to the program will help to determine whether the filename's extension is .abs or not.
in my application I've registered a file type (.asm) with my application (it's a tabbed notepad application), and when those files are double-clicked they open with my application through the arguments passed when it's loaded:
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Main(args));
}
Now the problem is, while this does work, if one instance of my application is already running, whenever a file is opened, a new instance of it is created rather than a new tab being opened in the current instance which I don't want. So I thought of checking if the program is already running, and if it is then I would call a separate function in the main form to load that document instead. But the problem is, I don't know how you call a function in Main.cs from Program.cs, how do we do that?
It's more complicated than just calling a function in Main.cs from Program.cs because the OS will launch a second process for you at the time you double-click your registered files. You need some way to find out if there is another existing process running already and then communicate with that existing process, if there is one.
Fortunately, there is a class in the .NET Framework that already does all of the hard work for you:
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
See this blog for a complete example:
http://windowsclient.net/blogs/suryahg/archive/2008/08/20/use-of-microsoft-visualbasic-applicationservices-part-2.aspx
The technique in this question might help:
C# : how to - single instance application that accepts new parameters?
We have a product that is a C# console app. Is it possible to restrict it to run from the command line only? In other words, users wouldn't be able to call it from a script or another app.
If it is, some example code would be much appreciated. Thanks.
You can check the process that created your application using the code given here: http://msdn.microsoft.com/en-us/netframework/aa569609.aspx#Question3 . To be started at the DOS command line the parent process should be cmd.exe. Note however as pointed out by Colin this can be bypassed easily using a batch script. You can disable that as well by making sure that the command prompt arguments to cmd.exe are null. For this you will need to use WMI :
http://skysanders.net/subtext/archive/2010/04/11/using-wmi-to-fetch-the-command-line-that-started-all.aspx
You should also check the cmd.exe image is from system32 folder.
I don't think it is possible to tell the difference.
Certainly the parent process is not a useful indicator. This is what you get in the parent process:
1. type app name into Command Prompt: cmd.exe
2. call app from batch script: cmd.exe
3. Double click on app or shortcut: explorer.exe
4. type app name into Run dialog box: explorer.exe
If you intend for 1. to be a valid way to start your program, then I don't think you can stop 2. which means your app can be called from any script or any program (since it's simple for another program to create a 1 line batch script and execute it)
(BTW, does anyone know a way to get a table on StackOverflow?)
#swisston if you start your console application from your another own application, than i want to recommend you "named kernel objects". For example mutex. You can create named mutex in your parent app. Then in main thread of your child console app try to open this mutex. If mutex not opened (not found): console app has no permissions to continue and must be closed;) wait, i'll make some code for you;)
Edit:
So it is very easy tactics. In parent app create your named mutex:
Mutex mutex = new Mutex(true, "MyPermissions");
Then in your child console application check if your mutex exists:
static bool CheckPermissions()
{
try
{
Mutex mutex = Mutex.OpenExisting("MyPermissions");
}
catch (Exception ex)
{
return false;
}
return true;
}
If your console application was run without your parent application CheckPermissions method will return false and console must be closed;)
I don't agree with what you're trying to do, but here is an idea that could work: require some sort of user input at the beginning of the program, maybe some sort of CAPTCHA (difficult to do in command line, but theoretically possible. Think ASCII art).
I am trying to create an app that will perform actions on specific times (much like the Windows Task Scheduler). I am currently using Process.Start() to launch the file (or exe) required by the task.
I am initiating a process by calling a file (an .mp3) and the process starts WMP (since it is the default application). So far so good. Now I want to kill that process. I know that it is normal behavior for the Process.Start(string, string) to return nothing (null in C#) in this case.
So I am asking how can I close WMP when I called it through Process.Start(string, string)??
Edit:
Please note that I am not opening WMP directly with Process.Start() and this is the line with which I run the process:
VB: Me._procs.Add(Process.Start(Me._procInfo))
C#: this._procs.Add(Process.Start(this._procInfo))
_procInfo is a ProcessStartInfo instance. _procInfo.FileName is "C:\route\myFile.mp3". That is why WMP opens. In any case, all of the Start() methods, except for the instance-one which returns a boolean, return nothing (null in C#), because WMP is not the process that was directly created (please note that WMP is run and the song does play).
Process.Start(string,string) returns you a Process resource that you can use to further control the new process.
Process newProcess = Process.Start("param1", "param2");
if (newProcess != null && !newProcess.HasExited)
newProcess.Kill();
The same structure works if you use Process.Start(string), or any other static Process.Start overload.
Process.Start() is a member function and associates a new or reused Process with the Process component identified by this. Behaviour of this method depends on the properties of the Process identified by this.
Don't do it this way.
It's not clear whether the intent of your program is 'Always launch with Windows Media Player' or 'Launch with the registered MP3 player', which might be, say, iTunes.
If you need WMP, use Process.Start with the full path to windows media player.
If you need the registed MP3 player, you can find out the correct exe using the code shown here. Again, start the process with this exe path, passing the MP3 as a parameter.
Two ways:
1-
Process customProc = Process.Start("ExecutablePath", "Argument(s)");
customProc.Kill()
2-
Dim pProcess() As Process = System.Diagnostics.Process.GetProcessesByName("ProcessName")
For Each p As Process In pProcess
p.Kill()
Next
If you are letting the registered windows program to open the file, rather than picking the program you want. Then I advise you do not kill the process.
The reason for this is what say your program does use the default application, but that application is already in use, and contains unsaved data. A user would not be happy for your program to overtake there application with the new file and then kill off the process that was already in use by the user for another purpose. Sure, it might not be in use but you must consider the worst case.
As such, I recommend what has been suggested. use Process.Start() with the full path to the program to be used and the file to be opened.
I tried to open .txt file and the process of my text editor was returned, also I tried .mp3 by WMP and it returned null. So it depends on the application. Do you need to run you mp3 only with WMP? If not, you definitely can create the application which will return the Process object.
proc = Process.Start(filename) should work, but like you say, it returns null instead of a process.
That seems to be inherent to Windows Media Player. Other applications return the process. You can get Windows Media Player's process by specifying the application in the start method.
proc = Process.Start("C:\Program Files\Windows Media Player\wmplayer.exe", filename)
Then you can kill it normally.
proc.Kill()
You will probably need to get the location of the application assiciated with .mp3 files from the registry.