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.
Related
I'm trying to get the result, yes or no, that users press to answer a program when run as admin.
Example:
IMAGE
I'm trying with the following code:
var processInstall = new ProcessStartInfo();
processInstall.CreateNoWindow = true;
processInstall.FileName = "myBatchFileAddress";
processInstall.Verb = "runas";
var process = new Process();
process.StartInfo = processInstall;
process.Start();
process.WaitForExit();
//Here I must deal with the result of user, if is yes or no
Batch files are a very old technology. Literally as old as DOS. So by their nature, the ability to communicate is limited.
DOS era programms primarily communicated via "ERRORLEVEL". The return in Console programm still affects said errorlevel. 0 was returned for "no problem". Anything else for a problem. Batchfiles could check it. And I am 90% certain windows actually still uses it - if a setup returns anything but 0, it asks "should I try again with compatibility settings?" Using EXIT you can define wich ERRORLEVEL the batchfile "returns".
However there is another way to, depending on how verbose the programms are: IO redirection.
Keep in mind that batchfiles are interpreted. So you ahve to run the console programm, then give the order to run teh console (usually as a parameter with full path; see console documentation for details).
Finally executing any batchfile as adminsitrator tends to screw up the paths utterly. The batchfile needs to be designed to compensate.
Of course if those programms are written by yourself, you have options like Interprocess Communication. But I asume this was not the case right now.
Edit: It seems I missunderstood the question. If a programm asks for Adminsitrative privileges due to a Manifest or is run with runas but the elevation is denied, the execution is never done at all. The programm is not even started by Windows. It is Propably not even loaded into memory.
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've seen a very similar question asked before, here, however the answer thread fell apart. Basically, I installed Python (and checked the option to add to the Path variable), confirmed that it is indeed in the path variable (through the Environment Variables window like you would normally).
When opening a cmd window manually, I can type python -V and get the version back, and anything else really, and everything works fine, python is indeed exposed through the command prompt (when opened manually).
However, when I attempt to run a command through cmd.exe with a C# app I have, I get
'python' is not recognized as an internal or external command,
operable program or batch file.
The block of C# code I have
var proc = new Process();
var startInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = "cmd.exe",
Arguments = "/K " + command
};
proc.StartInfo = startInfo;
proc.Start();
This has worked fine in the past. However I had my work machine upgraded to Windows 10 and have been struggling to get this working.
The command text hasn't changed since the application was working before my windows upgrade, and if I take it's text and run it through a manually opened command prompt it executes fine with no issues. So I'm hesitant to believe it's an issue with the command itself.
UPDATE: If I run echo %PATH% in a regular prompt I see python, if I run it in the command prompt my application opens, I do not. I tried using set PATH, but that didn't help. Why is it that the PATH variable is different between a command prompt I open manually and one my application opens?
I thought it might have something to do with User and System variables having their own path, but Python is in both of them when checked via System Properties, so I'm at a loss.
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 question [stackoverflow] here.
I'm trying to do the above, but I want to take the process one step further. I want to open an arbitrary file using the default editor for the file type. From that point, I want to allow my user to interact with the file as they would normally, or continue to work in my application. The extension is what happens after the user finishes editing. Is there a way I can capture a close (and ideally save) event from the external application and use that as a trigger to do something else? For my purposes, tracking the closing of the external application would do.
I can do this in the specific case. For example, I can open a Word instance from my application and track the events that interest my application. However, I want to de-couple my application from Word.I want to allow my users to use any document editor of their choice, then manage the storage of the file being edited discretely behind the scenes.
You can do this in a manner similar to the referenced question, but the syntax is slightly different:
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo =
new System.Diagnostics.ProcessStartInfo("C:\...\...\myfile.html");
process.Start();
process.WaitForExit(); // this line is the key difference
The WaitForExit() call will block until the other application is closed. You would use this code in a separate thread so that the user can keep using your application in the meantime.
Use the FileSystemWatcher class to watch for changes to the file.
EDIT: You can also handle the Exited event of the Process object to find out when the program is exited. However, note that that won't tell you of the user closes your file but doesn't exit the process. (Which is especially likely in Word).
To listen for file change, you can use the FileSystemWatcher and listen for a change in the last modified date.
You can also monitor the process and check then file when the process close.
I found this useful tip online just now. It seems to be what you are looking for. This article (link broken) has some more detail and useful, to-the-point tips on C# programming.
string filename = "instruction.txt";
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(#filename);
System.Diagnostics.Process rfp = new System.Diagnostics.Process();
rfp = System.Diagnostics.Process.Start(psi);
rfp.WaitForExit(2000);
if (rfp.HasExited)
{
System.IO.File.Delete(filename);
}
//execute other code after the program has closed
MessageBox.ShowDialog("The program is done.");