I have an Windows Form application that supplies the User Name, Domain, and Password to the StartInfo, and it throws this:
System.ComponentModel.Win32Exception: The handle is invalid
at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
When I allow the credentials to default to current user I get no such error, and the process I start works to the extent that it doesn't need to use credentials (the creds are necessary for mapping a drive in an MSBuild script). Here's the code that fills the start info:
Process p = new Process();
ProcessStartInfo si = new ProcessStartInfo(buildApp, buildArgs);
si.WorkingDirectory = msBuildWorkingDir;
si.UserName = txtUserName.Text;
char[] psw = txtPassword.Text.ToCharArray();
SecureString ss = new SecureString();
for (int x = 0; x < psw.Length; x++)
{
ss.AppendChar(psw[x]);
}
si.Password = ss;
si.Domain = "ABC";
si.RedirectStandardOutput = true;
si.UseShellExecute = false;
si.WorkingDirectory = txtWorkingDir.Text;
p.StartInfo = si;
p.Start();
It isn't that the user/psw isn't matching, because when I provide a bad psw, for example, it catches it. So, this "invalid handle" thing is happening after the cred is passed. Any ideas on what I might be omitting or screwing up?
You have to redirect your Input, Error, and Output.
for example:
ProcessStartInfo info = new ProcessStartInfo("cmd.exe");
info.UseShellExecute = false;
info.RedirectStandardInput = true;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
info.UserName = dialog.User;
using (Process install = Process.Start(info)) {
string output = install.StandardOutput.ReadToEnd();
install.WaitForExit();
// Do something with you output data
Console.WriteLine(output);
}
Also microsoft has said the error should read, "Unable to redirect input." (used to have a link, but that no longer worked)
Related
I'm working with LibGit2Sharp to add a number of Git operations to an application. I've added the Microsoft.Alm.Authentication to help with Authentication and credential manager access. It works great for retrieving credentials that are already entered from the command line.
However is there any way to also hook into the Credential Manager's Login UI that prompts for username and password for Github, BitBucket and VSTS. This UI pops up automatically from the command line, but doesn't fire when using LibGit2Sharp.
I've looked at the GitCredentialManager project on Github and I can see the components that provide the UI, but before trying to figure out how to hook those in explicitly, is there some way I'm missing that this is provided as part of the Microsoft.Alm.Authentication (or related package)? Or can anybody point to an example or guidance on how to best hook this up?
I managed to get your solution working with some minor modifications. I paste here a sample code to push using git credentials. It works using the credentials already stored in the computer and prompts for credentials the first time with UI.
Only problem I'm having so far is when user is prompt for credentials and they enter an invalid user/password. Git writes to the console asking for user/pass and the process is not finished until you input that. Tried to monitor StandardError/Output with no success. I get the error text in stderror but only after filling that manually.
public void PushLibGit2Sharp(string repositoryFolder, string branch)
{
using (var repo = new Repository(repositoryFolder))
{
var options = new PushOptions
{
CredentialsProvider = (url, usernameFromUrl, types) =>
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "git.exe",
Arguments = "credential fill",
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process process = new Process
{
StartInfo = startInfo
};
process.Start();
// Write query to stdin.
// For stdin to work we need to send \n instead of WriteLine
// We need to send empty line at the end
var uri = new Uri(url);
process.StandardInput.NewLine = "\n";
process.StandardInput.WriteLine($"protocol={uri.Scheme}");
process.StandardInput.WriteLine($"host={uri.Host}");
process.StandardInput.WriteLine($"path={uri.AbsolutePath}");
process.StandardInput.WriteLine();
// Get user/pass from stdout
string username = null;
string password = null;
string line;
while ((line = process.StandardOutput.ReadLine()) != null)
{
string[] details = line.Split('=');
if (details[0] == "username")
{
username = details[1];
}
else if (details[0] == "password")
{
password = details[1];
}
}
return new UsernamePasswordCredentials()
{
Username = username,
Password = password
};
}
};
repo.Network.Push(repo.Branches[branch], options);
}
}
Unfortunately, there's no functionality in libgit2 (or LibGit2Sharp) to talk directly to the git-credential-helper functionality, which is what git itself uses to perform this action.
Instead, you can set a CredentialsHandler on your PushOptions (or FetchOptions), eg:
options.CredentialsProvider = (url, usernameFromUrl, types) => {
string username, password;
Uri uri = new Uri(url);
string hostname = uri.Host;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.FileName = "git.exe";
startInfo.Arguments = "credential fill";
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.StandardInput.WriteLine("hostname={0}", hostname);
process.StandardInput.WriteLine("username={0}", usernameFromUrl);
while ((line = process.StandardOutput.ReadLine()) != null)
{
string[] details = line.Split('=', 2);
if (details[0] == "username")
{
username = details[1];
}
else if (details[0] == "password")
{
password = details[1];
}
}
return new UsernamePasswordCredentials(username, password);
};
I have spent all day looking for an answer but without luck.
I need to be able to disable the Password Complexity in the Local Security Policy on a stand-alone Windows 7 PC.
I have tried scripting with secedit.exe.
I have also messed a bit around with C#.
The end result shall be a script/program which will disable the policy and then create a new user account locally.
After some extended research I found out how to do it.
Posting the code here in case someone else needs to use it.
string tempFile = Path.GetTempFileName();
Process p = new Process();
p.StartInfo.FileName = Environment.ExpandEnvironmentVariables(#"%SystemRoot%\system32\secedit.exe");
p.StartInfo.Arguments = String.Format("/export /cfg \"{0}\" /quiet", tempFile);
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
StringBuilder newCfg = new StringBuilder();
string[] cfg = File.ReadAllLines(tempFile);
foreach (string line in cfg)
{
if (line.Contains("PasswordComplexity"))
{
newCfg.AppendLine(line.Replace("1", "0"));
continue;
}
newCfg.AppendLine(line);
}
File.WriteAllText(tempFile, newCfg.ToString());
Process p2 = new Process();
p2.StartInfo.FileName = Environment.ExpandEnvironmentVariables(#"%SystemRoot%\system32\secedit.exe");
p2.StartInfo.Arguments = String.Format("/configure /db secedit.sdb /cfg \"{0}\" /quiet", tempFile);
p2.StartInfo.CreateNoWindow = true;
p2.StartInfo.UseShellExecute = false;
p2.Start();
p2.WaitForExit();
I have a batch file containing the following commands:
cd C:\myfolder
NuGet Update -self
NuGet pack mypackage.nuspec
myfolder contains mypackage.nuspec and NuGet.exe. I try to run this command with C# using the following function:
private static int ExecuteCommand(string path)
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo(path);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
ProcessInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true";
// *** Redirect the output ***
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
Process = Process.Start(ProcessInfo);
Process.WaitForExit();
// *** Read the streams ***
string output = Process.StandardOutput.ReadToEnd();
string error = Process.StandardError.ReadToEnd();
int ExitCode = Process.ExitCode;
Process.Close();
return ExitCode;
}
However, my commands are not executed. What is causing this behavior and what is the solution? Those strings will probably be used in the future, I'll update my question then (just to prevent chriticism :)).
This is the final version of the function:
private static ShellCommandReturn ExecuteCommand(string path)
{
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo(path);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
processInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true";
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
int exitCode = process.ExitCode;
process.Close();
return new ShellCommandReturn { Error = error, ExitCode = exitCode, Output = output };
}
ShellCommandReturn is a simple custom class with a few data members where error, output and exit code of a shell command are stored.
Thanks.
EDIT: After a certain amount of collaboration :)
The problem is that this is executing in the context of a web application, which doesn't have the same environment variables set.
Apparently setting:
startInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true"
(using the naming of my final code below) fixes the problem.
Old answer (still worth reading)
Look at this code:
ProcessInfo = new ProcessStartInfo(path);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = true;
ProcessInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
Process = Process.Start(path);
You're creating a ProcessStartInfo, but then completely ignoring it. You should be passing it into Process.Start. You should also rename your variables. Conventionally local variables start with lower case in C#. Additionally, it's a good idea to initialize variables at the point of first use, where possible. Oh, and import namespaces so you don't fully qualified names such as System.IO.FileInfo in your code. Finally, object initializers are useful for classes like ProcessStartInfo:
var startInfo = new ProcessStartInfo(path) {
CreateNoWindow = false,
UseShellExecute = true,
WorkingDirectory = new FileInfo(path).DirectoryName;
};
var process = Process.Start(startInfo);
I have following code
using (StreamWriter outfile = new StreamWriter(#"f:\trial.cpp"))
{
outfile.Write(txtCode.InnerText);
}
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(#"cl.exe", #" 'trial.cpp'");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.UserName = "asdasd";
SecureString secureString = new SecureString();
foreach (char c in "abcded")
{
secureString.AppendChar(c);
}
procStartInfo.Password = secureString;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
procStartInfo.WorkingDirectory = #"f:\";
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
How to pass file name as parameter? Above code doesn't run and I have tried all full path, different path options.
can anyone help?
The argument is set incorrectly. You have:
var procStartInfo = new ProcessStartInfo(#"cl.exe", #" 'trial.cpp'");
Where there are spaces and single quotes in the name. Try:
var procStartInfo = new ProcessStartInfo(#"cl.exe", #"trial.cpp");
EDIT:
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "CL.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "trial.cpp";
try
{
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// error handling
}
The point here is that CL is a command line executable, not a windows GUI application.
http://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx
http://msdn.microsoft.com/en-us/library/kezkeayy.aspx
http://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx
If the cl.exe is not in the system PATH (which by default it is not) then the start process will not find the executable and it will fail to run.
So I suspect you are seeing the fact that the cl.exe is not in the system PATH.
When I run the following from cmd it runs ok
>mysql -h 134.86.157.132 -u sas vm < c:\vm.sql
When I try to do the same from code it does not work
ProcessStartInfo info = new ProcessStartInfo("mysql");
info.Arguments = #"-h 134.86.157.132 -u sas vm < c:\vm.sql";
info.Domain = "134.86.157.132";
info.UserName = "sas";
info.Arguments = #"vm < c:\vm.sql";
info.UseShellExecute = false;
Process.Start(info);
What am I doing wrong here? It does not work and I get some wrong password exception?
Edit:
I run it like this now
ProcessStartInfo info = new ProcessStartInfo("mysql");
info.UseShellExecute = false;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.Arguments = #"-h 134.86.157.132 -u sas vm < c:\vm.sql";
Process.Start(info);
and get following error The system cannot find the file specified
"-h 134.86.157.132 -u sas vm" are the arguments, don't use domain and username.
You also need to redirect the standard input stream to pass your vm.sql file in. See this example: ProcessStartInfo.RedirectStandardInput Property
Try it like this:
ProcessStartInfo info = new ProcessStartInfo("mysql");
info.Arguments = #"-h 134.86.157.132 -u sas vm < c:\vm.sql";
info.UseShellExecute = false;
Process.Start(info);
You're setting UseShellExecute to false, but it's the shell that interprets the "<" part to redirect stdin from a file.
Unless there's a specific reason why you need UseShellExecute to be false, set it to true. Alternatively, redirect standard input for the new process, and give it the data directly.
EDIT: As specified in other answers, keep the original arguments, and don't set Domain/UserName on the ProcessStartInfo. Those refer to Windows usernames and domains, not MySQL ones.
At that point you'll be able to set UseShellExecute to true with no ill effects, with any luck.
How about something like from this post:
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = new StreamWriter(p.StandardInput))
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("mysql -h 134.86.157.132 -u sas vm < c:\vm.sql");
}
}