Error when pass COM interface - c#

Initially, it was probably not proper to create COM through late binding, and then call its methods via InvokeMember.
But now, perhaps, too late to redo everything from scratch.
Therefore, tell me how to be.
There is a COM object in the DLL. Written in Delphi7.
In C#, it is used as follows:
Type comType = Type.GetTypeFromProgID(ProgID, false);
object comObj = Activator.CreateInstance(comType);
// and then call methods
comType.InvokeMember("DoLongWork", BindingFlags.InvokeMethod, null, comObj, null);
Now we need to add to it the opportunity to call the methods of the server (i.e. the one who keeps to himself this COM object)
For this, in a COM object in its TLB added additional interface
IHookCallback = interface(IDispatch)
procedure ServerHook(DoStuff: integer); safecall;
end;
And also, in its main interface added initialization method callback
ITestDisp = dispinterface
...
procedure SetupHook(const Callback: IHookCallback); safecall;
Then imported into the VS project DLL - with this COMom inside. Thereby gained access to the interface description.
Then (in VS) created a class that implements this interface.
And I try to transfer it to the COM through InvokeMember
comType.InvokeMember("SetupHook", BindingFlags.InvokeMethod, null, comObj, new object[] {SomeClass as IHookCallback});
and so, too, tried
comType.InvokeMember("SetupHook", BindingFlags.InvokeMethod, null, comObj, new object[] {SomeClass});
I receive an error
Exception has been thrown by the target of an invocation.
InnerException
Specified cast is not valid.
Where did I go wrong?

I did.
Write here may be useful to someone my decision.
Initially I have COM object with its own TLB. Written in Delphi 7.
There was also a project written in VS 2010. He created the COM using "Activator Class". And then the work was carried out through late binding. Ie through InvokeMember.
The task was this: were ready COM objects, which could not be replaced. Ie was necessary to ensure backward compatibility and work with existing code as before.
But at the same time, you had to write another 2 COM object with advanced features - support several new methods.
I solved this problem as follows: created a separate TLB on Delphi 7. It announced a completely independent interface (inherited from IDispatch but it does not matter).
Then I created a new COM object that implements the old interface and the new interface.
Then modified the C #. He also created objects through "Activator Class", but after the establishment immediately brought the created object to the second interface (I imported the description of a second TLB in C # project) with the operator "as". If the result was NULL then it was an old COM does not support the new functionality. Otherwise, we have a new COM with enhanced functionality.
Done.

Related

Marshalling Microsoft.VisualBasic.Collection to COM

I have a C# class that has a property(name is List) of type Microsoft.VisualBasic.Collection. We need to exposed this property to COM. For that, I was writing an interface for my class such that the property would marshaled as of UnmanagedType.IDispatch.
Something like this:
[DispId(0x68030000)]
Collection List { [DispId(0x68030000)] [return: MarshalAs(UnmanagedType.IDispatch)] get; }
This piece of code was earlier in VB and was being used by C++ as tye VT_DISPATCH. However, while building the C# library, I get the following error:
C:\Program Files
(x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(4335,5):
error MSB3212: The assembly "Name.dll" could not be converted to a
type library. Type library exporter encountered an error while
processing 'Namespace.InterfaceName.get_List(#0), ProjectName'. Error:
Error loading type library/DLL.
I read through few of the posts online which suggested that such errors might cause because of repetitive GUIDs. But that is not the case. I tried with multiple GUIDs. I don't feel this is an issue with any other attribute that I had set on my Interface since, I am able to marshal other properties and function calls ( some of them use primitive types and others use custom classes) successfully.
This is how it is consumed in the consuming C++ application:
LPDISPATCH result;
InvokeHelper(0x68030000, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
This has become a go live issue with the client and I really do not have much time to continue investigation since this is due tomorrow.
Appreciate any help!
I don't think Microsoft.VisualBasic.Collection is COM-visible. Therefore, you cannot use this type as a return value or parameter in a COM class or interface. However, ICollection (which Microsoft.VisualBasic.Collection implements) is COM visible. If that would suit your purposes, use that as the type of your property rather than Microsoft.VisualBasic.Collection.

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.

Passing a COM method default parameter

I have a ComVisible COM class written in C#. I want to call it from another C# bit of code using COM and pass the default value for the parameter. I can call plenty of other methods without default arguments.
This is the best I can come up with. The first two lines work for all my other methods.
Type mytype = Type.GetTypeFromProgID("MyType");
dynamic myinstance = Activator.CreateInstance(mytype);
object missingValue = System.Reflection.Missing.Value;
myinstance.generatecsvdocument("mystring", ref missingValue);
My method looks like this:
public void generatecsvdocument(string mystring, string rowseperator = "\n")
When I run it I get the error:
The best overloaded method match for 'generatecsvdocument(string,
string)' has some invalid arguments
object missingValue = System.Reflection.Missing.Value;
That cannot work here. It is only valid for a COM method that takes a VARIANT as an argument. Looks like object or dynamic in C#. A very different kind of default argument mechanism than C# supports, it is the callee that determines the default value. In C# it is the caller that determines it, the C# compiler uses metadata to know that default.
Missing.Value turns in a variant of type vtError with the value DISP_E_PARAMNOTFOUND at runtime. Signalling the COM method to use the default value. Not actually that commonly used, usually only implemented in COM servers that support scripting languages. Office Automation is the most common example, probably what inspired you to try this.
But no, your argument is string, not a variant. There is no way to discover the default either when you use late binding, implicit is that you don't know anything about the default value stored in metadata. Otherwise the reason that the vtError mechanism exists, scripting languages have the same problem. The only real way to get ahead is to rewrite the method and test for a null argument, substituting "\n" if that's the case.

Event in MSXML2.XMLHTTP

Does anyone know how MSXML2.XMLHTTP implement its event like onreadystatechange?Since I need construct a COM component with C# which will raise a event, but the client which use vbscript to call this COM object does not support WScript.CreateObject, so I can't handle the COM event.
But I find that MSXML2.XMLHTTP's event can be handled in my client as follows:
Function Hello()
If(objHttp.readyState=4) Then
MsgBox objHttp.responseText
End If
End Function
Set objHttp = CreateObject("MSXML2.XMLHTTP")
Set xmlDoc = CreateObject("MSXML.DOMDocument")
strWebserviceURL="http://localhost:8083/WebService.asmx/HelloWorld"
objHttp.onreadystatechange=getRef("Hello")
objHttp.Open "POST", strWebserviceURL
objHttp.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
objHttp.send()
I've learnt from here that the event of MSXML2.XMLHTTP is not implemented as a COM automation event.
So I wonder How does MSXML2.XMLHTTP implement its event, can anyone give a hint, any help will be appreciated.
"onreadystatechange" is not a COM Automation event because COM Automation events require the caller (mainly written in scripting languages) to implement specific COM Interfaces, which scripting languages often cannot do (look up COM Connection Points to get the full details).
If you start with the link you provided, and add information about MSXML that you can gather with the "OLE View" utility (also called the "OLE-COM Object Viewer"), you have everything you need to figure it out.
Use Ole View and navigate to the MSXML type library ("\Type Libraries\Microsoft XML, v6.0 (ver 6.0)"). Open that, which will open a separate window with the Type Library shown in detail.
From the Type Library viewer, open "CoClasses" and find XMLHTTP60 (that's the exact class name used in the MSDN post). You will verify that it implements the IXMLHTTPRequest interface.
Now, open the Interfaces node and find IXMLHTTPRequest. That will yield the following code (which is generated IDL based on the metadata stored in the type library):
...
[
odl,
uuid(ED8C108D-4349-11D2-91A4-00C04F7969E8),
helpstring("IXMLHTTPRequest Interface"),
dual,
oleautomation
]
interface IXMLHTTPRequest : IDispatch {
...
[id(0x0000000e), propput, helpstring("Register a complete event handler")]
HRESULT onreadystatechange([in] IDispatch* rhs);
};
That tells you how it all works. You will need to add a method to your code that takes an IDispatch reference as an argument. Save the reference. The contract for your object will say that the object provided must have a method named "OnWhateverMyEventNameIs", and possibly specify a list of arguments the method must take.
When your event needs to be signaled, your code will take that reference that was previously provided, look for a method with that name, and execute it if found.
Since you're working on C#, your setter method or property needs to be [COMVisible] (obviously) and it should take an Object reference. The simplest thing for your event triggering implementation might be to use the dynamic support in the language to just call the method (inside a try/catch, in case the method doesn't exist or is otherwise invalid).

Invoke method using Reflection on COM Object

I have an instance of a COM object... which is created like this:
Type type = TypeDelegator.GetTypeFromProgID("Broker.Application");
Object application = Activator.CreateInstance(type);
When I try to invoke a method:
type.GetMethod("RefreshAll").Invoke(application, null);
-> type.GetMethod("RefreshAll") returns null.
When I try to get all the methods with type.GetMethods(), there is only these methods:
GetLifetimeService
InitializeLifetimeService
CreateObjRef
ToString
Equals
GetHashCode
GetType
Where is the RefreshAll Method? And how can I invoke it?
You can't use GetMethod on COM objects, you have to use a different
way:
this.application.GetType().InvokeMember("RefreshAll", BindingFlags.InvokeMethod, null, this.application, null);
I am using this way in a old project that uses COM so it should work ok for you.
I realise this is a late answer but c# 4 changes things a bit with the introduction of the dynamic keyword which was designed with COM-interop in mind.
MSDN:
The COM interop scenario that the C# team specifically targeted in the C# 4 release was programming against Microsoft Office applications, such as Word and Excel. The intent was to make this task as easy and natural in C# as it always was in Visual Basic. [1]
Your code now becomes:
Type type = TypeDelegator.GetTypeFromProgID("Broker.Application");
dynamic application = Activator.CreateInstance(type);
application.RefreshAll(); // <---- new in c# 4
Now you won't see RefreshAll() in Visual Studio statement completion so don't be alarmed. It will compile.
[1] Understanding the Dynamic Keyword in C# 4

Categories