Issues with calling a static method through a string using reflection - c#

I am making an app which is interfaced with at the command line by typing commands that then call corresponding methods. The thing is, some of these methods are asynchronous, and thus, according to what I've heard, should return Task instead of void, even when their return value is not used (async is not necessary for the program I am making, however a library I am using in some of the methods is asynchronous).
Because of this, I can't use a dictionary of delegates (as far as I know), as they would be different types, so I have tried using reflection.
MethodInfo command = MethodBase.GetCurrentMethod()
.DeclaringType
.GetMethod(_commands[args[0]]);
command.Invoke(null, new string[][] { args });
The above snippet is intended to get a static method by its name and then call it with argument string[] args.
According to the documentation I'm looking at, alongside other StackOverflow answers, the first argument should be null if the method being called is static, however I get a NullReferenceException anyway. Why is this, and how do I fix it?

Well, GetMethod can well return null or some non static method
MethodInfo command = MethodBase
.GetCurrentMethod()
.DeclaringType
.GetMethod(_commands[args[0]]);
So we have to check if command is valid one; since args[0] is used in finding the method, I guess it should be removed from parameters (Skip(1)):
if (command != null && command.IsStatic)
command.Invoke(null, args.Skip(1).Cast<Object>().ToArray());
please, note, that you have to do more validation if method can be overload (i.e. we have several methods with the same name), something like this:
MethodInfo command = MethodBase
.GetCurrentMethod()
.DeclaringType
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == _commands[args[0]])
.Where(m => m.GetParameters().Length == args.Length - 1) // - 1 for Skip(1)
.FirstOrDefault();

You must check that the command is not null.
If you don't want to handle the case to only call it if not null, you can simply write:
command?.Invoke(null, new string[] { args });
Thus if the method does not exist, GetCurrentMethod returns null and nothing is done.
But if you want to manage the case you need to use a test and for example show a system message.
You should also hardness the code by checking if args is noty empty too.
And you should also add some bindings flags to the search.
if (args.Length == 0)
{
Console.WriteLine("No command provided.");
return;
}
string commandName = _commands[args[0]];
// You can remove non public or public depending on the nature of your methods
var flags = var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
var command = MethodBase.GetCurrentMethod().DeclaringType.GetMethod(commandName, flags);
if (command == null)
{
Console.WriteLine("Command not found: " + commandName);
Console.WriteLine("Allowed commands are:"
Console.WriteLine("- ...");
Console.WriteLine("- ...");
return;
}
command.Invoke(null, new string[] { args });
I removed the jagged array [][] as seen and suggested by #DmitryBychenko in case of it was a mistake (it should be) that causes the error in the executed method if it exists.
Here a some example of advanced command line arguments parsing:
Parsing command-line options in C#
Best way to parse command line arguments in C#?
https://codereview.stackexchange.com/questions/369/parsing-console-application-arguments
https://github.com/mykeels/CommandLineParser
https://github.com/commandlineparser/commandline
https://www.codeproject.com/Articles/19869/Powerful-and-simple-command-line-parsing-in-C

Related

Invoking CmdLet from a C#-based PSCmdLet, providing input and capturing output

I'm building a Powershell CmdLet for awesome printing. It should function just like Out-Print but with all the bells and whistles of winprint.
PS> get-help out-winprint
NAME
Out-WinPrint
SYNTAX
Out-WinPrint [[-Name] <string>] [-SheetDefintion <string>] [-ContentTypeEngine <string>]
[-InputObject <psobject>] [<CommonParameters>]
ALIASES
wp
For this to work, I need to take the input stream (InputObject) of my PSCmdLet implementation and pass it through Out-String so it's all expanded and formatted. I'm thinking the best way to do this is to use CommandInvocationIntrinsics.InvokeScript to invoke out-string, which should give me the output as a string...
protected override void ProcessRecord() {
if (InputObject == null || InputObject == AutomationNull.Value) {
return;
}
IDictionary dictionary = InputObject.BaseObject as IDictionary;
if (dictionary != null) {
// Dictionaries should be enumerated through because the pipeline does not enumerate through them.
foreach (DictionaryEntry entry in dictionary) {
ProcessObject(PSObject.AsPSObject(entry));
}
}
else {
ProcessObject(InputObject);
}
}
private void ProcessObject(PSObject input) {
object baseObject = input.BaseObject;
// Throw a terminating error for types that are not supported.
if (baseObject is ScriptBlock ||
baseObject is SwitchParameter ||
baseObject is PSReference ||
baseObject is PSObject) {
ErrorRecord error = new ErrorRecord(
new FormatException("Invalid data type for Out-WinPrint"),
DataNotQualifiedForWinprint,
ErrorCategory.InvalidType,
null);
this.ThrowTerminatingError(error);
}
_psObjects.Add(input);
}
protected override async void EndProcessing() {
base.EndProcessing();
//Return if no objects
if (_psObjects.Count == 0) {
return;
}
var text = this.SessionState.InvokeCommand.InvokeScript(#"Out-String", true, PipelineResultTypes.None, _psObjects, null);
// Just for testing...
this.WriteObject(text, false);
...
Assume I invoked my cmdlet like this:
PS> get-help out-winprint -full | out-winprint`
If I understand how this is supposed to work, the var text above should be a string and WriteObject call should display what out-string would display (namely the result of get-help out-winprint -full).
However, in reality text is string[] = { "" } (an array of strings with one element, an empty string).
What am I doing wrong?
You're doing two very small things wrong:
The method is called InvokeScript so literally what you're passing is a scriptblock.
Right now, your ScriptBlock is basically like this:
$args = #(<random stuff>) # this line is implicit of course,
# and $args will have the value of whatever your _psObjects has
Out-String
So as you can tell the arguments made it to the script, you're just not using them. So you want something a bit more like this instead as your script:
Out-String -InputObject $args
Only now the problem is that Out-String doesn't actually like being given a Object[] as an -InputObject so instead your script has to be something like:
$args | Out-String
Or some variation of that like using a foreach, you get the idea.
Your second error is that you're passing _psObjects onto the wrong parameter - it should be:
this.SessionState.InvokeCommand.InvokeScript(<ScriptBlock>, true, PipelineResultTypes.None, null, _psObjects);
The official documentation is really bad on this and I have absolutely no idea what the other parameter is for.
On one of the overloads is lists:
input = Optionall input to the command
args = Arguments to pass to the scriptblock
But on the next overload it says the following:
input = The list of objects to use as input to the script.
args = The array of arguments to the command.
All I can tell you is, in my tests it works when I do it as stated. Hope that helps!
For reference, tested and working PS code:
function Test
{
[CmdletBinding()]
param()
$results = $PSCmdlet.SessionState.InvokeCommand.InvokeScript('$args | Out-String', $false, "None", $null, "Hello World!")
foreach ($item in $results)
{
$item
}
}
Test
EDIT
I should add that according to my tests, if you pass something to both input and args then $args will be empty inside the script. Like I said, no actual idea what input does at all, just pass null to it.
EDIT 2
As mentioned by tig, on PowerShell issue 12137, whatever gets passed to input will be bound to the variable $input inside the scriptblock which means either input or args can be used.
That being said... be careful of using $input - it is a collection that will contain more than what is passed via the input parameter: according to my tests index 0 will contain a bool that is whatever is passed on 2nd parameter of InvokeScript() and index 1 will contain a PipelineResultTypes that is whatever was passed to InvokeScript() on the 3rd parameter.
Also I would not recommend using PowerShell.Create() in this case: why create a new instance of PowerShell when you have a PSCmdlet which implies you already have one?
I still think using args/$args is the best solution. Of course you could also make things a lot nicer (although completely unneeded in this case) by using a ScriptBlock like:
[CmdletBinding()]
param
(
[Parameter(Mandatory)]
[PSObject[]]
$objects
)
Out-String -InputObject $objects
This will be faster as well since you are no longer relying on the (slow) pipeline.
Just don't forget that now you need to wrap your _psObjects around an object[] like:
this.SessionState.InvokeCommand.InvokeScript(<ScriptBlock>, true, PipelineResultTypes.None, null, new object[] {_psObjects});

How to find if a parameter to an invoked method is a variable (via "var/string") or an in-line string using Roslyn

I'm currently trying to find invocations of .ExecuteSqlCommand and examine the first value being passed to the sql param.
Here is an example of the differences I've found in our code base.
ExecuteSqlCommand("[sql statement here]");
vs.
var sql = "sql statement";
ExecuteSqlCommand(sql);
So far, I have this:
var invocations = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Select(ie => ModelExtensions.GetSymbolInfo(model, ie).Symbol)
.Where(symbol => symbol != null && symbol.Name == "ExecuteSqlCommand");
foreach (var invocation in invocations)
{
var method = (IMethodSymbol)invocation;
foreach (var param in method.Parameters)
{
//I can't quite seem to get information from IParameterSymbol about whether the param is a string literal, or a reference to a string via a variable.
}
}
If the param is not a string, and instead, a var, then I'll need to get the value of the var (as much as it's defined at runtime).
I'm not too sure if this is a job for the SemanticModel or the SyntaxTree, but my GUESS is that the SemanticModel should have the richer information I need to let me discover what I'm looking for.
My overall goal is to interrogate the sql being passed to the ExecuteSqlCommand method.
Thanks!
SemanticModel.GetConstantValue is the API we have for handling this situation.
It can accept both a syntax node and an expression. You will still need to track the state of variables back to their declaration sites and determine if they were given a constant expression.
I would use SemanticModel.GetSymbolInfo.Symbol?.DeclaringSyntaxReferences.First() to find the declaration site of a variable and then check to see if its a constant expression.
The Syntax API could be used to extract the sql statement value but depends on whether the variable declaration (i.e. var sql = "sql statement";) is included as part of code submitted to the syntax tree.
For example, if it's part of the same method implementation as where ExcuteSqlCommand() is called then you can first get the name of variable (i.e. sql) passed to it and use that to find the matching variable declaration statement within that same method. Finally, the sql statement value (i.e. "sql statement") can be extracted from that.
The following code first checks if the sql value is passed as a string literal otherwise looks for the variable declaration. The assumption it's all within the same method:
// whatever c# *method* code contains the sql.
// otherwise the root of the tree would need to be changed to filter to a specific single `MethodDeclarationSyntax`.
string submittedCode = "public void SomeMethodContainingSql(){ ...//rest of code...";
var tree = CSharpSyntaxTree.ParseText(submittedCode);
var root = (CompilationUnitSyntax) tree.GetRoot();
var arguments = root
.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.First(node => node.DescendantNodes().OfType<IdentifierNameSyntax>()
.First()
.Identifier.Text == "ExecuteSqlCommand")
.ArgumentList.DescendantNodes().ToList();
string sqlStatementValue = "";
var literalExpression = arguments.OfType<LiteralExpressionSyntax>().FirstOrDefault();
if (literalExpression != null)
{
sqlStatementValue = literalExpression.GetText().ToString();
}
else
{
var variableName = arguments
.First()
.ToFullString();
var variableDeclaration = root
.DescendantNodes()
.OfType<VariableDeclarationSyntax>()
.Single(node => node.DescendantNodes().OfType<VariableDeclaratorSyntax>()
.First()
.Identifier.Text == variableName);
sqlStatementValue = variableDeclaration.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.First()
.DescendantTokens()
.First()
.Text;
}
Otherwise, may need to look for the variable declaration in other parts of the submitted code (ex. class fields, properties, other methods, etc.) which is a bit more cumbersome.
Unfortunately Roslyn cannot provide a variable's value in a common cases when values is defined at runtime, because Roslyn actually doesn't know all possible values which may pass from outside the program, it doesn't calculate them and so on so forth. But if you can limit the needed cases to a inlining strings or variables which was declared and initialized at strings, Roslyn may help you with this:
You need to keep InvocationExpressionSyntax (or directly their first arguments)
var invocations = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Select(ie => (ModelExtensions.GetSymbolInfo(model, ie).Symbol, ie))
// Would be better to compare not method's name but it FQN
.Where((symbol, node)) => symbol != null && symbol.Name == "ExecuteSqlCommand" &&
symbol.Parameters.Length == 1 &&
symbol.Parameters[0].Type.SpecialType == SpecialType.System_String &&
node.ArgumentList.Arguments.Count == 1)
.Select((symbol, node) => node);
You need to check not method parameter, but method argument which was passed to it
foreach (var invocation in invocations)
{
var argument = invocation .ArgumentList.Arguments[0];
if (argument is LiteralExpressionSyntax literal && literal.IsKind(SyntaxKind.StringLiteralExpression))
{
// You find invocation of kind `ExecuteSqlCommand("sql")`
}
else
{
var argSymbol = ModelExtensions.GetSymbolInfo(model, argument).Symbol;
if (!(argSymbol is null) && (argSymbol.Kind == SymbolKind.Field || argSymbol.Kind == SymbolKind.Property || argSymbol.Kind == SymbolKind.Local))
{
if (argSymbol.DeclaringSyntaxReferences.Length == 1)
{
var declarationNode = argSymbol.DeclaringSyntaxReferences[0].GetSyntax();
// I'm actually don't remember what exactlly `GetSyntax` returns for fields or locals:
// VariableDeclaratorSyntax or one of it parent LocalDeclarationStatementSyntax and FieldDeclarationSyntax, but if it returns declarations you also can
// get from them VariableDeclaratorSyntax that you need, it's just be a more deep pattern matching
if (declarationNode is VariableDeclaratorSyntax declaratorSyntax &&
declaratorSyntax.EqualsValueClauseSyntax?.Value is LiteralExpressionSyntax literal2 && literal2.IsKind(SyntaxKind.StringLiteralExpression) )
{
// You find invocation of kind `ExecuteSqlCommand(variable)` where variable is local variable or field
}
else if (declarationNode is PropertyDeclarationSyntax property)
{
// You can do the same things for properties initializer or expression body that you was do for fields and locals to check your case,
// but it doesn't work for property with get/set accessors in a common cases
}
}
}
}
}

Obtaining name of a method parameter from the corresponding argument in a method invocation in Roslyn

I was wondering if there was some existing logic to obtain the name (or any other relevant information) about the definition of a parameter in its containing method/constructor signature by looking at an invocation of that particular method/constructor. Basically, I just want to be able to get a default name for a variable that will be passed as an argument to the invocation. So, if a method if defined as such:
public void Foo(object firstParam, object secondParam, object thirdParam)
I would want to be able to say that the second argument of the following invocation
object bar = null;
this.Foo(null, bar, null)
is expected to have the name "secondParam". Basically, I just want to relate an argument to the original parameter whose "spot" it occupies in the invocation.
I am asking if any util methods that I am not aware of already exist within Roslyn, as there are some more complex scenarios to handle, such as named or optionnal arguments. The solution I've come up with in the meantime should covers some cases, but probably not all (especially params, which should require some more specialized logic to handle). Here's what I have so far:
private IEnumerable<IdentifierNameSyntax> GetParameterNamesFromArgumentList(ArgumentListSyntax argumentList, SyntaxNodeAnalysisContext context)
{
var arguments = argumentList.Arguments;
var parameters = argumentList.Parent.GetSymbolOrDeclaredAs<IMethodSymbol>(context)?.Parameters;
if (parameters != null)
{
var index = 0;
foreach (var parameter in parameters)
{
var argument = index < arguments.Count ? arguments[index] : null;
if (argument != null && argument.NameColon == null)
{
yield return SyntaxFactory.IdentifierName(parameter.Name);
}
else if (argument != null)
{
yield return argument.NameColon.Name;
}
index++;
}
}
}
I could be using DeclaringSyntaxReferenceson the method symbol, but I think that just having the names from the IParameterSymbol suited my needs well enough. Again, if this kind of logic is already implemented anywhere else, I'd rather use it. If not, well, feel free to tell me what you think of the problem.
Sadly I don't think there is a good public way to do this. See Roslyn's internal DetermineParameter helper for something that might help.

Get generic argument type and value supplied to a generic method

How do you get the argument value supplied to a closed/constructed generic method?
It's been a while since I haven't touched Reflection. All this used to be at the back of my, umm, whatever.
class Program
{
static void Main(string[] args)
{
new ConcreteFoo().GenericMethod<int>(5);
Console.ReadKey();
}
}
class ConcreteFoo
{
public void GenericMethod<Q>(Q q)
{
var method = MethodInfo.GetCurrentMethod();
var parameters = method.GetParameters();
if (parameters.Length > 0)
foreach (var p in parameters)
Console.WriteLine("Type: {0}", p.ParameterType);
// That still prints Q as the type.
// I've tried GetGenericArguments as well. No luck.
// I want to know:
// 1) The closed type, i.e. the actual generic argument supplied by the caller; and
// 2) The value of that argument
}
public void GenericMethodWithNoGenericParameters<Q>()
{
// Same here
}
}
class GenericFoo<T>
{
public void NonGenericMethod(T t) { /* And here*/ }
public void GenericMethod<Q>(Q q) { /* And here */ }
}
UPDATE
This question is absurd and hence closed by the asker. He wishes to retain it just to show his children how stupid daddy was, if they ever turned out to be C# programmers.
The short answer is typeof(Q).
The long answer (which tries to explain why you can't enumerate these types and you must write them specifically) goes like this:
Each generic method (which is more generic than it's declaring class) has corresponding, distinct MethodInfo instances for all of its (ever) touched particularizations and another MethodInfo for the "template"/open method.
You could use this to obtain what you want:
class ConcreteFoo {
public void GenericMethod<Q>(Q q) {
var method = MethodInfo.GetCurrentMethod();
var closedMethod = method.MakeGenericMethod(typeof(Q));
// etc
}
}
Why is that ?
It's because none of the "enumerating operations" in reflection return MethodInfo instances that refer to closed particularizations.
If you enumerate the static methods declared by ConcreteFoo like so:
var atTime1 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
ConcreteFoo.GenericMethod( true );
var atTime2 = typeof(ConcreteFoo).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
you will get identical results.
As far as GenericMethod and it's entourage of particularizations are concerned, you will get only the reflection object associated with GenericMethod (open variant).
atTime2 will not contain an extra MethodInfo referring to the freshly jitted GenericMethod< bool >.
But that not's really a bad thing, now is it ?
GetMethods() should return consistent results and not have its results vary in time.
The algebra of generic methods is actually quite nice when it comes to it's "navigation" operations:
All open MethodInfos have IsGenericMethod = true and IsGenericMethodDefinition = true
All closed MethodInfos have IsGenericMethod = true and IsGenericMethodDefinition = false
By calling .GetGenericMethodDefinition() on a closed MethodInfo you get the open one
By calling .MakeGenericType(params Type[] types) on an open MethodInfo you get whatever closed one you want (without being syntactically aware of what those types are and with the possibility of receiving an exception for not respecting the where clauses)
The same goes for the reflection operations that come from the current thread's perspective (rather than from that of the assemblies and types):
MethodBase MethodInfo.GetCurrentMethod()
and
StackTrace trace = new StackTrace();
IEnumerable<MethodBase> methods = from frame in trace.GetFrames()
select frame.GetMethod();
never return the actual closed variants of generic methods (if any)
that are actually on the top, or throughout the current call stack.
In a way your question is not absurd, because, while in the case of GetCurrentMethod
you could easily replace it with GetCurrentMethod plus MakeGenericMethod plus the syntactically available typeof(Whatever), you can't say that about your callers.
So.. for non-generic methods you can always look at your stack and know precisely what are those methods' parameter types. The methods that invoked each other and eventually yours got invoked... But for generic ones (which are really truly closed, for I repeat it is illogical to think that a generic method that runs and calls another and was called by someone else (etc) is an open one) you can't find out the types of the parameters just like you can't learn the values of any such methods' local variables (which are deterministic but it would be a great flaw in performance to make that a possibility).

C# - Investigating "Method' information using Reflection?

My intention is to investigate the "Methods" of a Type using reflection in order to verify the following :
The Methods should be instance methods and public.
Takes the parameter "params" and void in nature.
The Method does not makes recursive call.
I started as :
static void ProcessMethodInfo(Type t)
{
MethodInfo[] info = t.GetMethods();
foreach (MethodInfo mi in info)
{
// How to check the conditions here ?
}
}
But I don't know how to proceed further. Help is needed.
Well, if by 3 you mean the method under inspection should be non-recursive; then that is a pain - you'd need to parse the IL. But for the others;
Type type = ...
var qry = from method in type.GetMethods(
BindingFlags.Instance | BindingFlags.Public)
where method.ReturnType == typeof(void)
let parameters = method.GetParameters()
where parameters.Length == 1
&& parameters[0].ParameterType.IsArray
&& Attribute.IsDefined(parameters[0], typeof(ParamArrayAttribute))
select method;
foreach (var method in qry)
{
Console.WriteLine(method.Name);
}
mi.IsStatic, etc - read help
Determining if a parameter uses "params" using reflection in C#?
http://www.codeproject.com/KB/cs/sdilreader.aspx
ALL: use google ;)
I don't think you'll be able to detect item 3 using reflection.
Check the following members of the MethodInfo class:
IsPublic
IsStatic
ReturnType
GetParameters() method
In order to be able to check whether the method is recursive, I think you'll need something more then just simple reflection.

Categories