Running COM interface on different threads - c#

I am writing library plugin for application which provide COM interface IPlugin. I must implement function Start(), because that application is calling my library through ProgID and giving instance of interface there.
Short sample of usage:
[ComVisible(true)]
[Guid({GUID})]
[ClassInterface(ClassInterfaceType.None)]
public class Program : IPlugin
{
[STAThread]
public void Start(IInterface myInterface)
{
var a = myInterface.GetValue();
}
}
When I am using interface in main thread, all went fine.
But, when I want to use that interface in other thread, I am getting exception:
Unable to cast COM object of type 'System.__ComObject' to interface type 'IInterface'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{GUID}' failed due to the follow error: No such interface supported
I tried "save" interface to GlobalInterfaceTable and "load" it on other thread, tried CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream, tried solution from this SO source and many others, always getting same error (using "saved" and "loaded" interface on same thread is still O.K., so I think, that I am using it right).
Maybe I am misunderstood something, I cannot give here all my attempts to solve it.
I am trying it so long, but I have no solution.
Please, if there is some more things to try, I'll be glad to sort it out.

Related

Why cannot I cast my COM object to the interface it implements in C#?

I have this interface in the dll (this code is shown in Visual Studio from metadata):
#region Assembly XCapture.dll, v2.0.50727
// d:\svn\dashboard\trunk\Source\MockDiagnosticsServer\lib\XCapture.dll
#endregion
using System;
using System.Runtime.InteropServices;
namespace XCapture
{
[TypeLibType(4160)]
[Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
public interface IDiagnostics
{
[DispId(1)]
void GetStatusInfo(int index, ref object data);
}
}
So I created a COM server with such class:
[ComVisible(true)]
[Guid(SimpleDiagnosticsMock.CLSID)]
[ComDefaultInterface(typeof(IDiagnostics))]
[ClassInterface(ClassInterfaceType.None)]
public class SimpleDiagnosticsMock : ReferenceCountedObject, IDiagnostics
{
public const string CLSID = "281C897B-A81F-4C61-8472-79B61B99A6BC";
// These routines perform the additional COM registration needed by
// the service. ---- stripped from example
void IDiagnostics.GetStatusInfo(int index, ref object data)
{
Log.Info("GetStatusInfo called with index={0}, data={1}", index, data);
data = index.ToString();
}
}
Server seems to work fine, and I am able to use the object from a VBScript. But then I try to use it from another C# client:
[STAThread]
static void Main(string[] args)
{
Guid mockClsId = new Guid("281C897B-A81F-4C61-8472-79B61B99A6BC");
Type mockType = Type.GetTypeFromCLSID(mockClsId, true);
IDiagnostics mock = (IDiagnostics)Activator.CreateInstance(mockType);
//var diag = mock as IDiagnostics;
object s = null;
mock.GetStatusInfo(3, ref s);
Console.WriteLine(s);
Console.ReadKey();
}
And it fails with
Unable to cast COM object of type 'System.__ComObject' to interface
type 'XCapture.IDiagnostics'. This operation failed because the
QueryInterface call on the COM component for the interface with IID
'{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}' failed due to the following
error: No such interface supported (Exception from HRESULT: 0x80004002
(E_NOINTERFACE)).
What am I doing wrong?
I have also tried to use InvokeMember, and that kinda worked except that I wasn't able to get the ref-returned data parameter.
EDIT: added STAThread attribute to my Main procedure. This does not solve the issue, but you really should use STAThread with COM unless you're absolutely sure you don't need it. See Hans Passant's answer below.
This exception can be a DLL Hell problem. But the simplest explanation is for what's missing from your snippet. Your Main() method is missing the [STAThread] attribute.
That's an important attribute that matters when you use COM objects in your code. Most of them are not thread-safe and they require a thread that's a hospitable home for code that cannot support threading. The attribute forces the state of the thread, the one you can set explicitly with Thread.SetApartmentState(). Which you can't do for the main thread of an app since Windows starts it, so the attribute is used to configure it.
If you omit it then you the main thread joins the MTA, the multi-threaded apartment. COM is then forced to create a new thread to give the component a safe home. Which requires all calls to be marshaled from your main thread to that helper thread. The E_NOINTERFACE error is raised when COM cannot find a way to do that, it requires a helper that knows how to serialize the method arguments. That's something that needs to be taken care of by the COM developer, he didn't do that. Sloppy but not unusual.
A requirement of an STA thread is that it also pumps a message loop. The kind you get in a Winforms or WPF app from Application.Run(). You don't have one in your code. You might get away with it since you don't actually make any calls from a worker thread. But COM components tend to rely on the message loop to be available for their own use. You'll notice this by it misbehaving, not raising an event or deadlocking.
So start fixing this by applying the attribute first:
[STAThread]
static void Main(string[] args)
{
// etc..
}
Which will solve this exception. If you have the described event raising or deadlock problems then you'll need to change your application type. Winforms is usually easy to get going.
I cannot otherwise take a stab at the mocking failure. There are significant deployment details involved with COM, registry keys have to be written to allow COM to discover components. You have to get the guids right and the interfaces have to be an exact match. Regasm.exe is required to register a .NET component that's [ComVisible]. If you try to mock an existing COM component, and got it right, then you'll destroy the registration for the real component. Not so sure that's worth pursuing ;) And you'll have a significant problem adding a reference to the [ComVisible] assembly, the IDE refuses to allow a .NET program to use a .NET assembly through COM. Only late binding can fool the machine. Judging from the COM exception, you haven't gotten close to mocking yet. Best to use the COM component as-is, also a real test.
So, the problem was that my DLL with IDiagnostics interface was generated from a TLB, and that TLB never got registered.
Since the DLL was imported from the TLB, RegAsm.exe refuses to register the library. So I used the regtlibv12.exe tool to register the TLB itself:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe "$(ProjectDir)\lib\Diagnostics.tlb"
Then everything magically started to work.
Since regtlibv12 is not a supported tool, I still don't know how to do this properly.

Refer to COM object in the worker thread in C#

I have a COM object SerInterface objCANinterface; which is created in VC++ 6.0 ATL
which i was able to successfully call the methods from my C# GUI.(ThreadingModel is Both when checked in registry)
But i have created a thread which has to use this object opened inside this thread to continues the process.
so i have added the following statements in my constructor class as
public MyClass()
{
// Start the thread with a ParameterizedThreadStart.
ParameterizedThreadStart start = new ParameterizedThreadStart(callBackLoad);
threadFlash = new Thread(start);
threadFlash.SetApartmentState(ApartmentState.STA);
...
}
void myFun()
{
threadFlash.Start(objCANinterface);
threadFlash.Join(); //Wai
}
[STAThread]
public void callBackLoad(object refinterface)
{
//I am not able to access objCANinterface inside my thread so i have passed the object as a parameter and trying to cast
SerInterface objSrinterface = (SerInterface)refinterface;
}
This is not successful Could you please let us know how do i solve this issue. I am getting the following error
An unhandled exception of type 'System.InvalidCastException' occurred in MyClass.exe
Additional information: Unable to cast COM object of type 'System.__ComObject' to interface type 'MYINTERFACELib.SerInterface'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{37C34F3C-0082-46F5-9974-37CEB2E1C2EE}' failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).
Please let me know how to solve this issue. Thanks in advance.
# oberfreak.........
have you tried it by using "as" as well? Wen you step down with a debugger, which type is it, is it the expected type? Are your interops included in the assembly or in a seperate file?
# oberfreak, how do i use "As", could you please let me know how to use that. My interops are included along with my exe. I was able to perfectly work out of the thread.
# Hans Passant:
You forgot to register the type library in your .rgs script and you also didn't create the proxy/stub project. A threading model of Both still requires an object to be marshaled when it was created on an STA thread and used on another STA thread. Which tends to make it pointless to create a new thread. Get ahead by creating the object on the worker thread instead.
# Hans Passant : I have registered my type library and interop also created in the C# which is making me to use inside my C# code.I cannot create a new object as I need to use the same object to continue the CAN sequence commands. Could you please provide any sample for better marshalling inside a thread.
I have unregistered/removed the reference from the Exe and have registered and added the reference back to the tool. Then it worked with the parameter thread passing which made as STA thread. Thanks for the answers proposed.

C# Winform COM interop and Threading

I have been reading (again) a lot about COM interopability and threading. Well, now, I'm facing some trouble understanding the following scenario.
I have a COM Dll referenced in VS2008 named ComDbLib and by the VS2008 object browser I can see that it has a:
public class ComDb2Class that inherit interfaces IComDb2, ComDb2,
IComDb;
public interface IComDb2 that inherit interface IComDb;
public interface ComDb2 that inherit interface IComDb2;
public interface IComDb;
Making a call from a MTA BGWorker thread to a method that creates a new object of ComDb2Class type (ComDb2Class objectDb = new ComDb2Class();) fails with the well known exception:
«Unable to cast COM object of type 'ComDb2Class' to interface type 'IComDb2'. This operation failed because the QueryInterface call on the COM component for the interface with IID 'XXXXXXXX' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).».
I need the BGWorker so it must be part of the solution.
Making a call to the same method but from an STA thread there is not an exception. Anyway I want to avoid this approach because i would need to make a new Thread each time i want to call this method (there will be numerous calls to the method and these calls are not localized in a single place in the aplication but are dispersed through it), this approach would be time costly.
Now the part that intrigues me is that if you invoke the method that creates an object of type (IComDb objectDb = new ComDb2Class();) from a MTA BGWorker thread it doesn't give any exception!
I can't understand it, for me it would make sense that the exception would arise from any calls from a MTA Thread, because the COM would a STA and could only be invoked in a STA Thread.
Geezz i'm getting lost here :s can you explain to me why the class constructor failed in the first approach and not in the last??

Why is this C# COM class usable from VBScript but not JScript?

Consider the automation-compatible COM library in C#, given below. It follows a common COM pattern of having a visible factory coclass FooFactory implementing ICreateFoos which creates an object of type IFoo. FooFactory is the only coclass in the type library. (The factory pattern is particularly useful with COM, as it does not allow for parameterized constructors).
In the code below, I'm finding that I cannot access the returned IFoo interface from jscript unless I make the FooImpl class ComVisible (by uncommenting commented lines; this causes it to appear as a coclass in the type library). There is no such problem accessing this from VBscript.
That is, I can run this VBScript:
set ff = CreateObject("jstest.FooFactory")
set foo = ff.CreateFoo(0)
foo.Foo
But this functionally identical JScript fails, with the error "C:\temp\jstest\jstest.js(4, 1) Microsoft JScript runtime error: 'foo' is null or not an object":
var ff = new ActiveXObject("jstest.FooFactory");
var foo = ff.CreateFoo(0)
//WScript.Stdout.WriteLine(null==foo)
foo.Foo();
If I uncomment the line, I can see that null==foo is false.
Why does this happen? Is it a bug? Note that I think this is a problem is a combination of JScript and the C#/.net-specific implementation (possibly of IDispatch), because I have other similar COM servers - implemented in C++ - that do not exhibit this problem from JScript.
The problem goes away if I uncomment the commented lines in the code below, making FooImpl visible as a coclass - but I specifically do not want to do this as I don't want to expose implementation details. A workaround seems to be to make FooImpl ComVisible, but mark its constructor internal, which prevents clients from being able to CoCreate it, but that's hardly elegant.
I'm running on WinXP SP3 with Visual Studio 2005, .net 2, and have been able to reproduce the issue on a completely fresh install of TinyXP on a VirtualBox (both with Windows Script Host 5.7), and also on Windows 7 Ultimate using .net SDKs 2.0, 3.0, 3.5 and 4.0 (WSH 5.8). All OSes were 32-bit.
The library code:
using System;
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
namespace jstest
{
[ComVisible(true)]
public interface ICreateFoos
{
IFoo CreateFoo(int importantNumber);
}
[ComVisible(true)]
public interface IFoo
{
void Foo();
}
[ComVisible(true)]
public class FooFactory : ICreateFoos
{
public IFoo CreateFoo(int importantNumber)
{ // in *this* version, we don't use importantNumber yet
return new FooImpl();
}
}
//[ComVisible(true)]
public class FooImpl : IFoo
{
public void Foo()
{
Console.WriteLine("Foo");
}
}
}
You can compile and register (you may have to run as admin to regasm) this with
csc /target:library jstest.cs
regasm /codebase jstest.dll
When QueryInterface is called against the IFoo object returned from CreateFoo for the IDispatch GUID it returns E_NOINTERFACE unless ComVisible is set for the actual implementing class.
When jscript prepares to call the Foo method it calls QueryInterface several times, including with this specific GUID, and since an error is returns it doesn't try to use Invoke.
When vbscript prepares to call the Foo method it does not check the interface supports IDispatch. QueryInterface is called, once, with the GUID for IDispatchEx but it seems to simply assume IDispatch will be supported.

Marshal.ReleaseComObject throws exception

Trying to use a COM visible .NET class via other .NET application and get exception:
Message: The object's type must be
__ComObject or derived from __ComObject.
Parameter name: o
Stack Trace: at
System.Runtime.InteropServices.Marshal.ReleaseComObject(Object
o)
The class looks as follows:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IViewer : IComInteropDefinedInterface
{
}
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("[some guid]")]
public class MyViewer : UserControl, IViewer
{
//IViewer implementation
}
I register the component with:
regasm [Assembly Path] /tlb /codebase
The client application, which is also in .NET instantiates successfully the given class, but when he callsMarshal.ReleaseComObject() it gets the exception described above.
Any idea for solving this problem?
EDIT:
Unfortunately I can't provide the client application code for instantiating my object. However I know the client is using the same method to instantiate real COM objects.
I got this problem recently, when reimplementing a native COM to managed code.
The solution was to ask if the object is a native COM with Marshal.IsComObject, only native COMs must be release with Marshal.ReleaseComObject.
This is code:
if (Marshal.IsComObject(comObject))
{
Marshal.ReleaseComObject(comObject);
}
comObject = null;
Important: you have to be sure, no use that object after been Released.
For a more detailed explanation read this post: http://blogs.msdn.com/b/visualstudio/archive/2010/03/01/marshal-releasecomobject-considered-dangerous.aspx
But how are you creating the class instance? Simply using the expression new MyViewer() doesn't create a COM object. Instead it creates a plain old .Net object which cannot be used with the ReleaseComObject method.
Based on your sample code, in particular the line about MyViewer having an implementation, it doesn't sound like you're dealing with a COM object. Instead it looks like you have a managed object which implements a COM interface.
In order to use the ReleaseComObject you'd need to actually have a COM / RCW object.
My guess would be that you are actually not using COM but simply use a referenced .NET class. If your project contains code like
MyViewer viewer = new MyViewer();
and you have added the library containing MyViewer not as a COM reference, you are actually not using COM.
I would rather try:
if (comObject != null)
{
if (System.Runtime.InteropServices.Marshal.IsComObject(comObject))
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(comObject);
}
comObject= null;
}

Categories

Resources