0

I am having some trouble with assemblies and DLL's.

instrument_ is declared as an object and I'm creating an instance of "PP150" from the dll whose path is specified by path_.

string className = ContineoProperties.getSingleton().getClassName(path_);
assembly_ = Assembly.LoadFrom(path_);
Type classType = assembly_.GetType("Instrument." + className);
instrument_ = Activator.CreateInstance(classType);

Later I to call the method isntrument_.instrumentCommand(cmd.getCommandName())

The error I get is with when i call the method.

'object' does not contain a definition for 'instrumentCommand'

The isntrument_ is created fine. its just the method call that's giving me a problem. The method does exist in the "PP150.dll". Do I need some DLLImport to allow it to recognize it as a function?

Thanks, P

Otiel
  • 18,404
  • 16
  • 78
  • 126
YahooMania
  • 115
  • 4
  • 15

4 Answers4

3

If object type is not known in compile time,

To call a method defined on an object, you must use Reflection.

MethodInfo mInfo = classType.GetMethod("instrumentCommand");

mInfo.Invoke(instrument_, new Object[] { _parameters});
kochobay
  • 392
  • 1
  • 7
  • if isntrumentCommand is overloaded? can i just create one MethodInfo? And .Invoke() twice with different number of paramters? – YahooMania Dec 15 '11 at 21:46
  • No it's not possible. I got an error saying Ambiguous match found. – YahooMania Dec 15 '11 at 21:50
  • you can get each overloaded method by specifiying type array on GetMethod. please see http://stackoverflow.com/questions/223495/how-to-use-reflection-to-invoke-an-overloaded-method-in-net – kochobay Dec 15 '11 at 21:56
2

The compiler is never going to recognize the methods on a type that you are loading via reflection (e.g. using Assembly.GetType() and Activator.CreateInstance()). Unless you have the type metadata available at build time, you will always get that error if you try to call methods that are not defined on Object itself.

You have two options for making that kind of method call. Both of them require you to give up type safety, the only difference is the amount of work required. In both cases, if you make a mistake, the compiler will not tell you -- you will get a runtime exception instead.

  1. Declare instrument_ as dynamic instead of object. This, obviously, only works in .NET 4.0, but it accomplishes exactly what you're trying to do. The method call will be dispatched at runtime, so as long as the instance that instrument_ references actually has a method call with the appropriate name, it will work.

  2. Use reflection to call the method. You're already using reflection to load the type, so you are halfway there. You would need to add something like this:

    // The array of types is the parameter list; assuming instrumentCommand takes
    // a string it would look like this:
    MethodInfo method = classType.GetMethod("instrumentCommand", new Type[] { typeof(string) });
    method.Invoke(instrument_, new object[] { cmd.getCommandName() });
    
Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
  • Hey Michael, thanks for your help. Unfortunately i dont have .NET 4.0. But I did implement your second step, you explained it well. However, I'm having issues connecting to my instrument so I cannot test it yet. I will let you know if it works. -P – YahooMania Dec 16 '11 at 00:02
  • Yup, it works. Thanks Michael. I just accepted the previous answer because it was first :P – YahooMania Dec 19 '11 at 17:47
1

This happens because Activator.CreateInstance returns an object. I would create a separate DLL for the interface which is implemented by the class you want to instantiate. Both the DLL containing this class, and the executable should reference the DLL containing the interface. This way you could cast the object returned by Activator.CreateInstance to the interface, and call its methods:

IInstrument.dll:

interface IInstrument
{
  void instrumentCommand(string cmd);
}

Instrument.dll (add IInstrument.dll as reference):

class Instrument : IInstrument
{
  public void instrumentCommand(string cmd)
  {
    // ... implementation ...
  }
}

InstrumentApp.exe (add IInstrument.dll as reference):

class Program
{
  public static void Main()
  {
    // ... load Instrument.dll into assembly object ...
    // ... load the type from the assembly ...
    IInstrument instrument_ = (IInstrument)Activator.CreateInstance(classType);
    instrument_.instrumentCommand(cmd.getCommandName()); 
  }
}
kol
  • 27,881
  • 12
  • 83
  • 120
1

The most simple thing would be to link agains PP150.

If you did link against the dll you must use Assembly.LoadFile or Assembly.Load and not LoadFrom because the last one will cause the assembly load to load your assembly in the LoadFrom loader context which will alter type identity. Suppose you load the Type T from Assembly A via LoadFrom and you link against A as well.

object CreateTypeFrom()
{
    var A = Assembly.LoadFrom(@"xxxx");
    return A.CreateInstance("T");
}

void Test()
{
    object t = CreateTypeFrom();
    T RealT = new T(); // no prob
    T Castedt = (T)t; // this will throw an InvalidCastException
    T isNull = t as T; // this will result in a null instance
}

As you can see although you did create two times an instance of T they cannot be casted to due to different loader context which will make the type pretty useless.

To get rid of these things you could simply use Reflection to create a proxy type which will forward your calls to the proxy type. If you are using .NET 4 you can take advantage of the DLR to find the best matching methods at runtime. The code below creats a Version object and returns it as dynamic object. Then I do call the Major property to an integer and print it out to console. This does work with no exceptions nor compile time errors if you are using .NET 4 or later.

        dynamic CreateTypeFrom()
        {
            var assembly = typeof(string).Assembly;
            return assembly.CreateInstance("System.Version", true, BindingFlags.CreateInstance, null, new object[] { 1, 2, 3, 4 }, null, null);
        }

        [TestMethod]
        public void Test()
        {
            var t = CreateTypeFrom();
            int major = t.Major;
            Console.WriteLine(major);
        }
Alois Kraus
  • 13,229
  • 1
  • 38
  • 64