Question

I have a .net assembly providing a huge number of API's and I need to write a COM wrapper for it . I can not write interfaces to each and every method and property provided my the .net assembly due to time constraints. Instead What i plan to do is to write a generic commandParser function which takes the string argument giving the full path (?) of the property or method being referred and in turn the method has to call the correct property or method . e.g suppose i need to set the property Visible , i would pass the string as below (refer image also for the class structure) or if need to call a method

"APx.AcousticResponse.AcquiredWaveform.DeltaCursor.Visible"

or

"APx.BandpassLevel.FixedTuningFrequency.GetValue(Unit)"**

Obviously i am clueless , I know using reflections it is possible but i am not at there yet.Here is the dummy code i have been working so far with out any success :P

public bool methodFromString(string methodName,object [] parameteres)
            {
                string [] split = methodName.Split(new char []{'.'});
                apx = new APx500();
                Type wrapType = apx .GetType();
                FieldInfo[] fields = wrapType.GetFields();
                MemberInfo[] members = wrapType.GetMembers();
                Console.WriteLine(members.Length);
                MethodInfo [] methods = wrapType.GetMethods();
                foreach (MemberInfo mem in members)
                {
                    //Console.WriteLine(mem.Name);
                    if(mem.Name == split[0])
                    {
                        Console.WriteLine(mem.Name);
                        Type memType = mem.GetType();
                        MethodInfo[] temp = memType.GetMethods();
                        foreach (MethodInfo t in temp)
                        {

                            Console.WriteLine(memType.Name);
                        }
                    }
                    //if (met.Name == methodName)
                    //{
                    //    try
                    //    {
                    //        met.Invoke(APx, parameteres);
                    //        break;
                    //    }
                    //    catch (TargetInvocationException ex)
                    //    {
                    //        Console.WriteLine(ex.Message);
                    //        Console.WriteLine(ex.InnerException);
                    //        break;
                    //    }
                    //}
                }


               // MethodInfo theMethod = wrapType.GetMethod(methodName);
                //theMethod.Invoke(APx, parameteres);
                //wrapType.InvokeMember(methodName,
                //    BindingFlags.InvokeMethod | BindingFlags.Public |
                //        BindingFlags.Static,
                //    null,
                //    null,
                //    parameteres);

                return true;
            }

Any hints or help is much appreciated

Was it helpful?

Solution

The following piece of code might be somewhat long but I certainly do think that it does all that you have requested.

Let's start with the DynamicInvoke class.

public class DynamicInvoke
{
    List<object> Parameters { get; set; }
    List<string> Paths { get; set; }
    object Instance { get; set; }
    Type Type { get; set; }

    public DynamicInvoke(string path, params object[] parameters)
    {
        Parameters = parameters.ToList();

        Paths = path.Split('+').ToList();

        Type = AppDomain.CurrentDomain
                        .GetAssemblies()
                        .Where(x => x.GetName().Name == Paths[0])
                        .First()
                        .GetTypes()
                        .Where(x => x.FullName == Paths[1])
                        .First();

        Instance = Activator.CreateInstance(Type, Parameters.ToArray());
    }

    public T DynamicPropertyGet<T>()
    { 
        return (T)Type.GetProperty(Paths[2]).GetValue(Instance, null);            
    }

    public void DynamicPropertySet(object value)
    {
        Type.GetProperty(Paths[2]).SetValue(Instance, value, null);
    }

    public T DynamicMethodInvoke<T>(params object[] parameters)
    { 
        return (T)Type.GetMethods()
                      .Where(x => x.Name == Paths[2] && AreAllEqual(x, parameters))                          
                      .First()
                      .Invoke(Instance, parameters);
    }

    bool AreAllEqual(MethodInfo method, params object[] parameters)
    {
        var p1 = method.GetParameters().Select(x => x.ParameterType);
        var p2 = parameters.Select(x => x.GetType());

        var except1 = p1.Except(p2).ToList().Count;
        var except2 = p2.Except(p1).ToList().Count;

        return (except1 > 0 || except2 > 0) ? false : true;
    }
}

As you can see, it contains four properties which will hold all the relevant values we will need to invoke the required properties and methods.

In its constructor you simply pass as its first parameter the path to the property or the method you need to invoke. The rest are the required parameters that you might need to instantiate the type on which the property or the method will be invoked. The constructor will then parse the path parameter and provide you with the appropriate instance.

You can then either invoke the DynamicPropertyGet() method to get the value from some property, DynamicPropertySet(object) method to set the value of some property, or the DynamicMethodInvoke(params object[]) to invoke a method. The generic parameters are used to designate the types of the instances that will be returned.

The AreAllEqual(MethodInfo, params object[]) method is simply here to decide which overloaded method to invoke, depending on the parameter count and type.

The following is our test class. You can notice that it defines both properties and overloaded methods.

class People
{
    public string Name { get; set; }

    public People()
    {
        Name = "Billy";
    }

    public People(string name)
    {
        Name = name;
    }

    public string CallMe()
    {
        return Name;
    }

    public string CallMe(string value)
    {
        return value;
    }

    public void NoReturn()
    {
        Console.WriteLine("nothing");
    }
}

You can now test this approach with the following lines of code.

class Program
{
    static void Main()
    {
        var path = "Types+Types.People+Name";
        var path2 = "Types+Types.People+CallMe";
        var path3 = "Types+Types.People+NoReturn";

        var instance1 = new DynamicInvoke(path);
        var instance2 = new DynamicInvoke(path, "Jill");
        var instance3 = new DynamicInvoke(path2);
        var instance4 = new DynamicInvoke(path2, "Johnny");
        var instance5 = new DynamicInvoke(path3);

        instance1.DynamicPropertySet("Tom");

        sc.WriteLine(instance1.DynamicPropertyGet<string>());
        sc.WriteLine(instance2.DynamicPropertyGet<string>());

        sc.WriteLine(instance3.DynamicMethodInvoke<string>());
        sc.WriteLine(instance4.DynamicMethodInvoke<string>("Timmy"));

        instance5.DynamicMethodInvoke<object>();

        Console.Read();
    }
}

The paths to the properties and methods are split in three parts using the "+" sign. The 1st part is the name of the assembly you want to use. the 2nd part is the full name of the type you will instantiate. While the 3rd part is the name of the method or the property you want to invoke.

You should also notice that each instance variable holds an instance of your type specified in the path and you can modify its properties multiple times by simply invoking the above mentioned methods.

EDIT: Inner property access example.

The following is a situation that you seem to be dealing with.

class University
{
    public Faculty Faculty { get; set; }

    public University()
    {
        Faculty = new Faculty();
    }
}

class Faculty
{
    public string Name { get; set; }

    public Faculty()
    {
        Name = "MIT";
    }
}

Let's say you have a University class, and you have a Faculty class. You can see that you have a Faculty property, of type Faculty, defined in the University class. You can also notice that the Faculty class has a Name property. This property represents your "AnalyzeFilesFixe‌​dSampleRate" property of type double.

To reach this property, you simply have to execute the following lines of code.

var path = "Types+Types.University+Faculty";

var instance = new DynamicInvoke(path);

Consolec.WriteLine(instance.DynamicPropertyGet<Faculty>().Name);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top