In a project I'm currently working on, I am starting an external process. However, the external process is the EXE of a complex program which loads current-user information from a user folder. The desktop shortcut for the program resolves the matter by setting the "Target:" parameter to X:\exepath\prgm.exe and setting the "Start In" parameter to the user's path, X:\exepath\users\username.
I currently launch the process like this:
Process p = new Process();
p.StartInfo = new ProcessStartInfo( "X:\exepath\prgm.exe" );
p.StartInfo.WorkingDirectory = "X:\exepath\users\username";
p.Start();
while (!p.HasExited) { }
However, when the process is started, the program it launches ends up looking for all resources in the WorkingDirectory instead of pulling user content from that folder and all other content from the directory the EXE resides in. This suggests that Working Directory and the system shortcut "Start In:" parameter behave differently.
Is there any way to mimic that behavior with a C# Process? Alternatively, is it possible to create a shortcut in C#, which I could then start with my Process invocation?
Please let me know if more info would be helpful.
EDIT -
After some more trial and error, I decided to use WSH to create a shortcut and run it. WSH uses the name WorkingDirectory for the value of the "Start In:" parameter. It behaves identically under the hood as the execution of the process in my code above. I am still getting the error.
The difference is likely due to using a Shell Process to execute: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute.aspx
The WorkingDirectory property behaves
differently when UseShellExecute is
true than when UseShellExecute is
false. When UseShellExecute is true,
the WorkingDirectory property
specifies the location of the
executable. If WorkingDirectory is an
empty string, the current directory is
understood to contain the executable.
When UseShellExecute is false, the
WorkingDirectory property is not used
to find the executable. Instead, it is
used by the process that is started
and has meaning only within the
context of the new process.
I suspect if you set p.StartInfo.UseShellExecute to false it may behave as you want.
I have resolved my problem, which was not related to the creation of a Process after all. In fact, the root cause is a little embarrassing, but potentially educational, so I'll provide an explanation.
The code I posted in the OP was sample code to illustrate the problem. In my actual project, I was retrieving the ExePath and the UserPath from registry keys. The project is a Chooser tool to switch between multiple installed versions of the third-party software, and reads/edits these registry keys to do its work.
When I wrote the code that writes to the registry, I used DirectoryInfo.FullPath, which returned "X:\ExePath" instead of "X:\ExePath\". This made the program unable to find the files it needed from the ExePath folder, looking for X:\ExePathsettings.inf" instead of "X:\ExePath\settings.inf". I inserted trailing backslashes to my code and to the existing registry entires, and everything worked just fine.
Lesson learned: Always check your path values very, very carefully.
Related
I got PythonApplication1.py into the Sources of the C# Windows Form Application. On specific condition I write it into the Program Data folder this way:
File.WriteAllBytes(#"%ProgramData%\\Folder\\PythonApplication1.py", Resources.PythonApplication1);
and then with specific condition C# runs Python Application from program data folder, this way:
Process.Start(#"%ProgramData%\\Folder\\PythonApplication1.py");
On the side of Python code I got second thing, it writes some data into the text document, which is located by the same path, but creation of it happens in Python itself, but it can be done from C# code, no matter, here:
path = (#"%ProgramData%\Folder\doc.txt")
but python code must write in this file by same program data path:
data = open (r'doc.txt', 'w')
and to write, just by same location, as both are there:
with open(r'doc.txt') as my_file:
The problem is, when I run this python code as it shown above, this way:
Process.Start(#"%ProgramData%\\Folder\\PythonApplication1.py");
It does two wrong things, which does not happens, if it is not %ProgramData% directory and located with C# .exe in debug folder, just by Process.Start("PythonApplication1.py");, or if I run Python Code inside %ProgramData%\Folder path with doc.txt, just by hand. It creates and writes directly into the file, all is correct.
Otherwise if Process.Start(#"%ProgramData%\\Folder\\PythonApplication1.py"); I got second result, it does not writes data into the doc.txt by %ProgramData%\Folder and python code creates it out of program data folder in debug folder with C# .exe.
So question is how to create and write from python code into the doc.txt located by C# Application "%ProgramData%\Folder" path.
Seems like, if PythonApplication1.py is exist inside ProgramData:
File.WriteAllBytes(#"%ProgramData%\\TAOZ\\PythonApplication1.py", Resources.PythonApplication1);
And if executed by hand it creates text document and writes to it, it must do the with Process.Start, but not.
also I tried to use star info, but not sure if this is correct, anyway I got same result:
ProcessStartInfo startInfo = new ProcessStartInfo(#"%ProgramData%\\folder\\PythonApplication1.py");
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
Process.Start(startInfo);
So seems like I need two things here. To expand the search environment variable %ProgramData%. and also need to use the start up info to tell the process to start on that particular folder. Otherwise, the current working folder will be the same folder as application I guess, but not sure how to do it, and if it is reason of problem, need your help.
Since it's a python program, you'll have to point to the python EXE. Assuming you have the pythonpath environment variable, this ought to work:
ProcessStartInfo startInfo = new ProcessStartInfo(Environment.ExpandEnvironmentVariables(#"%PYTHONPATH%\python.exe"), Environment.ExpandEnvironmentVariables(#"%ProgramData%\folder\PythonApplication1.py"));
startInfo.WorkingDirectory = Environment.ExpandEnvironmentVariables(#"%ProgramData%\folder\");
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
Process.Start(startInfo);
I would like to know if there are other alternatives to capturing a process output in C#.
I have the following code
Process __process = new Process();
__process.StartInfo.Verb = "runas";
__process.StartInfo.FileName = "some file path here";
__process.StartInfo.UseShellExecute = true;
__process.Start();
__process.WaitForExit(300000);
The reason why UseShellExecute is set to true here is because that's required for the verb "runas" to run as administrator. As a result, I cannot redirect the standard output of the program anymore.
What are other ways that I can capture the output of Process if I can't use StandardOutput? I thought about making the program write to a file and then read from that file but that doesn't seem very efficient.
If you can code the actual program you're starting the process to use pipes that would probably be the best / most efficient solution. There might be a way to have the process output piped by some other command/program.
Pipe Operations in the .Net Framework
You could create a named or anonymous pipe and pass it's name/id/handle to the program as an argument. The program can then connect to the other end of the pipe and stream data back to your application that called it.
EDIT I'm assuming this is all running on a Windows environment, and I don't know if using pipes on a process started with UseShellExecute = true works..
I'm trying to run an external problem from C# by using Process.Start, but am running into permissions issues. When I open a command prompt normally (not as an admin) and run my commands they work fine, but when I open a command prompt via Process.Start, I get a write error on the directory. ("I can't write on file test.log")
If I run it as an admin via Process.Start it works fine, but I get the permissions popup. Does anyone have any ideas that might help me figure this out? Thanks!
Here is the code I'm using:
Process proc = new Process();
proc.StartInfo.FileName = #"cmd.exe";
proc.StartInfo.Arguments = #"/k latex C:\Users\Shane\Documents\test.tex";
proc.Start();
proc.WaitForExit();
I wonder whether it's trying to write a diagnostic log to the current working directory, which you may not have permissions for. (I don't know offhand whether it will inherit the working directory, or be the directory that contains cmd.exe.) I suggest you specify the working directory for the new process using ProcessStartInfo.WorkingDirectory.
(As an aside, I personally find it cleaner to create a new ProcessStartInfo an populate that - C# object initializers make this particularly nice) and then call Process.Start(ProcessStartInfo) to start it. Otherwise it looks like there's already a process when there isn't really one yet. Just MHO though, and unrelated to the problem you're investigating, probably.)
Instead of using cmd.exe as a FileName property of Process object, keep your commands in one batch file and then use that file for execution.
Also you can mention administrator's privilages like username, password, domain etc via StartInfo property of Process class. If you use these properties I think permission problem will not come. Here you can find more information about StartInfo property.
Hope it helps.
I’m trying to launch an application (Operating System, My Application and the application I want to launch are all 32 bits), from .NET 3.51.
The code that launches the Process is used for other applications, but there’s one that is giving us a headache. If we “double click” on the application’s icon, it works as expected, meaning that it works fine as an application in the computer. Double clicking the .exe directly, also works.
The operating system is Windows 7 32Bits (Home and/or Professional).
Our .NET application is compiled with x86 to avoid problems.
The code that launches “Processes” is located inside a DLL (also 32 bits) made by us, basically it’s a simple DLL that holds some “Common Code” across the board, common methods, functions and stuff we use throughout our code. One of those methods look like this:
public static bool FireUpProcess( Process process, string path, bool enableRaisingEvents,
ProcessWindowStyle windowStyle, string arguments )
{
if ( process != null )
{
try
{
process.StartInfo.FileName = #path;
if ( arguments != null )
{
if ( arguments != String.Empty )
{
process.StartInfo.Arguments = arguments;
}
}
process.StartInfo.WindowStyle = windowStyle;
process.EnableRaisingEvents = enableRaisingEvents;
process.Start();
}
catch
{
try
{
process.Kill();
}
catch ( InvalidOperationException )
{
} // The process is not even created
return false;
}
}
else
{
return false;
}
return true;
}
I don’t know who wrote this method, but it has been working for roughly six years with different applications, therefore I assume it’s “ok”. However, we have a customer with a piece of software that won’t launch when passed through that argument.
The arguments are:
process is a System.Diagnostics.Process created with a simple "new Process();”
path is a full path to the .exe “c:/path/to/my.exe”.
enableRaisingEvents is false
windowStyle is Maximized (but have tried others).
It gives a crappy MessageBox… which I have happily immortalized. It’s in spanish but the translation ought to be easy:
It says:
Application Error
An unexpected exception has occurred for the program (0x0eedfade) at …
Googling that 0x0eedfade gives strange results that look scary, but the truth is, if I go to the .exe that I’m trying to launch and double click it, it works perfectly.
For The Record: If I try to launch other things (I.e.: Notepad.exe, Adobe Acrobat Reader) it works, but Firefox doesn’t open and doesn’t show an error.
This “some work, some doesn’t” behavior leads me to believe that there might be a problem with a Windows 7 security mechanism or similar that I don’t know.
What am I missing or doing wrong?
UPDATE: Ok; I’ve gotten a copy of the software. It’s a messy software but it works. Now that I can debug, I see that the program gives an error when launched with my FireUpProcess method.
As suggested I added the WorkingDirectory code, but here’s the code:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
if (process != null)
{
try
{
if ( !String.IsNullOrEmpty(#path) )
{
process.StartInfo.FileName = #path;
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
process.StartInfo.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); // HERE The program reports the following:
That means, “The program could not be started because ddip.dll is missing… try reinstalling bla bla”.
The thing is, if I execute the same #path from the command line, the program opens perfectly:
That opens the program. And the same happens if I click on the “shortcut” that it’s located in the “programs” menu. There aren’t any parameters in that shortcut, it’s a simple call to the executable file.
So the question is now: What is the difference between my code and the other methods?
There has got to be something different that causes my process not to start.
Any ideas?
UPDATE AND SOLUTION
I made it work by using one of the below provided answers. Turns out that none directly pointed me to the solution, but they all gave me good ideas here and there.
I added an app manifest to our application (should have had it since the age of vista, don’t know why it wasn’t there in the 1st place). The app manifest I added by using VStudio 2008 add file -> app manifest.
In it, I made sure we have this:
<requestedExecutionLevel level=“asInvoker” uiAccess=“false” />
We don’t need admin or anything like that, but apparently Vista/7 need to know it.
After that was added, the process is correctly launched.
note: UseShellExecute is true by default (as suggested by some), you have to explicitly turn it to false if that’s what you want.
You are not setting the process.StartInfo.WorkingDirectory property. There's plenty of poorly written software out there that assumes the working directory will be the directory in which the EXE is stored. At least add this line:
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
The exception is however rather strange. I'd definitely recommend you tell the customer to update their anti-malware tools.
If the exe has a manifest, you should set UseShellExecute to true on the process object before you call Start. It's not a bad idea in any case.
As Kate Gregory pointed out, if you want to "emulate" the user double clicking on the icon, you have to set UseShellExecute to true. Setting this flags make the code use a totally different path, using the underlying windows ShellExecute function.
Now, I will add to this, that if you're running on a UAC-equipped Windows (Vista, 7, 2008, ...) you maybe should also try to use the runas verb as explained here and here.
With .NET, that would be:
if (System.Environment.OSVersion.Version.Major >= 6) // UAC's around...
{
processStartInfo.Verb = "runas";
}
I've had similar problems in the past. I resolved it by executing the cmd app as follows:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
//if path contains " ", surround it with quotes.
//add /c and the path as parameters to the cmd process.
//Any other parameters can be added after the path.
ProcessStartInfo psi = new ProcessStartInfo("cmd", "/c" + path ));
psi.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
psi.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); ...}
If it is possible I would try to use Process Monitor from Sysinternals. When you start it up you can deselect Registry and Network Activity on the toolbar (the 5 icons on the right side). Then you only see Process and Disk activity. Since it looks like a file not found problem you should use the Filter dialog (6. icon from the left) select Process Name from the Drop down list (Architecture is the default) and enter your failing executable name. This will greatly limit the captured output so you can see what is going on. Then start the exectuable and check in the Result Column for NAME NOT FOUND result. This are the locations where a file was searched but not found. If you know the offending dll name you can search for it with Ctrl+F as usual to dig it out. Then you can compare the different search paths from your working application and when it was started from your application.
Could it be that the environment variable PATH has a different value inside your process? It could be that adding . (the current directory) helps to fix the dll search path. Or is the application started from a different user account? It could also be the new feature that when an application is installing things into Programm Files but has no rights (only administrator can do this) Windows will redirect the writes into the user profile. This is a secure and transparent way to get more secure. But this could cause e.g. during first application startup some e.g. config file to be deployed into the Administrators Profile when he is running the application not with consent from the UAC dialog. Then other users might also start the application but fail because the additional config file is located in the Administrators profile and not in Program Files as expected for everyone.
I believe Hans Passant is on the right track. In addition to what he said, check to ensure that ddip.dll and the exe are in the same directory. This is not always the case as there are other ways to bind assemblies outside the bin. Namely, the GAC and AssemblyResolve event. Considering your situation I see no reason the GAC is involved. Check the exe's code that is launched for any hooks into the AssemblyResolve event. If it's hooked into you may need to update the implementation to allow another process to launch it.
Because you are getting an exception regarding a missing DLL, I have little confidence in the answers regarding path delimiter issues. Nonetheless, you have the application code, so verify that it references ddip.dll. This will give you a good deal of confidence that you are in fact referencing the correct .exe and therefore it's not just a path delimiter problem with the command prompt (E.G. misinterpreted spaces).
I have an MSBuild task that executes (among other things) a call to xcopy. What I have found is that this call to xcopy executes correctly when I run my MSBuild task from a batch file, and fails to execute or produce any output that would allow me any idea what is going on when that same batch file is called from another C# application with a System.Diagnostics.Process.
Both processes are launched with more or less the same structure:
waitProc.StartInfo.Arguments = "/C [executable]";
waitProc.StartInfo.FileName = "cmd.exe";
waitProc.StartInfo.UseShellExecute = false;
Furthermore by changing the "UseShellExecute" from false to true on the xcopy command I can make this succeed in both use cases, however the command fails to run in a third use case. The third use case being our automated build system which is a windows service calling msbuild directly. In the case of the failure on our build machine the copy command hangs indefinitely which is, I believe, because the System.Diagnostics.Process tries to display a window, and services do not have a Windows desktop session associated with them, so they cannot display windows.
I have tried using the "CreateNoWindow" property, and I've tried setting the "WindowStyle" to "ProcessWindowStyle.Hidden," but that does not change the behavior on the build machine.
All of this said, what I really want to know is what exactly the UseShellExecute property does, because it seems to do a whole lot more than the MSDN documentation suggests.
Thanks.
ProcessStartInfo.UseShellExecute tells the Process to use the Windows Shell to execute the specified application.
Without this set, you can only execute an EXE file directly. By setting this, you allow the Windows Shell to be used, which allows things such as specifying a .doc file and having the associated program open the file.
However, using the Windows Shell requires a valid desktop context, which is why your third use case fails.
In general, using cmd.exe is problematic unless you're using the Windows Shell. You may want to just write the code to handle your "batch" operation directly - ie: use the methods from types in the System.IO namespace to do your copying. This would avoid this issue entirely.
From the Documentation:
Setting this property to false enables
you to redirect input, output, and
error streams.
Note: UseShellExecute must be false
if the UserName property is not a null
reference (Nothing in Visual Basic) or
an empty string, or an
InvalidOperationException will be
thrown when the
Process.Start(ProcessStartInfo) method
is called. When you use the operating
system shell to start processes, you
can start any document (which is any
registered file type associated with
an executable that has a default open
action) and perform operations on the
file, such as printing, with the
Process component. When
UseShellExecute is false, you can
start only executables with the
Process component.
Note: UseShellExecute must be true if
you set the ErrorDialog property to
true. The WorkingDirectory property
behaves differently when
UseShellExecute is true than when
UseShellExecute is false. When
UseShellExecute is true, the
WorkingDirectory property specifies
the location of the executable. If
WorkingDirectory is an empty string,
the current directory is understood to
contain the executable.
When UseShellExecute is false, the
WorkingDirectory property is not used
to find the executable. Instead, it is
used by the process that is started
and has meaning only within the
context of the new process.
The use of 'UseShellExecute' IIRC, is to allow explorer (the main shell) to execute the process and not the .NET runtime....unless somebody corrects me that I'm wrong...
did you specify WorkingDirectory correctly? You can see actual output of your command by adding >c:\log.txt 2>c:\err.txt run it wil this addition and check those files