Passing List<String> from C# to native COM interface - c#

I'm doing some prototyping, plugging into a big legacy C++ application. I'm using C# to do this as a proof of concept (eventually I will write the library in C++). I've setup a COM interface between my C++ application and a .net class library and am able to pass strings and ints between. They get automatically marshalled as appropriate (e.g. a managed string goes to BSTR*).
I now have added a new method to my COM interface which returns a List<String>. I figured the method type generated would return an array of BSTR* (BSTR**) and a count a SAFEARRAY, however when I look at the IDL (using oleview), this interface method doesn't even appear. I'm guessing the COM framework isn't sure what a List<String> looks like, so what are my options here? Just using string[]?
I'm not using C++/CX, and I cannot.
Thanks

Related

Adding an in-proc COM Server object to a C# application

I am new to COM and need to add a Server COM object to my c# application so I can call its methods and implement events. The documentation I have says this requires a COM-aware language such as Visual C++. My app is written in C# so I'm not sure how this is going to work. Any direction would be appreciated.
I am writing an app that communicates with a serial hypercom terminal. The device came with a DLL (the com server interface) that I will need to figure out how to use in my c# application.
I added a reference to the DLL to my project, which shows up as a type library. In object explorer I get interfaces, and some classes etc.
Thanks,
Matt
You can add the COM object as a reference. .NET will create an interop assembly to work with the COM object, just like it was a .NET type.
CComObjectRoot can be used as a base class for all COM objects implemented with ATL. As such you don't have to worry to implement something similar in C#, the required methods (AddRef, Release, QueryInterface) will be generated by tlbexp for classes that are tagged with ClassInterface.
STDMETHODIMP is a define which serves to declare the most common methods that can be called from COM (#define STDMETHODIMP HRESULT STDMETHODCALLTYPE). Again if your class is tagged with ClassInterface you will not have to worry about.
Such construction is required in C++ when your class implements several interfaces. I think this is not required if you tell C# compiler that your C# object implement IDispatch, IFPESOlementationEvents. The appropriate code will be written automatically by the compiler.
Probably everything will not make much sense if you are new to COM and C#, I'll suggest to take a look at the various introduction that you may find on the web, like this.

IDispatch parameters passed from Delphi to C#

I am using Delphi 2010 to access objects stored in a C# Assembly written in Visual Studio 2008 using the techniques described in the Hosting CLR in Delphi. These techniques do work and I am able to obtain an instance of a .NET class - via an interface - and call its methods and read it properties. Calling the methods works fine as long as long as the parameters are simple parameters, such as strings, integers, chars, and booleans. But if I try to pass a parameter that is an IDispatch reference, the C#/.NET code only sees the parameter as a null value.
The one difference from the Hosting CLR in Delphi example is that I do not define the interfaces in both Delphi and C# by hand. Instead, I write the interface type library as a RIDL file in Delphi 2010. Then I compile that RIDL file into a TLB file, then use Microsoft's TLBIMP.EXE to build a COM/Interop DLL from the DLL, and then I add a reference to that DLL in the C# project. I use this approach for defining the interfaces of the COM objects that I use for both the interface usage (as described in Hosting CLR in Delphi) and as for defining the interface of the IDispatch interface that I pass as a parameter.
Here is the interface that I use from Delphi and the one that the C# classes that I use implement (.NET Metadata):
[TypeLibType(4160)]
[Guid("C4D342E4-62A0-4049-BF1E-9F2A6EE19E5E")]
public interface ITestInterface
{
[DispId(203)]
int Run(object Test);
}
Delphi can startup the CLR, then instantiate a C# object that implements ITestInterface. Delphi can call Run and I can verify that Run executes properly. Return values are as expected and if I pass parameters other than "object Test" (rewrite the interface), then those parameters are properly seen. But when I pass a reference to an IDispatch object for the Test interface, the C# code sees it only as NULL. Trying to cast the parameter using "as" to the right interface doesn't fail - but it results in a NULL because the initial value seems to be NULL.
I have double checked and the value is not NULL when it leaves Delphi.
I'm guessing that there is some sort of extra marshalling step that I have to perform on the C# side to properly obtain an interface supported by the IDispatch object.
Any thoughts, suggestion would be greatly appreciated.
The CLR won't know what to do with a raw interface pointer and no type info. You'll need to pass a VARIANT of type VT_DISPATCH. That will map to a __ComObject on the C# side, late binding is required to make calls on that reference.

Marshal a list of objects from VB6 to C#

I have a development which requires the passing of objects between a VB6 application and a C# class library. The objects are defined in the C# class library and are used as parameters for methods exposed by other classes in the same library. The objects all contain simple string/numeric properties and so marshaling has been relatively painless.
We now have a requirement to pass an object which contains a list of other objects. If I was coding this in VB6 I might have a class containing a collection as a member variable. In C# I might have a class with a List member variable.
Is it possible to construct a C# class in such a way that the VB6 application could populate this inner list and marshal it successfully? I don't have a lot of experience here but I would guess Id have to use an array of Object types.
Options are more limited through COM than what you have in C#:
You can't use generics (COM does not have support for this, and TLBEXP will leave them out)
There is the olden ArrayList class. Or an array.
The COM interop layer will automatically generate a COM enumerator for a C# class that implements IEnumerable (the non-generic version), you can iterate it on the VB6 side with For Each.
Similarly, it generates an IEnumerable for a COM class that implements an COM enumerator. You can use foreach in C# code to enumerate a VB6 Collection. Choose between them depending on who creates the collection.

Using a COM dll from C# without a type library

I need to use a COM component (a dll) developed in Delphi ages ago. The problem is: the dll does not contain a type library... and every interop feature (eg. TlbImp) in .NET seem to rely on TLBs. The component has been used in Delphi programs here for many years without problems because "It's not much of a problem using COM objects from Delphi, because we know the interfaces" (quote Delphi developer).
Is there any way I can use this DLL from c# without a TLB? I've tried using the DLL as unmanaged, but the only method it exports are DllUnregisterServer, DllRegisterServer, DllCanUnloadNow and DllGetClassObject. I know the names of the classes and functions I'm going to use, if that can be of any help.
UPDATE:
I've tried implementing Jeff's suggestion, but I'm getting this error:
"Unable to cast COM object of type 'ComTest.ResSrvDll' to interface type 'ComTest.IResSrvDll'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{75400500-939F-11D4-9E44-0050040CE72C}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."
This is what I've done:
I got this interface definition from one of the Delphi-guys:
unit ResSrvDllIf;
interface
type
IResSrvDll = interface
['{75400500-939F-11D4-9E44-0050040CE72C}']
procedure clearAll;
function ResObjOpen(const aClientID: WideString; const aClientSubID: WideString;
const aResFileName: WideString; aResShared: Integer): Integer; {safecall;}
...
end;
implementation
end.
From this I've made this interface
using System.Runtime.InteropServices;
namespace ComTest
{
[ComImport]
[Guid("75400500-939F-11D4-9E44-0050040CE72C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IResSrvDll
{
int ResObjOpen(string aClientID, string aClientSubID, string aResFileName, int aResShared);
}
}
And this coclass (got the guid from the delphi-guys)
using System.Runtime.InteropServices;
namespace ComTest
{
[ComImport]
[Guid("75400503-939F-11D4-9E44-0050040CE72C")]
public class ResSrvDll
{
}
}
UPDATE
The solution from Jeff is the way to do it. It is worth noticing, though, that the interface definition must match the COM-components exactly! ie. same order, same names, etc.
You just need the CLS_ID and interface id. I wrote about this specific issue on my blog:
"Using Obscure Windows COM APIs in .NET"
Write a wrapper in VB.Net. VB.Net supports true late binding (no messy reflection). All you need is the progId. You should also implement IDisposable to explicitely manage the component lifecycle.
It is quite frequent that you will encounter an interface implementation that is not backed by a type library (Delphi or otherwise). Shell extensions are one example.
You basically need to make a Windows API call to create the instance through the proper COM function calls. The API will take care of managing the DLL via the exported functions you mentioned earlier.
You will need to recreate the interface definition in C# code but after that you simply create the object, cast it to the interface, and it is no different than anything else. The only real caveat here is, depending on your usage, you may have some threading issues to deal with so check the "threading model" that was used for the DLL and consider your usage based on that.
Here is a link to a tutorial on consuming the interfaces that are not TLB based.
Tutorial
If you've managed to create an instance of the object, you're over the first major hurdle!
Now try this:
myObject.GetType().InvokeMember(
"ResObjOpen", // method name goes here
BindingFlags.InvokeMethod,
null,
myObject,
new object[] {
someClientID, // arguments go here
someSubId,
somFileName,
someInt} );
The reason I think you may need to do this is if the Delphi COM object is not a "dual" object. It may only support late binding, i.e. the kind of invocation you see above.
(In C# 4.0 they're making this easier with the dynamic keyword.)
EDIT: Just noticed something very suspicious. The IID for the interface and the CLSID for the object itself appear to be the same. That's not right.
Given that you've succeeded in creating the object, it would appear to be the CLSID of the object. So it's not the right IID. You need to go back to your Delphi folks and ask them to tell you what the IID of the interface IResSrvDll is.
Edit again: You could try changing the enum member you specify from ComInterfaceType. There should be ones for IDispatch and "dual" - although as your object doesn't support IDispatch, neither of those should be the right choice. The IUnknown setting (which appears in your sample code) should work - suggesting that the IID is wrong.
Yes and no.
All C# (and any CLR language) needs in order communicate with a COM object is a compatible interface signature. Typically specifying the methods, GUID and apartment style of the interface. If you can add this definition into your code base then the TLB is not necessary.
There is a small caveat that comes with that statement. I believe you will get into trouble if you try and use a COM object across apartment boundaries and don't have a suitable TLB registered. I can't 100% remember on this point though.
You can also do late binding and then invoke methods through reflection (myObject.InvokeMember("NameOfTheMethod", options, params, etc.)).
A wrapper should, however, offer better performance and faster marshalling.
I suspect that the dynamic keyword (C# 4.0) will accomplish this. If it does, it will give results which are largely equivalent to invoking the methods, i.e. how Groo suggests.

Registering handlers for .NET COM event in C++

I've been following the 'tutorials' of how to expose a .NET framework through COM ( http://msdn.microsoft.com/en-us/library/zsfww439.aspx and http://msdn.microsoft.com/en-us/library/bd9cdfyx.aspx ). Everything works except for the events part. When I add events to the C# interface the following C++ code is generated:
struct __declspec(uuid("..."))
_MessageEventHandler : IDispatch
{};
struct __declspec(uuid("..."))
IConnection : IDispatch
{
virtual HRESULT __stdcall add_MessageEvent (
/*[in]*/ struct _MessageEventHandler * value ) = 0;
virtual HRESULT __stdcall remove_MessageEvent (
/*[in]*/ struct _MessageEventHandler * value ) = 0;
}
The problem is that I haven't found any info on how to use this in C++. Do I need to derive from _MessageEventHandler and implement operator()? Or something else entirely?
(Note that for the moment I'm also trying the more documented approach of using IConnectionPointContainer and IConnectionPoint.)
It has been a long time since I used COM and at that time I was using Visual C++ 6.0. I remember that implementing sinks for COM connection points was not a straightforward process. There were multiple ways for implementing them, depending if you used MFC or ATL. Maybe there are easier ways now. Here are couple of links that can help you:
Code Project - Sinking events from managed code in unmanaged C++
Code Project - COM - large number of articles about COM
Code Project - Handling COM Events in a Console Application
Code Project - Handling COM Events in a Console Application, Part II
IDispatch is used for runtime binding languages like VB, you wouldn't normally need to do this for a strongly typed language like c#.
When you call a method through IDispatch, What you actually do is build an array containing the method id (called a dispid) and parameters, then hand that to a function that searches through a table of methods by dispid, when it finds one, it uses your parameter array to build a callstack and then call the method. (This is a oversimplification, of course).
So knowing that a class implements IDispatch tells you almost nothing.
So this is ether a false lead, or you are missing the declaration for the MessageEventHandler dispatch tables.
It's not at all surprising that you can't figure out how to implement from this, you are missing some vital information.

Categories