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.
Related
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.
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??
I instantiate a service class that contains a COM interop component that does terminal automation. I am using Task Library (TPL) from Microsoft. I want to make calls to the COM object from a TPL task (background thread) so my UI doesn't freezes while the COM object is working.
However when I call my first function from the background thread (which receives an IntPtr) an COM Exception is thrown detailing HRESULT: 0xC0000005.
I know this is an access violation exception and I think I'm not mashaling my object the right way.
How can I call methods from a COM object created in the main thread from a background thread?
public void Button1_Click(object sender, EventArgs e)
{
var comWrapper = new COMWrapper(); // A simple wrapper for a COM object
Task.Factory
.StartNew(() => LoadStuff(comWrapper))
.ContinueWith(() => {
// Output results...
});
}
int LoadStuff(COMWrapper w)
{
return w.LoadStuffFromCOM();
}
Method that calls the COM object:
int LoadStuffFromCOM()
{
string buffer;
IntPtr pointer = Marshal.StringToHGlobalUni(buffer);
return comObject.GetValue(pointer); // Exception here...
}
Many legacy COM objects were made to run inside of a desktop application. That means they expected to run on the UI thread, with the Windows message pump as the only synchronizing method.
You're now trying to run that code in an environment it may never have "heard of". There's a good chance that you have violated the assumptions the author made when he wrote the code.
The code may work if you don't violate the assumptions, but if you do, then you're going to have a problem (or two, or two dozen).
It is possible to have COM / OLE Interop objects running on background, but they must be compiled with correct Threading Model
In case of Delphi for MTA it should be compiled
initialization
TTypedComObjectFactory.Create(ComServer, TSomeLogic, Class_SomeLogic,
ciMultiInstance, tmFree);
end.
In case of STA by default it uses
initialization
TTypedComObjectFactory.Create(ComServer, TSomeLogic, Class_SomeLogic,
ciMultiInstance, tmApartment);
end.
It should be simmilar in C/C++ and other unmanaged languages
More information can be found here:
http://msdn.microsoft.com/en-us/library/ff647812.aspx#scalenetchapt07 _topic11
IF you are desperate you could spawn a whole separate process which executed the com code. Then you would only have to write the ipc
I have an intermittent problem where I cannot create an instance of the System.Windows.Forms.Form class in my C# library. I log exceptions in my program, but unfortunately nothing was recorded for this problem. It might not be relevant, but my library, called via COM (i.e. there is no Main method).
So I threw a test exception, and it was caught and logged successfully -- so I'm thinking that maybe something screwy is going on in the message loop?
Update 1
The Form is created using the following code in a Thread which has ApartmentState.STA set.
someForm = new Form();
someForm.Visible = false;
someForm.Text = "Hello world!";
It's just a guess, since you've provided no code and little context, but might you be missing the STAThread attribute on your entry point method (Main)? This is necessary for WinForms apps that make use of COM.
I am getting following error:
"COM object that has been separated from its underlying RCW cannot be used."
I am sure the problem is because COM object is being called not on the thread it has been created - STA. I tried to implement IDisposable but it has not worked for me.
There is a couple of posts dealing with similar problem but which still do not solve my issue:
Is it safe to call an RCW from a finalizer?
Release Excel Object In My Destructor
Could anyone post an example/explain how COM object should be correctly accessed from another thread?
Here is minimal code which shows the problem:
using System;
using System.Threading;
namespace Test.ComInterop
{
public class Program
{
MyCom _myCom;
[STAThread]
static void Main( string[] args )
{
new Program();
}
public Program()
{
_myCom = new MyCom();
// this method call works
string version = _myCom.ComMethod();
StartThread();
}
private void StartThread()
{
Thread t = new Thread( UIRun );
t.SetApartmentState( ApartmentState.STA );
t.Start();
}
void UIRun()
{
TestUI window = new TestUI();
window.Show();
// this method call fails
window.Title = _myCom.ComMethod();
window.Closed += ( sender2, e2 )
=> window.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
}
}
class MyCom
{
private dynamic _com;
public MyCom()
{
_com = Activator.CreateInstance(
Type.GetTypeFromProgID( "Excel.Application" ) );
}
public string ComMethod()
{
return (string) _com.Version;
}
}
}
The problem is your program's startup thread. It creates the COM object, starts a thread, then exits. As part of the cleanup of that main thread, .NET calls CoUninitialize() and that's the end of the COM object. Getting that error is the expected result.
There's just no point in letting your main startup thread exit like that. Let it do the work now done by your own thread, problem solved.
Sorry for maybe not answering directly your question. This is merely an advice for handling it in a different way. Hope it helps.
With COM interop with Excel there are quite a few pitfalls - not directly related to COM I think, but to how Excel COM is implemented.
I struggled a lot with COM interop with Excel (and also MsProject). For Excel the only good solution was a dedicated thread for handling the whole Excel communication from creation until termination. There are a few design flaws in the Excel API. Some method calls are not stateless, meaning two threads will have a hard time to make the stuff work. It would be safer to delegate all the communication to one thread and handle the communication with other threads yourself.
Beside this, the thread you are using for communication MUST also have the en/US culture (LCID issues). This usually results in an other message:
Old format or invalid type library
but might be useful to you to know.
Usually this is because the underlying COM object has been released from its wrapper - this happens when you manually release it via Marshal.Release or the managed wrapper is disposed. Using it on the wrong thread will simply cause any calls to the COM object to actually occur on the thread it was created on - I was stung by this in the past, it has thread affinity for execution.
You don't appear to be disposing of the wrapper, but I'm not sure what the affect of the dynamic variable will be.
Have you tried changing your thread apartment state to MTA?
Try making your MyCom class inherit for DispatcherObject. After you start up your other thread, do a _myCom.Dispatcher.Run(). When you want to talk to your COM object, just do a _myCom.Dispatcher.BeginInvoke/Invoke.