How to display a multi-line output from cmd in c# - c#

I need my c# program to run a cmd query to show all sql services and then display them in a message box. In the situation I will be running this in, there will frequently be more than one and the names may be different each time.
"Wmic service where (PathName like '%Binn\sqlservr%') get caption, name" displays the information I need but there are multiple lines returned. the WriteNote() method is one that writes the information to a textbox in the program.
I have tried everything I can find on Google and nothing seems to be working.
private void DoListSQLServices()
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
//startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "Wmic service where (PathName like '%Binn\\sqlservr%') get caption, name";
process.StartInfo = startInfo;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
process.Start();
string output = process.StandardOutput.ReadToEnd();
this.WriteNote(output);
process.WaitForExit();
}
When I run this code I get this:
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
{file location of project}\bin\Debug>
and a pbd file is put in the debug folder.
What I want is for it to show the caption and name of the SQL services like if you run that command through cmd manually. Ultimately the user will type one of them in for the program to restart them but I would rather have the program display the list than have the user go to Services in windows and search for them manually.
Thanks in advance!

As mentioned by Noodles in the comments, it's much better to do this via the WMI support in .NET. One of the fairly direct methods for this is to use the ManagementObjectSearcher class to execute an ObjectQuery against the WMI data.
Create a new console application. Add a reference for the System.Management assembly to and add using System.Management; to the top of your program.cs file.
Now add the following code:
static void Main()
{
// Create a scope (connection to WMI)
var scope = new ManagementScope(#"\\localhost\root\cimv2");
// Create query
var query = new ObjectQuery(#"SELECT Name,Caption FROM Win32_Service WHERE PathName like '%Binn\\sqlserv%'");
// Create a search to run the query against the scope
using (var search = new ManagementObjectSearcher(scope, query))
{
// Iterate through the query results
foreach (var item in search.Get())
{
// get values, all strings in this case
string name = (string)item["Name"];
string caption = (string)item["Caption"];
Console.WriteLine("{0}\t{1}", name, caption);
}
}
}
Note that the ObjectQuery query syntax is a little different to the WMIC syntax. It's called WQL (WMI Query Language) and is heavily modelled on SQL.
Hopefully you can adapt the above for your own use fairly easily. Just be careful with the actual type of the fields you're fetching. Here's a list of properties for Win32_Service objects.

Related

Controlling 2nd cmd window - c#

I am trying to use DB2's Command Line processor. When using it on the command line, I enter: db2cmd then a 2nd window opens where I can connect to the database and submit queries. I am attempting to write a wrapper for the CLP and when I access the command line, it opens the DB2 CLP in a 2nd window and I can't figure out how to send arguements to the new window. Here is what I have:
static void Main(string[] args)
{
var p = new Process();
var info = new ProcessStartInfo();
info.FileName = "db2cmd";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
p.StartInfo = info;
p.Start();
using (var sw = p.StandardInput)
{
sw.WriteLine("DB2 CONNECT TO dbname USER \"username\" USING \"password\"");
sw.WriteLine("DB2 SELECT * FROM SPYPRD.CLMDTL FETCH FIRST 10 ROWS ONLY");
}
}
Maybe better to use an alternative interface (e.g. the ADO .net interface) from C# instead of using the CLP.
To use commands like LOAD and EXPORT etc, from ADO (or jdbc/odbc etc) you invoke them via stored procedure SYSPROC.ADMIN_CMD() if the Db2-server runs on Linux/Unix/Windows. Pay attention to the details in the documentation for that stored procedure because all file names are relative to the Db2-server (not the workstation).
Not sure if CLP accepts stdin redirection for passwords - you would also need to parse the stdout, maybe messy.
If the database is local then you can use the CLP without needing a userid/password on the CONNECT statement. If you wish to connect with different credentials use runAs to specify the credentials independently of the CLP, to avoid needing to send arguments to the CLP.
If the database is remote and is on Linux/Unix/Windows, best to use an alternative interface than CLP (e.g. ADO etc), or consider shipping the script to the remote-server to run locally on the Db2-LUW server as the required-user again avoiding passwords(e.g. if the Db2-server runs on Linux/Unix/Windows, use tools equivalent to psexec or ssh etc).

C# git command line process

Our organization utilizes VisualStudioOnline, GitHub and BitBucket for various repositories. I've been trying to figure out a way in c# to automate the pull of changes from various git repositories on a regular schedule. I've tried starting a process like this and then redirecting the standard input/output.
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\Git\bin\sh.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = "--login -i"
}
};
p.Start();
using (var so = p.StandardOutput)
{
using (var si = p.StandardInput)
{
if (si.BaseStream.CanWrite)
{
...
}
}
}
Inside of the block, I'm able to execute git commands like this:
si.WriteLine("cd \"P:/code/testapp\""); which navigates the git
shell to that directory
si.WriteLine("git rev-parse HEAD");
which will give me the latest hash of the repository at that location.
I can see the hash returned in the bash shell and can also read it in through the standard input like this var hash = so.ReadLine();
When I try to do si.WriteLint("git pull"); though, it asks for username and password, which I would expect, but that isn't done through the standard input/output. I can't us var prompt = so.ReadLine(); to get the prompt for username or password and I can't use si.WriteLine("username"); to input my password to the prompt.
I've tried a number of things to get this to work, but so far no luck.
WARNING: messy code ahead, just tried to quickly prototype something, not create a masterpiece
Attempt 1: used standard input/output redirects as shown above to try to write/read the prompts for username/password.
Attempt 2: attempt to get the ssh-agent process that is being utilized by the git bash and write to it
si.WriteLine("ssh-agent --help");
var sshAgentInfo = string.Empty;
var tmp = a.ReadLine();
while (a.Peek() != -1)
{
sshAgentInfo += tmp;
tmp = a.ReadLine();
}
var begPos = sshAgentInfo.IndexOf("SSH_AGENT_PID=", StringComparison.Ordinal);
begPos = begPos + "SSH_AGENT_PID=".Length;
var endPos = sshAgentInfo.IndexOf(";", begPos, StringComparison.Ordinal);
var processId = int.Parse(sshAgentInfo.Substring(begPos, endPos - begPos));
var sshProcess = Process.GetProcessById(processId);
sshProcess.StartInfo.RedirectStandardInput = true;
sshProcess.StartInfo.RedirectStandardInput = true;
using (var si1 = sshProcess.StandardInput) { ... }
Attempt 3: Utilize credential git config credential.helper wincred
This and four were very similar attempts. Again just trying to figure out how to set the password in either of these credential managers from the command line.
Attempt 4: Utilize Git-Credential-Manager-for-Windows
I've tried looking through the documentation here and it seems as though there is an enhancement request to do something along these lines.
Both of these seem to have similar problems to attempt one. The git shell seems to be invoking another process which handles the standard input and output separate from the git shell. The difference and hope I have for these last two though is, Is there a way to call into those credential managers directly to set username/passwords for different urls? In the .git directory, the config file has the credentials setting to tell it which manager to use and it stores the username and email in plain text.
If I invoke a git pull through the shell on the machine and enter my credentials once through that shell it seems to store them, but is there a way to allow users to enter their credentials through a website and then call into the manager through the command line to securely store that information for future automated use?
I also came across this. I haven't had a chance to try it yet, but is this a viable option for doing something like this?
Any help would be greatly appreciated.

My command which is being produced by my C# code isn't working in C#, but works perfectly when I paste it to cmd

It's all about a line I want to use to get windows update information, which is part of wmic.
My code looks like this:
Process p = new Process();
string arguments = "qfe list full /format:htable > "+ path;
ProcessStartInfo procStartInfo = new ProcessStartInfo("wmic", arguments);
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
procStartInfo.UseShellExecute = false;
p.StartInfo = procStartInfo;
p.Start();
while path is the valid location where the file would be dumped, ending with a hotfixlog.htm of course.
The problem is, that nothing happens at all. However, when I take the final product from the arguments-variable, and paste it manually into cmd with 'wmic < variablecontent >' it's working perfectly fine and I end up with the .htm I expect.
The line created looks like this:
"qfe list full /format:htable > C:\Users\...\WindowsHotfixes.htm"
What do I have to change to make it work from the code? I was expecting the backslashes to cause problems, but when manually entering the line they don't.
Your code will not work because the redirection operator (>) is not an element of the OS available to any application, but an operator in cmd.exe. It works in the command line because cmd is handling it, but wmic doesn't know what to do with it.
You can use the redirection if your command line is something like
cmd /c"wmic qfe list full /format:htable > x:\somewhere\file.htm"
Or you can remove the redirection and indicate to wmic that you want the data saved in a file
wmic /output:"x:\somewhere\file.htm" qfe list full /format:htable

Writing and executing multiple lines sequentially in an elevated command prompt using c#

Am a Newbie in C# and I have 3 commands(command2, command3 and command4) I need to execute in the elevated command prompt and I will also like to view the execution process as it happens. Currently, the problem is that the code below just opens the elevated command prompt and without executing the commands. I also seek better interpretations of the lines if wrong.
My code and Interpretation/Understanding of each line based on reviews of similar cases: ConsoleApp1
class Program
{
static void Main(string[] args)
{
string command2 = #"netsh wlan";
string command3 = #" set hostednetwork mode=true ssid=egghead key=beanhead keyusage=persistent";
string command4 = #" start hostednetwork";
string maincomm = command2.Replace(#"\", #"\\") + " " + command3.Replace(#"\", #"\\") ; //I merged commands 2 and 3
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd"; //Intend to open cmd. without this the newProcess hits an error saying - Cannot run process without a filename.
newstartInfo.Verb = "runas"; //Opens cmd in elevated mode
newstartInfo.Arguments = maincomm; //I intend to pass in the merged commands.
newstartInfo.UseShellExecute = true; //
newstartInfo.CreateNoWindow = true; // I intend to see the cmd window
Process newProcess = new Process(); //
newProcess.StartInfo = newstartInfo; //Assigns my newstartInfo to the process object that will execute
newProcess.Start(); // Begin process and Execute newstartInfo
newProcess.StartInfo.Arguments = command4; //I intend to overwrite the initial command argument hereby passing the another command to execute.
newProcess.WaitForExit(); //
}
}
This is what I did to overcome the challenge and It gave me exactly what I wanted. I modified my code to use the System.IO to write directly to the elevated command prompt.
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd";
newstartInfo.Verb = "runas";
newstartInfo.RedirectStandardInput = true;
newstartInfo.UseShellExecute = false; //The Process object must have the UseShellExecute property set to false in order to redirect IO streams.
Process newProcess = new Process();
newProcess.StartInfo = newstartInfo;
newProcess.Start();
StreamWriter write = newProcess.StandardInput ; //Using the Streamwriter to write to the elevated command prompt.
write.WriteLine(maincomm); //First command executes in elevated command prompt
write.WriteLine(command4); //Second command executes and Everything works fine
newProcess.WaitForExit();
Referrence: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardinput(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo(v=vs.110).aspx
I think an understanding of some properties of the ProcessStartInfo might clear things.
The verb - Gets or sets the verb to use when opening the application or document specified by the FileName property.,
+The UseShellExecute - Gets or sets a value indicating whether to use the operating system shell to start the process.
+The FileName - Gets or sets the application or document to start MSDN Docs
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, by using the Process object. When UseShellExecute is false, you can start only executables by using the Process object Documentation from MSDN.
In my case, cmd is an executable. the verb property is some thing that answers the question "How should my I run my FileName(for executables e.g cmd or any application)?" for which I answered - "runas" i.e run as administrator. When the FileName is a document (e.g `someFile.txt), the verb answers the question "What should I do with the file for which answer(verb) could be -"Edit","print" etc. also?"
use true if the shell should be used when starting the process; false if the process should be created directly from the executable file. The default is true MSDN Docs - UserShellInfo.
Another thing worth noting is knowing what you are trying to achieve. In my case, I want to be able to run commands via an executable(cmd prompt) with the same process - i.e starting the cmd as a process I can keep track of.

Reading MySql binlog

I don't agree that this question was answered effectively: decode mysqlbinlog in C#.
I have, what I think is the same question: I want to read the MySql binlogs from within a c# application, but do not know the format of the file. How can I properly parse these files' data?
First, what I learned:
Most of the source code files for MySql are installed along with the assemblies and generally located in [basedir]\include. For example, a typical install would place the files in Program Files\MySql\MySql 5.6\include.
mysqlbin.cc was NOT in that folder. However, I was easily able to get the file by doing a quick Google search. The file can be found here: https://code.google.com/p/mg-common-utils/source/browse/trunk/myreplicator/src/mysqlbinlog.cc?r=4 . It is well documented and easy to read.
Second, my solution:
As akuzminsky pointed out, the format of MySql's binlog is subject to change. However, the format returned from the mysqlbinlog.exe utility is consistent. This application is typically included in a MySql install and should be located in [basedir]\bin. I now run this application from within a c# Console Application and parse the results. I used the following steps to accomplish this:
Enabled binlogging on the MySql server from within the options file. In MySql Workbench, check 'log-bin' under the logging tab. Or, type 'log-bin=' in the settings file (often located in [basedir]. Might be called 'my.ini' or 'my.cnf' or something else. Generally, with .cnf or .ini extension). A filename is not required. When one is not specified, MySql will automatically create filenames for the logs. However, review MySql documentation on possible issues with this.
From within my client application, I query the server to get the path of each binary log (there could be many). To do this:
query show global variables like 'datadir' //returns the data directory.
query show binary logs //returns the filename of each binary log, along with its file size (helpful for reading).
Parsing these together gets the path for each binary log.
Since mysqlbinlog.exe is located in [basedir]\bin, I query the server to get the path of the base directory:
query show global variables like 'basedir'
Then, I parse the result with '\bin\mysqlbinlog.exe'
I use the Process class to create a new process, execute each binary log using mysqlbinlog.exe, and read each files results into a string variable:
private static string GetLogTexts(Liststring> logfilenames)
{
List<string> _logtexts = new List<string>();
string _basedir = GetBaseDir();
foreach(string logfilename in logfilenames)
{
Process proc = new Process();
proc.StartInfo.FileName = _basedir + "\\bin\\mysqlbinlog";
proc.StartInfo.Arguments = string.Format("\"{0}\"", logfile);
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
_logtexts.Add(proc.StandardOutput.ReadToEnd());
}
return _logtexts;
}
private static string GetBaseDir()
{
string path = "";
using (MySqlConnection conn = new MySqlConnection(RemoteServerConnectionString))
{
conn.Open();
using (MySqlCommand cmd1 = new MySqlCommand("show global variables like 'basedir'", conn))
{
using (MySqlDataReader reader = cmd1.ExecuteReader())
{
while (reader.Read())
{
path = reader.GetString(1);
}
}
}
}
return path;
}
Finally, I parse the results using my own logic (specific for what I am looking for). The results are very easy to read: mysqlbinlog uses regular line breaks and statements are terminated by a delimiter, which is defined prior to the statement (as usual, there can be multiple delimiters).
I hope this helps someone!

Categories