Passing Arbitrary Arguments From C# to Lua Functions - c#

I have discovered the cause of the issue. An answer has been posted below.
EDIT: The problem has changed, please see "The problem" section.
I am using LuaInterface. The generic call for lua functions using this library has this signature LuaFunction.Call(params object[] args). I have created a wrapper function that catches exceptions from the library and formats them for display on the in-game console window.
I am trying to call a lua function, but it is not receiving the arguments. This is the line in C#
Game.Instance.scriptEngine.Call("GenerateChunk", chunks[chunkID], GetChunkGridPosition(chunkID));
Which is simply wrapping a call to this Lua function that accepts two arguments:
//lua
function GenerateChunk(worldChunk, chunkGridPosition)
Log(LogLevel.Error, worldChunk.ToString());
Log(LogLevel.Error, chunkGridPosition.ToString());
end
that merely calls back into a C# Log function (which resolves correctly, and is visible in the Lua context).
The problem is that I am getting an "invalid arguments to method call" error from luainterface when attempting to call the GenerateChunk function, throwing this back:
invalid arguments to method call
at JASG.ScriptEngine.LuaError(Exception ex) Scripting\ScriptEngine.cs:line 144
at JASG.ScriptEngine.Call(String fnName, Object[] args) Scripting\ScriptEngine.cs:line 86
at JASG.ChunkManager.WakeChunk(Int32 chunkID) World\ChunkManager.cs:line 123
at JASG.ChunkManager.GetChunk(Int32 chunkID, Boolean wakeIfAsleep) World\ChunkManager.cs:line 53
I have tried various ways of calling the ScriptEngine.Call method, tried wrapping the arguments in an object[] array, etc., but no dice. Any ideas why lua is not receiving my arguments that I am passing? I have verified both arguments are non-null in C# when being passed in.

I've never used Lua before, but I've seen this kind of strange behaviors with calling COM objects (or any interop), or when the target assembly call is loaded on a different App Domain, or any other technology that intercommunicates a .Net assembly with a non-.Net one.
Have you tried using the [Serializable] attribute on the classes that define the result of "chunks[chunkID]" and "GetChunkGridPosition(chunkID)"? Are all your interop classes and types compatible between both assemblies?
Just thinking out loud here.

Side note: you should reduce your code to the shortest example that produces the problem. For instance, we don't need to see your wrapper function. You should have tried removing it. If that solved the problem, it's an important clue you should have been mentioned. If the problem remained, then that code is just a distracting irrelevancy for anyone reading this.
Your problem could be in your Log function. Everything thing else looks fine, that's the only code we can't actually see, and your problem can be reproduced like this:
public static void Log(int errorLevel, string message)
{
Console.WriteLine(message);
}
public void Test()
{
var lua = new Lua();
lua.RegisterFunction("Log", this, GetType().GetMethod("Log"));
lua.DoString("function foo() Log('a','b') end");
lua.GetFunction("foo").Call();
}
In this case, because 'a' cannot be marshaled into a number.

I incorrectly identified the problem as being with the call into Lua. The error message I was receiving was in fact originating from the Lua script calling back into my C# Log function.
I have discovered the hard way that in spite of exposing the enum LogManager.LogLevel to the lua script envronment, Lua does not support enum types. Thus,
Log(LogLevel.Debug, "hello");
was becoming
Log("Debug", "hello");
when marshalled by LuaInterface for the C# function. It was not until I created an ancillary ScriptLog(string level, string msg) that I was able to properly use the function from within lua. I wanted to keep the functionality of being able to use the enum names within Lua.
NOTE: As Lua does not support enum types, tonumber(LogLevel.Debug) fails as well.

Related

How do I create an apple block from C# interop code?

Apple recently deprecated the MIDIDestinationCreate method and replaced it with a MidiDestinationCreateWithProtocol (MIDIDestinationCreate)
The old method required to pass a simple callback method 'MIDIReadProc' which from C# could be done by creating the following delegate signature, then creating a delegate and assigning a method as the callback. We declare the delegate signature:
internal delegate void MIDIReadProc(MIDIPacketListPtr pktlist, IntPtr readProcRefCon, IntPtr srcConnRefCon);
Create a delegate to assign our callback method to:
private CoreMidiInterop.NativeMethods.MIDIReadProc m_readProcDelegate;
Assign a method to the delegate we created:
m_readProcDelegate = CallMessageReceived;
Create a callback method, that should receive MIDI messages from macOS Core MIDI:
private void CallMessageReceived(MIDIPacketListPtr pktlist, IntPtr readProcRefCon, IntPtr srcConnRefCon)
{
...
}
Eventually pass this delegate to the apple Core MIDI method:
CoreMidiInterop.NativeMethods
.MIDIDestinationCreate(m_clientRef, CoreFoundationUtils.ToCFStringRef(name), m_readProcDelegate, CFStringRef.Zero, out MIDIEndpointRef destinationRef);
This all works as expected.
That's now deprecated and no longer works on macOS Big Sur. The new method "MidiDestinationCreateWithProtocol" requires an apple block to use as the callback param (called MIDIReceiveBlock readBlock):
OSStatus MIDIDestinationCreateWithProtocol(MIDIClientRef client, CFStringRef name, MIDIProtocolID protocol, MIDIEndpointRef *outDest, MIDIReceiveBlock readBlock);
The documentation here: MIDIReceiveBlock
How is it possible to create an apple block with C# code? I've been searching for examples but cannot find any. I did start looking at the underlying implementation of apple blocks here:
Block Implementation Specification
It's no simple thing, so any help/example of how to do this in C# would be really helpful.
First, I feel that Apple is being inconsistent with their use of the word "deprecate". Since these new functions involving the words "WithProtocol" are only valid for Big Sur at this time, I don't see how all previous code can be deprecated until the previous versions of the operating system are no longer supported. See https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/FunctionalArea.html.
See https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/CoreMidi.html#MIDIDestinationCreateWithBlock MIDIDestinationCreate should indeed be deprecated, but MIDIDestinationCreateWithProtocol is only for BigSur. In addition the "WithProtocol" functions are to support MIDI 2.0, and the full specifications for MIDI 2.0 have not yet been released to the general public. I would therefore prefer to use MIDIDestinationCreateWithBlock. See the Xcode project BRossTools in the GitHub project at https://github.com/BradleyRoss/ObjectiveC-Examples. You can search this repository for the names of the CoreMIDI functions you are looking for and it may help. You might also want to look at https://github.com/mixedinkey-opensource/MIKMIDI. Part of the problem is that the functions ending with "WithBlock" and most of the functions ending with "WithProtocol" use closures (another name for blocks). I'm not sure how C# closures and CLANG closures (used in Objective-C) compare. Try to look for information on C# closures.

Datatype of Methods

Question
How does a delegate store a reference to a function? The source code appears to refer to it as an Object, and the manner in which it invokes the method seems redacted from the source code. Can anyone explain how C# is handling this?
Original Post
It seems I'm constantly fighting the abstractions C# imposes on its programmers. One that's been irking me is the obfuscation of Functions/Methods. As I understand it, all methods are in fact anonymous methods assigned to properties of a class. This is the reason why no function is prefixed by a datatype. For example...
void foo() { ... }
... would be written in Javascript as...
Function foo = function():void { ... };
In my experience, Anonymous functions are typically bad form, but here it's replete throughout the language standard. Because you cannot define a function with its datatype (and apparently the implication/handling is assumed by the compiler), how does one store a reference to a method if the type is never declared?
I'm trying very hard to avoid Delegates and its variants (Action & Func), both because...
it is another abstraction from what's actually happening
the unnecessary overhead required to instantiate these classes (which in turn carry their own pointers to the methods being called).
Looking at the source code for the Delegate.cs, it appears to refer to the reference of a function as simply Object (see lines 23-25).
If these really are objects, how are we calling them? According to the delegate.cs trail, it dead-ends on the following path:
Delegate.cs:DynamicInvoke() > DynamicInvokeImpl() > methodinfo.cs:UnsafeInvoke() > UnsafeInvokeInternal() > RuntimeMethodHandle.InvokeMethod() > runtimehandles.cs:InvokeMethod()
internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor);
This really doesn't explain how its invoked if indeed the method is an object. It feels as though this is not code at all, and the actual code called has been redacted from source repository.
Your help is appreciated.
Response to Previous Comments
#Amy: I gave an example immediately after that statement to explain what I meant. If a function were prefixed by a datatype, you could write a true anonymous function, and store it as a property to an Object such as:
private Dictionary<string, Function> ops = new Dictionary<string, Function> {
{"foo", int (int a, int b) { return a + b } }
};
As it stands, C# doesn't allow you to write true anonymous functions, and walls that functionality off behind Delegates and Lambda expressions.
#500 Internal server error: I already explained what I was trying to do. I even bolded it. You assume there's any ulterior motive here; I'm simply trying to understand how C# stores a reference to a method. I even provided links to the source code so that others could read the code for themselves and help answer the question.
#Dialecticus: Obviously if I already found the typical answer on Google, the only other place to find the answer I'm looking for would be here. I realize this is outside the knowledge of most C# developers, and that's why I've provided the source code links. You don't have to reply if you don't know the answer.
While I'm not fully understanding your insights about "true anonymous functions", "not prefixed by a data type" etc, I can explain you how applications written in C# call methods.
First of all, there is no such a thing "function" in C#. Each and every executable entity in C# is in fact a method, that means, it belongs to a class. Even if you define lambdas or anonymous functions like this:
collection.Where(item => item > 0);
the C# compiler creates a compiler-generated class behind the scenes and puts the lambda body return item > 0 into a compiler-generated method.
So assuming you have this code:
class Example
{
public static void StaticMethod() { }
public void InstanceMethod() { }
public Action Property { get; } = () => { };
}
static class Program
{
static void Main()
{
Example.StaticMethod();
var ex = new Example();
ex.InstanceMethod();
ex.Property();
}
}
The C# compiler will create an IL code out of that. The IL code is not executable right away, it needs to be run in a virtual machine.
The IL code will contain a class Example with two methods (actually, four - a default constructor and the property getter method will be automatically generated) and a compiler-generated class containing a method whose body is the body of the lambda expression.
The IL code of Main will look like this (simplified):
call void Example::StaticMethod()
newobj instance void Example::.ctor()
callvirt instance void Example::InstanceMethod()
callvirt instance class [mscorlib]System.Action Example::get_Prop()
callvirt instance void [mscorlib]System.Action::Invoke()
Notice those call and callvirt instructions: these are method calls.
To actually execute the called methods, their IL code needs to be compiled into machine code (CPU instructions). This occurs in the virtual machine called .NET Runtime. There are several of them like .NET Framework, .NET Core, Mono etc.
A .NET Runtime contains a JIT (just-in-time) compiler. It converts the IL code to the actually executable code during the execution of your program.
When the .NET Runtime first encounters the IL code "call method StaticMethod from class Example", it first looks in the internal cache of already compiled methods. When there are no matches (which means this is the first call of that method), the Runtime asks the JIT compiler to create such a compiled-and-ready-to-run method using the IL code. The IL code is converted into a sequence of CPU operations and stored in the process' memory. A pointer to that compiled code is stored in the cache for future reuse.
This all will happen behind the call or callvirt IL instructions (again, simplified).
Once this happened, the Runtime is ready to execute the method. The CPU gets the compiled code's first operation address as the next operation to execute and goes on until the code returns. Then, the Runtime takes over again and proceeds with next IL instructions.
The DynamicInvoke method of the delegates does the same thing: it instructs the Runtime to call a method (after some additional arguments checks etc). The "dead end" you mention RuntimeMethodHandle.InvokeMethod is an intrinsic call to the Runtime directly. The parameters of this method are:
object target - the object on which the delegate invokes the instance method (this parameter).
object[] arguments - the arguments to pass to the method.
Signature sig - the actual method to call, Signature is an internal class that provides the connection between the managed IL code and native executable code.
bool constructor - true if this is a constructor call.
So in summary, methods are not represented as objects in C# (while you of course can have a delegate instance that is an object, but it doesn't represent the executable method, it rather provides an invokable reference to it).
Methods are called by the Runtime, the JIT compiler makes the methods executable.
You cannot define a global "function" outside of classes in C#. You could get a direct native pointer to the compiled (jitted) method code and probably even call it manually by directly manipulating own process' memory. But why?
You clearly misunderstand main differences between script languages, C/C++ and C#.
I guess the main difficulty is that there is no such thing as a function in C#. At all.
C#7 introduced the new feature "a local function", but that is not what a function in JS is.
All pieces of code are methods.
That name is intentionally different from function or a procedure to emphasize the fact that all executable code in C# belongs to a class.
Anonymous methods and lambdas are just a syntax sugar.
A compiler will generate a real method in the same (or a nested) class, where the method with anonymous method declaration belongs to.
This simple article explains it. You can take the examples, compile them and check the generated IL code yourself.
So all the methods (anonymous or not) do belong to a class. It's impossible to answer your updated question, besides saying It does not store a reference to a function, as there is no such thing in C#.
How does one store a reference to a method?
Depending on what you mean by reference, it can be either
An instance of MethodInfo class, used to reference reflection information for a method,
RuntimeMethodHandle (obtainable via RuntimeMethodInfo.MethodHandle) stores a real memory pointer to a JITed method code
A delegate, that is very different from just a memory pointer, but logically could be used to "pass a method reference to another method" .
I believe you are looking for the MethodInfo option, it has a MethodInfo.Invoke method which is very much alike Function..apply function in JS. You have already seen in the Delegate source code how that class is used.
If by "reference" you mean the C-style function pointer, it is in RuntimeMethodHandle struct. You should never use it without solid understanding how a particular .Net platform implementation and a C# compiler work.
Hopefully it clarifies things a bit.
A delegate is simply a pointer(memory location to jump to) to a method with the specified parameters and return type. Any Method that matches the signature(Parameters and return type) is eligible to fulfill the role, irrespective of the defined object. Anonymous simply means the delegate is not named.
Most times the type is implied(if it is not you will get a compiler error):
C# is a strongly typed language. That means every expression (including delegates) MUST have a return type(including void) as well as strongly typed parameters(if any). Generics were created to permit explicit types to be used within general contexts, such as Lists.
To put it another way, delegates are the type-safe managed version of C++ callbacks.
Delegates are helpful in eliminating switch statements by allowing the code to jump to the proper handler without testing any conditions.
A delegate is similar to a Closure in Javascript terminology.
In your response to Amy, you are attempting to equate a loosely typed language like JS, and a strongly typed language C#. In C# it is not possible to pass an arbitrary(loosely-typed) function anywhere. Lambdas and delegates are the only way to guarantee type safety.
I would recommend trying F#, if you are looking to pass functions around.
EDIT:
If you are trying to mimic the behavior of Javascipt, I would try looking at using inheritance through Interfaces. I can mimic multiple inheritance, and be type safe at the same time. But, be aware that it cannot fully supplant Javascript's dependency injection model.
As you probably found out C# doesn't have the concept of a function as in your JavaScript example.
C# is a statically typed language and the only way you can use function pointers is by using the built in types (Func,Action) or custom delegates.(I'm talking about safe,strongly typed pointers)
Javascript is a dynamic language that's why you can do what you describe
If you are willing to lose type safety, you can use the "dynamic" features of C# or refection to achieve what you want like in the following examples (Don't do this,use Func/Action)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ConsoleApp1
{
class Program
{
private static Dictionary<string, Func<int, int, int>> FuncOps = new Dictionary<string, Func<int, int, int>>
{
{"add", (a, b) => a + b},
{"subtract", (a, b) => a - b}
};
//There are no anonymous delegates
//private static Dictionary<string, delegate> DelecateOps = new Dictionary<string, delegate>
//{
// {"add", delegate {} }
//};
private static Dictionary<string, dynamic> DynamicOps = new Dictionary<string, dynamic>
{
{"add", new Func<int, int, int>((a, b) => a + b)},
{"subtract", new Func<int, int, int>((a, b) => a - b)},
{"inverse", new Func<int, int>((a) => -a )} //Can't do this with Func
};
private static Dictionary<string, MethodInfo> ReflectionOps = new Dictionary<string, MethodInfo>
{
{"abs", typeof(Math).GetMethods().Single(m => m.Name == "Abs" && m.ReturnParameter.ParameterType == typeof(int))}
};
static void Main(string[] args)
{
Console.WriteLine(FuncOps["add"](3, 2));//5
Console.WriteLine(FuncOps["subtract"](3, 2));//1
Console.WriteLine(DynamicOps["add"](3, 2));//5
Console.WriteLine(DynamicOps["subtract"](3, 2));//1
Console.WriteLine(DynamicOps["inverse"](3));//-3
Console.WriteLine(ReflectionOps["abs"].Invoke(null, new object[] { -1 }));//1
Console.ReadLine();
}
}
}
one more example that you shouldn't use
delegate object CustomFunc(params object[] paramaters);
private static Dictionary<string, CustomFunc> CustomParamsOps = new Dictionary<string, CustomFunc>
{
{"add", parameters => (int) parameters[0] + (int) parameters[1]},
{"subtract", parameters => (int) parameters[0] - (int) parameters[1]},
{"inverse", parameters => -((int) parameters[0])}
};
Console.WriteLine(CustomParamsOps["add"](3, 2)); //5
Console.WriteLine(CustomParamsOps["subtract"](3, 2)); //1
Console.WriteLine(CustomParamsOps["inverse"](3)); //-3
I will provide a really short and simplified answer compared to the others. Everything in C# (classes, variables, properties, structs, etc) has a backed with tons of things your programs can hook into. This network of backend stuff slightly lowers the speed of C# when compared to "deeper" languages like C++, but also gives programmers a lot more tools to work with and makes the language easier to use. In this backend is included things like "garbage collection," which is a feature that automatically deletes objects from memory when there are no variables left that reference them. Speaking of reference, the whole system of passing objects by reference, which is default in C#, is also managed in the backend. In C#, Delegates are possible because of features in this backend that allow for something called "reflection."
From Wikipedia:
Reflection is the ability of a computer program to examine,
introspect, and modify its own structure and behavior at runtime.
So when C# compiles and it finds a Delegate, it is just going to make a function, and then store a reflective reference to that function in the variable, allowing you to pass it around and do all sorts of cool stuff with it. You aren't actually storing the function itself in the variable though, you are storing a reference, which is kinda like an address that points you to where the function is stored in RAM.

Passing a COM object from C# to Perl using PerlNET

I’m trying to pass a COM object from C# code to Perl.
At the moment I’m wrapping my Perl code with PerlNET (PDK 9.4; ActiveState) and I have defined a simple subroutine (+ required pod declaration) in Perl to pass objects from C# to the wrapped Perl module.
It seems that the objects I pass are not recognized correctly as COM objects.
An example:
In C# (.NET 4.0), the ScriptControl is used to load a simple class from a file written in VBScript.
var host = new ScriptControl();
host.Language = "VBScript";
var text = File.ReadAllText("TestScript.vbs");
host.AddCode(text);
dynamic obj = host.Run("GetTestClass");
What I get (obj) is of type System.__ComObject. When I pass it to my Perl/PerlNET assembly and try to call method Xyz() in Perl I get the following (runtime) exception:
Can't locate public method Xyz() for System.__ComObject
If, however, I do more or less the same thing in Perl, it works. (In the following case, passing only the contents of my .vbs file as parameter.)
I can even use the script control :
sub UseScriptControl {
my ($self, $text) = #_;
my $script = Win32::OLE->new('ScriptControl');
$script->{Language} = 'VBScript';
$script->AddCode($text);
my $obj = $script->Run('GetTestClass');
$obj->Xyz();
}
Now, calling Xyz() on obj works fine (using Win32::OLE).
In both cases I use:
use strict;
use Win32;
use Win32::OLE::Variant;
Another approach:
I can invoke methods by using InvokeMember of class System.Type if I specify exactly which overload I want to use and which types I’m passing:
use PerlNET qw(typeof);
typeof($obj)->InvokeMember("Xyz",
PerlNET::enum("System.Reflection.BindingFlags.InvokeMethod"),
PerlNET::null("System.Reflection.Binder"),
$obj,
"System.Object[]"->new());
Using this approach would mean rewriting the whole wrapped Perl module. And using this syntax..
Now I am wondering if I am losing both the advantages of the dynamic keyword in .NET 4.0 and the dynamic characteristics of Perl (with Win32::OLE) by using PerlNET with COM objects.
It seems like my preferred solution boils down to some way of mimicking the behaviour of the dynamic keyword in C#/.NET 4.0.
Or, better, finding some way of converting the passed COM object to something that will be recognized as compatible with Win32::OLE. Maybe extract some information of the __ComObject for it to be identified correctly as COM object.
I have to add that I posted to the PDK discussion site too (but didn’t get any response yet): http://community.activestate.com/node/18247
I also posted it to PerlMonks - as I'm not quite sure if this is more a Perl or C#/.NET question:
http://www.perlmonks.org/?node_id=1146244
I would greatly appreciate any help - or advise on where to look further.

Instantiating C# Classes in Lua

I am writing a scripting engine for my game using the LuaInterface library. I am getting an error when attempting to instantiate the class in Lua. The error is:
"./Scripts/sv_worldgen.lua:2: attempt to call global 'Campfire' (a string value)"
Where sv_worldgen.lua is (in entirety):
function GenerateChunk(worldChunk, chunkGridPosition)
tf = Campfire()
tf:SetPosition(chunkGridPosition)
end
Campfire is a class in C#, and appears to be exposed to lua as per the CLRPackage example and of course the LuaInterface Reference. I cannot seem to get around this error, and I have done due diligence of searching. The only other behavior of the script I can manage throws a similar error, but where it is "(a table value)". What am I doing wrong? Thank you in advance!
I tried explicitly doing Campfire._ctor(), but _ctor() is a string value.
This was resolved by using CLRPackage and using it to first load the assembly.
//Lua
JASG = CLRPackage("JASG", "JASG")
Then and only then can you link the classname to the actual C# class using (this must be done before trying to access it in Lua):
//Lua
Campfire=JASG.Campfire;
and then normal instantiation can occur by
//Lua
cf = Campfire()

C# 3.5 DLR Expression.Dynamic Question

I have inherited a small scripting language and I am attempting to port it to the DLR so that it is a little easier to manage. So far it has been fairly straight forward. I have run into a problem though attempting to dynamically call members of a variable. The current language runs on .NET and uses a parsing loop and reflection to do this, but I was hoping to get away from that. Here is an example of the script language:
string $system1RemoteUri;
string $dbconnection = $config.GetDBConnection ("somedb");
float $minBad = 0.998;
float $minGood = 0.2;
$systen1RemoteURI, $minBad, and $minGood are variables that will be set in the script, along with $dbconnection. However $dbconnection will get its value from a variable passed in called $config. The 4 variables need to be available to the caller, so they are passed into the lambda, initially as null. Here is the generated Lambda IL (debug view):
.Lambda #Lambda1<Delegate6$1>(
System.String& $$system1RemoteUri,
System.String& $$dbconnection,
System.Double& $$minBad,
System.Double& $$minGood
System.Object $$config) {
.Block() {
$$minBad = 0.998D;
$$minGood = 0.2D
}
//Some assignment similar to...
//.Dynamic Call GetDBConnection($config, "somedb");
}
What I am trying to figure out is how to use Expression.Dynamic to emit the $config.GetDBConnection("somedb"). From looking at examples in the Sympl libraries I believe the emitted IL should look like:
.Dynamic Call GetdbConnection($config, "somedb") but I cant figure out how to actually emit that from Expression.Dynamic.
It seems to want a CallSiteBinder which I cannot create correctly, and I do not understand what the order of parameters is to Expression.Dynamic, as it seems to only want the "member" being invoked, and not the base.
I do not know the runtime type of $config it is just some object which implements a function called GetDBConnection(string). This is not provided by an interface or base class.
Any help would be appreciated.
You can either turn this into an InvokeMemberBinder or turn "$config.GetDBConnection" into a GetMember and then do an Invoke on the result of that passing $someDb as the argument.
To implement your GetMemberBinder and InvokeMemberBinder you can use the DLR outer-layer DefaultBinder class. In the latest IronPython/IronRuby source code you can just create a new DefaultBinder instance out of thin air. Then in your FallbackGetMember / FallbackInvoke you can call defaultBinder.GetMember(...) and defaultBinder.Call (which should be renamed Invoke). That'll deal with most .NET types for you. Also all objects which implement IDynamicMetaObjectProvider will work with it as well. For other dynamic operations you can use the other methods on the default binder. And if you want to start customizing your overload resolution and binding rules it has lots of knobs you can turn.
Unfortunately the default binder doesn't have an InvokeMemberBinder implementation right now so you're probably better off w/ GetMember/Invoke.

Categories