C# service onCustomCommand with parameters? - c#

I am currently trying to implement a service that runs a special command that does something with arguments passed, for example: file paths. I am using the cmd command:
sc control "ServiceName" 128
However, this command doesn't provide any way for me to input arguments. The method is as below:
protected override void OnCustomCommand(int command)
{
switch(command)
{
case 128:
Command.StartProcess(3);
LogWriter.WriteLog("Next output: ");
Command.StartProcess(4);
break;
case 129:
// input extension, output extension, key id , working path
try
{
test t1 = new test();
t1.readLog(#"C:\Users\Joe\Desktop\success.txt");
LogWriter.WriteLog(t1.input);
LogWriter.WriteLog(t1.output);
}
catch(Exception e)
{
LogWriter.WriteLog(e.ToString());
}
finally { LogWriter.WriteLog("abc"); }
//LogWriter.WriteLog(t1.output + "def");
break;
}
}
The only argument i can input is the int command for the method. I would like to input a folder path for t1.readLog();. In the above code, I have to hard code the path which isn't flexible and troublesome. So, is there anyway to work this out?

You are overriding the wrong method for this. Take a look at ServiceBase.OnStart.
It gets array of arguments that can be set in command line like this:
sc start MyService arg1 arg2
Parse args array as you want:
protected override void OnStart(string[] args)
{
var path = args[0];
var someParameter = args[1];
}
Process initialization arguments for the service in the OnStart
method, not in the Main method. The arguments in the args parameter
array can be set manually in the properties window for the service in
the Services console

Related

The term 'Get-WBSummary' is not recognized as a name of a cmdlet [duplicate]

I have below command and it returns me null object . When I run the command separately in PowerShell window I get the right result. Below is my PowerShell method which is calling the command and the also the PowerShell command which I have defined. I am basically looking to return a string value. Please let me know what wrong am I doing?
C# method:
public string RunScript( string contentScript, Dictionary<string, EntityProperty> parameters)
{
List<string> parameterList = new List<string>();
foreach( var item in parameters )
{
parameterList.Add( item.Value.ToString() );
}
using( PowerShell ps = PowerShell.Create() )
{
ps.AddScript( contentScript );
// in ContentScript I get "Get-RowAndPartitionKey" on debugging
ps.AddParameters( parameterList );//I get list of strings
IAsyncResult async = ps.BeginInvoke();
StringBuilder stringBuilder = new StringBuilder();
foreach( PSObject result in ps.EndInvoke( async ) )
// here i get result empty in ps.EndInvoke(async)
{
stringBuilder.AppendLine( result.ToString() );
}
return stringBuilder.ToString();
}
}
}
My Powershell GetRowAndPartitionKey cmdlet definition, which the code above is trying to call:
public abstract class GetRowAndPartitionKey : PSCmdlet
{
[Parameter]
public List<string> Properties { get; set; } = new List<string>();
}
[Cmdlet( VerbsCommon.Get, "RowAndPartitionKey" )]
public class GetRowAndPartitionKeyCmd : GetRowAndPartitionKey
{
protected override void ProcessRecord()
{
string rowKey = string.Join( "_", Properties );
string pKey = string.Empty;
WriteObject( new
{
RowKey = rowKey,
PartitionKey = pKey
} );
}
}
}
When using the PowerShell SDK, if you want to pass parameters to a single command with .AddParameter() / .AddParameters() / AddArgument(), use .AddCommand(), not .AddScript()
.AddScript() is for passing arbitrary pieces of PowerShell code that is executed as a script block to which the parameters added with .AddParameters() are passed.
That is, your invocation is equivalent to & { Get-RowAndPartitionKey } <your-parameters>, and as you can see, your Get-RowAndPartitionKey command therefore doesn't receive the parameter values.
See this answer or more information.
Note: As a prerequisite for calling your custom Get-RowAndPartitionKey cmdlet, you may have to explicitly import the module (DLL) that contains it, which you can do:
either: with a separate, synchronous Import-Module call executed beforehand (for simplicity, I'm using .AddArgument() here, with passes an argument positionally, which binds to the -Name parameter (which also accepts paths)):
ps.AddCommand("Import-Module").AddArgument(#"<your-module-path-here>").Invoke();
or: as part of a single (in this case asynchronous) invocation - note the required .AddStatement() call to separate the two commands:
IAsyncResult async =
ps.AddCommand("Import-Module").AddArgument(#"<your-module-path-here>")
.AddStatement()
.AddCommand("GetRowAndPartitionKey").AddParameter("Properties", parameterList)
.BeginInvoke();
"<your-module-path-here>" refers to the full file-system path of the module that contains the Get-RowAndPartitionKey cmdlet; depending on how that module is implemented, it is either a path to the module's directory, its .psd1 module manifest, or to its .dll, if it is a stand-alone assembly.
Alternative import method, using the PowerShell SDK's dedicated .ImportPSModule() method:
This method obviates the need for an in-session Import-Module call, but requires extra setup:
Create a default session state.
Call .ImportPSModule() on it to import the module.
Pass this session state to PowerShell.Create()
var iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { #"<your-module-path-here>" });
var ps = PowerShell.Create(iss);
// Now the PowerShell commands submitted to the `ps` instance
// will see the module's exported commands.
Caveat: A PowerShell instance reflects its initial session state in .Runspace.InitialSessionState, but as a conceptually read-only property; the tricky part is that it is technically still modifiable, so that mistaken attempts to modify it are quietly ignored rather than resulting in exceptions.
To troubleshoot these calls:
Check ps.HadErrors after .Invoke() / .EndInvoke() to see if the PowerShell commands reported any (non-terminating) errors.
Enumerate ps.Streams.Errors to inspect the specific errors that occurred.
See this answer to a follow-up question for self-contained sample code that demonstrates these techniques.

C# passing array to array but still getting "cannot convert from 'string[]' to 'string'” error

(using VS Community 2019 v16.10.4 on Win 10 Pro 202H)
I'm working on a C# console/winforms desktop app which monitors some local drive properties and displays a status message. Status message display is executed via Task Scheduler (the task is programmatically defined and registered). The UI is executed from the console app using the Process method. My intention is to pass an argument from Scheduler to the console app, perform some conditional logic and then pass the result to the UI entry (Program.cs) for further processing.
I’m testing passing an argument from the console app to the UI entry point and I’m getting a “Argument 1: cannot convert from 'string[]' to 'string'” error.
Code is:
class Program_Console
{
public static void Main(string[] tsArgs)
{
// based on MSDN example
tsArgs = new string[] { "Test Pass0", "TP1", "TP2" };
Process p = new Process();
try
{
p.StartInfo.FileName = BURS_Dir;
p.Start(tsArgs); // error here
}
public class Program_UI
{
[STAThread]
public void Main(string[] tsArgs)
{
Isn’t "tsArgs" consistently an array?
EDIT:
For clarity I’m using .NET Framework 4.7.2. The problem was not with consistency of what I am passing but in the Process.Start(String, IEnumerable String) overload. I believed “IEnumerable String” included string[ ]; it obviously does not since I was able to pass a plain string (not a string variable -- that also failed – just a hardcoded string).
In case it’s useful to somebody, my work-around is saving the arguments to a SQLite table in the console app and loading them into a List in the UI app. I’m sure a more proficient programmer could do it more efficiently.
Start doesn' have a costractor with string array. if you look at msdn document youi will see that you can use something the closest to your example
public static Start (string fileName, IEnumerable<string> arguments);
so you can try
p.Start( filename,tsArgs );
and replace filename with yours
The only Start() method taking arguments as an array also needs the filename: Start(). You can't set the Filename via StartInfo and then omit that parameter in the method call.
The following should work for you:
p.Start(BURS_Dir, tsArgs);
In .Net 5.0+, and .Net Core and Standard 2.1+, you can use a ProcessStartInfo for multiple command-line arguments
tsArgs = new string[] { "Test Pass0", "TP1", "TP2" };
Process p = new Process();
try
{
p.StartInfo.FileName = BURS_Dir;
foreach (var arg in tsArgs)
p.StartInfo.ArgumentList.Add(arg);
p.Start();
}
catch
{ //
}
Alternatively, just add them directly
Process p = new Process
{
StartInfo =
{
FileName = BURS_Dir,
ArgumentList = { "Test Pass0", "TP1", "TP2" },
}
};
try
{
p.Start();
}
catch
{ //
}

C# Application with Numerous Errors

I am having fun with a couple of errors I am getting in a C# application I am writing.
The error I keep getting is:
encrypt and decrypt calls must have a return type
Console.WriteLine being used as a method
static void encrypt(string[] args) expected class, delegate, interface or struct
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string pw ="", hash =""; //Declare an intialise variables
if (args.Length < 4) // Test to see if correct number of arguments have been passed
{
Console.WriteLine("Please use command line arguments in this format: encrypt -e (or -d) password-to-encrypt-with input-file output-file");
Environment.Exit(0);
}
if (args[1].Length < 10 || args[1].Length > 40) // Test to see if the password is between 10 and 40 characters
{
Console.WriteLine("Please use a password between 10 and 40 characters");
Environment.Exit(0);
}
switch (args[0]) //Uses first argument value to drive switch statement (-e or -d)
{
case "-e":
encrypt(string[] args);
break;
case "-d":
decrypt(string[] args);
break;
default:
Console.WriteLine("When using the program please use -e to encrypt and -d to decrypt");
break;
}
} //End of MAIN
static void encrypt(string[] args) //Function to encrypt
{
string inputtext =""; //Initialise Varible (Ensure it is empty)
inputtext=System.IO.File.ReadAllText(args[2]); //Read file in an assign to input text
return;
}
static void decrypt(string[] args) //Function to decrypt
{
string inputtext =""; //Initialise Varible (Ensure it is empty)
inputtext=System.IO.File.ReadAllText(args[2]); //Read file in an assign to input text
return;
}
}
}
Any help would be much appreciated!
Alistair
When calling a method, you must not specify the types of the arguments. So:
case "-e":
encrypt(args);
break;
Along with what Hans has said, you mentioned an error about return types in your methods.
Your encrypt and decrypt methods have return statements, but they are void methods meaning they don't have any return types.
Either give it a type you want to return (presumably the string you are manipulating) or just remove the return statement altogether. You do not need to explicitly put return at the end of a method to get it to exit out of the method. It will do that anyway.
Two small pro-tips, I would declare your fields on different lines, not all bunched together (with the way you have declared pw and hash) and also add a using directive for System.IO, so you don't have to call System.IO.File.ReadAllText, you can just call File.ReadAllText.

C#: How do I add a response to "/?" in my program

Basically, I know that some apps when called in command line with "/?" spit back a formatted list of how to call the app with params from the command line. Also, these apps sometimes even popup a box alerting the user that the program can only be run with certain params passed in and give this detailed formatted params (similar to the command prompt output).
How do they do this (The /? is more important for me than the popup box)?
The Main method takes string[] parameter with the command line args.
You can also call the Environment.GetCommandLineArgs method.
You can then check whether the array contains "/?".
Try looking at NDesk.Options. It's a single source file embeddable C# library that provides argument parsing. You can parse your arguments quickly:
public static void Main(string[] args)
{
string data = null;
bool help = false;
int verbose = 0;
var p = new OptionSet () {
{ "file=", "The {FILE} to work on", v => data = v },
{ "v|verbose", "Prints out extra status messages", v => { ++verbose } },
{ "h|?|help", "Show this message and exit", v => help = v != null },
};
List<string> extra = p.Parse(args);
}
It can write out the help screen in a professional looking format easily as well:
if (help)
{
Console.WriteLine("Usage: {0} [OPTIONS]", EXECUTABLE_NAME);
Console.WriteLine("This is a sample program.");
Console.WriteLine();
Console.WriteLine("Options:");
p.WriteOptionDescriptions(Console.Out);
}
This gives output like so:
C:\>program.exe /?
Usage: program [OPTIONS]
This is a sample program.
Options:
-file, --file=FILE The FILE to work on
-v, -verbose Prints out extra status messages
-h, -?, --help Show this message and exit

How to receive an argument in console program?

So I want other users to be able to run my programm sending arguments. how to do such thing?
If you have a Main method (which you'll have with a command-line app) you can access them directly as the args string-array parameter.
public static void Main(string[] args) {
var arg1 = args[0];
var arg2 = args[1];
}
If you're some other place in your code you can access the static Environment.GetCommandLineArgs method
//somewhere in your code
var args = Environment.GetCommandLineArgs();
var arg1 = args[0];
var arg2 = args[1];
You mean args when launching? such as myapp.exe blah blah2 blah3
Make your main method look like this:
public static void Main(string[] args)
{
}
now args is an array of the arguments passed into the program. So in the example case, args[0] == "blah", args[1] == "blah2", etc
The program is run from a method with this signature
public static void Main(string[] args)
The parameter args will contain the command line arguments, split on space.
While string[] args works just fine, it's worth mentioning Environment.GetCommandLineArgs.
You can read command line arguments from Main's optional string[] parameter:
static void Main(string[] args)
{
if (args.Length >= 1)
{
string x = args[0];
// etc...
}
}
Note that the following declaration for the Main method is also valid, but then you don't have access to the command line arguments:
static void Main()
{
// ...
}
See the documentation for more details.
This is supported by default, and the arguments will appear in the args array passed to your program.
public static void Main(string[] args)
If you say
App.exe Hello World What's Up
On a command line, you will receive an args array like this:
[0] = "Hello"
[1] = "World"
[2] = "What's"
[3] = "Up"
It's just up to you to determine what arguments you want, how they will be formatted, etc.
try these:
http://sourceforge.net/projects/csharpoptparse/
http://www.codeproject.com/KB/recipes/command_line.aspx
they basically allow you to define args and parse them in an OO way rather than having to lots of string comparisons and stuff like that. i used a similar one for java and it was great

Categories