Commands have many other attributes and methods that are helpful. For example, we
can take the messages written to standard out by the process by accessing the
StandardOutput field of our object. We can also view the state of the
command, for example, if the command has finished running, by calling querying
HasExecuted.
Command object
Any Command that uses our library is a subclass of, and must extend,
our Command class. Command classes are tagged with the
[CommandSyntax] attribute which contains the syntax of the command
without arguments. All arguments are tagged with the [ParameterSyntax]
attribute, containing the syntax for a particular parameter of the command.
As a simple example, we can create a Command called NetstatCommand
object for the windows netstat command. First, we must tag the class
definition with the attribute [CommandSyntax("netstat")]. Secondly,
we can implement the parameters of the netstat command. If we wanted
to implement the -o flag, we can create a boolean field
and tag it with ParameterSyntax("-o")].
Next, we can implement the interval parameter. Since this is a numeric
value, the field of our NetstatCommand class would be an int.
This is also a user-defined value, and not a flag, so we must tag the field with
the attribute [CommandSyntax("{arg}")]. The CommandWrap library will
take the user-defined argument, and substitute it in for {arg}. The
library also handles many other types, as well as Collections, which
is explained in greater detail below.
namespace CommandWrapExamples
{
using Crabwise.CommandWrap;
[CommandSyntax("netstat")]
public class NetstatCommand : Command
{
[ParameterSyntax("-o")]
public bool? ShowOwningProcess { get; set; }
[ParameterSyntax("{arg}")]
public int? Interval { get; set; }
}
}
Now that we have a basic command class created, let's use it. Create an instance
of the NetstatCommand class. We can run a process using this command
object by calling its Execute() method inherited from its super class.
Commands have many other attributes and methods that are helpful. For example, we
can take the messages written to standard out by the process by accessing the
StandardOutput field of our object. We can also view the state of the
command, for example, if the command has finished running, by querying HasExecuted.
public void RunBasicExample()
{
NetstatCommand netcmd = new NetstatCommand();
netcmd.ShowOwningProcess = true;
int ret = netcmd.Execute();
Console.WriteLine(netcmd.StandardOutput);
Console.WriteLine("Has cmd executed?: " + netcmd.HasExecuted);
Console.ReadLine();
}
Not all processes can be run from any location on the machine without special parameters,
and sometimes we want to launch a process with more specific information, for example,
a specific location. Much of this can be done by creating a CommandStartInfo
object.
Let's create a new class, CalcCommand, that simply defines the CommandSyntax
attribute as calc.exe. We can try to create this object and run it,
but chances are that we have to specify the location of calc.exe.
namespace CommandWrapExamples
{
using Crabwise.CommandWrap;
[CommandSyntax("calc.exe")]
public class CalcCommand : Command
{
}
}
Now we can create a new CommandStartInfo object before our instance
of CalcCommand. One of the many fields of this object is Path,
which we can set to be the location of calc.exe, %windir%\system32\.
Now, we can call the Execute() method on our instance of CalcCommand,
giving it our instance of CommandStartInfo.
public void RunCalcExample()
{
CommandStartInfo calcInfo = new CommandStartInfo();
calcInfo.Path = "%windir%\\system32\\";
CalcCommand calccmd = new CalcCommand();
calccmd.Execute(calcInfo);
}
{arg}
As mentioned before, the argument of a command parameter may be anything from a
boolean to a collection of strings. Let's create a subclass
of Command called ZipCommand, which could be used with
an imaginary zip process. Our imaginary process has the following syntax: zip
files -- [files], where [files] is a list of file paths
to zip. This parameter is required.
To do this, we can instantiate a collection of strings in the class,
allowing the user to add files however they see fit in other modules. We can then
make a parameter for our Command called Files of type Collection<String>.
For this, we would tag the parameter with the following attribute: [ParameterSyntax("files
-- {arg}", Required = true)]. This will allow access to the underlying
collection of strings, and then when the Command is executed,
it will replace {args} in the Files parameter with the
contents of the collection.
class ZipCommand : Command
{
Collection<string> filesToZip;
[ParameterSyntax("files -- {arg}", Required = true)]
public Collection<string> Files
{
get { return this.filesToZip; }
}
}
To continue with ZipCommand, let's imagine that the process also requires
a destination path for the resulting zip. This turns the syntax to zip files --
[files] dest -- [dest]. The destination parameter must appear after the
list of files to zip.
The CommandWrap library allows us to specify a position of the parameters
for any particular Command, the first parameter being 0. Parameters
with the same position value will appear in an arbitrary order.
To do this, all that needs to be done is set the Position value (this
value is set at default to 0). Since our dest parameter must appear
after the files parameter, let's set the dest parameter's
position to 1 by tagging it with the following attribute: [ParameterSyntax("dest
-- {arg}", Required = true, Position = 1)].
class ZipCommand : Command
{
Collection<string> filesToZip;
[ParameterSyntax("files -- {arg}", Required = true)]
public Collection<string> Files
{
get { return this.filesToZip; }
}
[ParameterSyntax("dest -- {arg}", Required = true, Position = 1)]
public string Destination { get; set; }
}