In order to allow compatibility with another project that is written in .Net 2.0, we have had to come up with a COM interop (the newer application is in .Net 4.0). This would work because the 2.0 process would be able to use SxS execution with .Net 4.0. In order to have a COM interop from what I understand I have to do something like this:
Type myClass = Type.GetTypeFromProgID("Net4Assembly.Assembly4");
object myInstance = Activator.CreateInstance(myClass);
IAssembly4 assembly4Interface = (IAssembly4)myInstance;
assembly4Interface.CallMethod();
I have already created the COM component and registered it and this works fine. But the problem is that since the project written in 2.0 is outside our department, I want to find a way of doing the casting in line 3 above using reflection. So far I have found a suggestion in Invoke method using Reflection on COM Object
But this doesn't work for me since when I get all the methods of the object in "myInstance" which is of type COMObject, I can only see the methods that are mentioned in that link. I get this error:
Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))
I think I should somehow cast the COMObject to the interface and then I would have access to the methods? Shouldn't I be able to extract the interface from the COMObject, then call the method using reflection? I tried GetInterfaces() from the COMObject but nothing is returned.
I am not sure if this will work but assuming you have No Misspelled Assembly Name try something like this
Type myClass = Type.GetTypeFromProgID("Net4Assembly.Assembly4");
object myInstance = Activator.CreateInstance(myClass );
//object[] arguments = new object[] //add parameters if youre assembly expects them here
object result = myClass .InvokeMember("SubtractTwoNumbers", BindingFlags.InvokeMethod,
null, myInstance, arguments);
Related
I love using dynamic variables for accessing COM objects. However I have a problem with one object. See the following code working in VBS:
WScript.Echo "Nazwa firmy: " & itg.FirmaInfo.Nazwa
itg is a specific object that works basically equally well in vbscript and in c# using dynamic variables. Until I try to use the member FirmaInfo. Seems like it is a very special member which requires QueryInterface call. When I was accessing it through Jacob it was in this way:
static final String sIFirmaInfo = "{3F707848-DC7D-4B37-A4C8-7270644020F7}";
ActiveXComponent fi0 = itg.getPropertyAsComponent("firmainfo");
fi = new ActiveXComponent(fi0.QueryInterface(sIFirmaInfo));
fi0.safeRelease();
// now I am able access Nazwa member of fi
I can't find a way to do this in c#. When I do a simple approach:
Console.WriteLine(itg.FirmaInfo.Nazwa)
I get an error:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.__ComObject' does not contain a definition for 'Nazwa'
at CallSite.Target(Closure , CallSite , ComObject )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at Itg.open(String sKatFirmy, String sUser, String sPass) in w:\introl\prozapbi\Itg.cs:line 100
I know I could try a static client to COM object, but I am not familiar with this technique. Maybe I can stay with my dynamic approach, but need just a 3 suitable lines of code? Something that would turn my FirmaInfo object to one that exposes the IFirmaInfo interface.
I wasn't able to accomplish the task using dynamic. I present a workaround.
Switching myself to a static way of accessing a COM object turned out to be very easy and fast. It took a couple of minutes. Here's what I did:
set tlbimp="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\TlbImp.exe"
%tlbimp% "C:\path_to_type_library\Mxfk2015_c.dll"
These commands produce a DLL. I analyzed the DLL using ilspy, but ildasm would do too. I needed exact names to use in code. Finally after adding a reference to the dll created by tlbimp, I could change the only invocation that was failing to a static cast.
dynamic itg = ...
var fi = (MXDokFK.FirmaInfo)itg.FirmaInfo;
So the thing started working and I could move on. I don't use GUI. All from command line.
I have a C++ COM server application that I'm calling from my managed C# app. The COM server changes from time to time as new COM methods are added.
In my managed application, I create the object as follows:
Type comType = Type.GetTypeFromCLSID(new Guid(...));
Object comObj = Activator.CreateInstance(comType);
I would like to know if the COM object implements a specific method. I believe that I need to call GetMethod() on the comType as follows:
MethodInfo[] methods = comType.GetMethods();
However, this appears to return a list of methods of the .Net 'Type' object and not the COM server app. I may be missing a cast but I'm not sure what I need to cast it to.
((???)comType).GetMethods();
I've scanned many topics on this site, searched the internet, and experimented with code and nothing has worked. Most people are using separate projects or assemblies which I am not, it's a custom class that exists in the same project and same namespace. If I build the object manually by hard coding it in it works fine but I don't want to do that.
It's a C# ASPX project and I am debugging on IIS from Visual Studio (so maybe that's the issue?).
Type type = Type.GetType("<namespace>."+classname);
Object obj = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod(function);
response = (<cast object>)(methodInfo.Invoke(obj, null));
I am aiming for variable code where I can write plugins that will be dynamically instantiated. Type.GetType always returns null.
In almost all cases type returns null or when I switch it up with other code I'll get other errors thrown about not finding file or assembly and other errors like this class just doesn't exist...
What do I have to do to be able to build an object dynamically off a string? Let's say I have a class called "Foobar" and I want to do this,
string classname = "Foobar";
Object foobar = new classname(); //easy in PHP, nightmare in C#
Any help would be great, thanks. And before you tell me just to reference another post, I have referenced many and still have no success so if it's not the code than maybe it's how I'm debugging in a browser on IIS?
Type.GetType(String) accepts assembly-qualified type name.
See Type.AssemblyQualifiedName Property
You need to get the type from the assembly is was defined in:
typeof(SomeTypeInAssembly).Assembly.GetType("Namespace.Type")
If it is in the same namespace and assembly as the current object as you say, you should just be able to do the following to get hold of the Type you require:
Type type = Type.GetType(this.GetType().Namespace + "." + classname);
The rest should work as you have it.
Thanks for the suggestions, final code I ended up with below from the suggestions and other post I found on this site.
string fullname = string.Format("{0}.{1}", this.GetType().Namespace, classname);
Type type = Type.GetType(fullname,true);
Baseclass class = (BaseClass)Activator.CreateInstance(type,<parameter1>,...);
MethodInfo methodInfo = type.GetMethod("<method>");
methodInfo.Invoke(class, null);
You can store the return (may need casting) if you want to deal with the return type. Hope this helps someone if they're having issues like me.
Thanks to Rhumborl post, I guess it was an issue in just how I was originally trying to call the Type.GetType function.
String is a native type , witch is automatically inherited from object , since every type (aka class) is an object, so there is no need to do such a thing
This is an offshoot question that's related to another I asked here. I'm splitting it off because it's really a sub-question:
I'm having difficulties casting an object of type dynamic to another (known) static type.
I have an ironPython script that is doing this:
import clr
clr.AddReference("System")
from System import *
def GetBclUri():
return Uri("http://google.com")
note that it's simply newing up a BCL System.Uri type and returning it. So I know the static type of the returned object.
now over in C# land, I'm newing up the script hosting stuff and calling this getter to return the Uri object:
dynamic uri = scriptEngine.GetBclUri();
System.Uri u = uri as System.Uri; // casts the dynamic to static fine
Works no problem. I now can use the strongly typed Uri object as if it was originally instantiated statically.
however....
Now I want to define my own C# class that will be newed up in dynamic-land just like I did with the Uri. My simple C# class:
namespace Entity
{
public class TestPy // stupid simple test class of my own
{
public string DoSomething(string something)
{
return something;
}
}
}
Now in Python, new up an object of this type and return it:
sys.path.append(r'C:..path here...')
clr.AddReferenceToFile("entity.dll")
import Entity.TestPy
def GetTest():
return Entity.TestPy(); // the C# class
then in C# call the getter:
dynamic test = scriptEngine.GetTest();
Entity.TestPy t = test as Entity.TestPy; // t==null!!!
here, the cast does not work. Note that the 'test' object (dynamic) is valid--I can call the DoSomething()--it just won't cast to the known static type
string s = test.DoSomething("asdf"); // dynamic object works fine
so I'm perplexed. the BCL type System.Uri will cast from a dynamic type to the correct static one, but my own type won't. There's obviously something I'm not getting about this...
--
Update: I did a bunch of tests to make sure my assembly refs are all lining up correctly. I changed the referenced assembly ver number then looked at the dynamic objects GetType() info in C#--it is the correct version number, but it still will not cast back to the known static type.
I then created another class in my console app to check to see I would get the same result, which turned out positive: I can get a dynamic reference in C# to a static type instantiated in my Python script, but it will not cast back to the known static type correctly.
--
even more info:
Anton suggests below that the AppDomain assembly binding context is the likely culprit. After doing some tests I think it very likely is. . . but I can't figure out how to resolve it! I was unaware of assembly binding contexts so thanks to Anton I've become more educated on assembly resolution and the subtle bugs that crop up there.
So I watched the assembly resolution process by putting a handler on the event in C# prior to starting the script engine. That allowed me to see the python engine start up and the runtime start to resolve assemblies:
private static Type pType = null; // this will be the python type ref
// prior to script engine starting, start monitoring assembly resolution
AppDomain.CurrentDomain.AssemblyResolve
+= new ResolveEventHandler(CurrentDomain_AssemblyResolve);
... and the handler sets the var pType to the Type that python is loading:
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
if (args.LoadedAssembly.FullName ==
"Entity, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null")
{
// when the script engine loads the entity assembly, get a reference
// to that type so we can use it to cast to later.
// This Type ref magically carries with it (invisibly as far as I can
// tell) the assembly binding context
pType = args.LoadedAssembly.GetType("Entity.TestPy");
}
}
So while the type used by python is the same on in C#, I'm thinking (as proposed by Anton) that the different binding contexts mean that to the runtime, the two types (the one in the 'load binding context' and the 'loadfrom binding context) are different--so you can't cast on to the other.
So now that I have hold of the Type (along with it's binding context) loaded by Python, lo and behold in C# I can cast the dynamic object to this static type and it works:
dynamic test = scriptEngine.GetTest();
var pythonBoundContextObject =
Convert.ChangeType(test, pType); // pType = python bound
string wow = pythonBoundContextObject .DoSomething("success");
But, sigh, this doesn't totally fix the problem, because the var pythonBoundContextObject while of the correct type, still carries the taint of the wrong assembly binding context. This means that I can't pass this to other parts of my code because we still have this bizzare type mismatch where the invisible specter of binding context stops me cold.
// class that takes type TestPy in the ctor...
public class Foo
{
TestPy tp;
public Foo(TestPy t)
{
this.tp = t;
}
}
// can't pass the pythonBoundContextObject (from above): wrong binding context
Foo f = new Foo(pythonBoundContextObject); // all aboard the fail boat
So the resolution is going to have to be on the Python side: getting the script to load in the right assembly binding context.
in Python, if I do this:
# in my python script
AppDomain.CurrentDomain.Load(
"Entity, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null");
the runtime can't resolve my type:
import Entity.TestPy #fails
Here's an answer from the IronPython team which covers the same problem:
C# / IronPython Interop with shared C# Class Library
(Lifted from http://lists.ironpython.com/pipermail/users-ironpython.com/2010-September/013717.html )
I bet that IronPython loads your entity.dll into a different assembly load context, so that you have two copies of it loaded and the types in them are of course different. You might be able to work around this issue by hooking AppDomain.AssemblyReslove/AppDomain.AssemblyLoad and returning your local assembly (typeof (Entity.TestPy).Assembly) when IronPython tries to load it, but I don't guarantee that this will work.
You don't experience this with System.Uri because mscorlib.dll (and maybe some other system assemblies) is treated specially by the runtime.
Update: IronPython FAQ states that if the assembly isn't already loaded clr.AddReferenceToFile uses Assembly.LoadFile, which loads into the 'Neither' context. Try accessing a method from Entity.TestPy before calling IronPython to load the assembly into the default Load context.
I have an application that uses plugins that are managed via an interface I then dynamically load the plugin classes and cast them to the interface to work with them.
I have the following line of code, assume that IPlugin is my interface.
IPlugin _plugin = (IPlugin)Activator.CreateInstance(oInfo.Assembly, oInfo.FullyQualifiedName)
This should be pretty simple, create the instance and cast it to the interface. I know that the assembly and fully qualified name values are correct, but I am getting the following exception.
Exception=
System.InvalidCastException: Unable to
cast object of type
‘System.Runtime.Remoting.ObjectHandle’
to type
‘MyNamespace.Components.Integration.IPlugin’.
at
MyNamespace.Components.Integration.PluginProxy..ctor(Int32
instanceId)
Any ideas what could cause this?
The exception indicates that you're getting an ObjectHandle, which suggests that your object is being marshaled and must be unwrapped.
Try the following
ObjectHandle marshaled_plugin = (ObjectHandle)Activator.CreateInstance(oInfo.Assembly, Info.FullyQualifiedName);
IPlugin plugin = (IPlugin)marshaled_plugin.Unwrap();
As you can see in the documentation, this overload returns an ObjectHandle object that wraps the new instance.
The ObjectHandle cannot be casted directly to your interface.
Instead, you need to call the Unwrap method, like this:
IPlugin _plugin = (IPlugin)Activator.CreateInstance(...).Unwrap();