COM Interop, RPC server is unavailable in c# - c#

I am using a COM Interop and i am instantiating the COM class object from the interop dll
So, few times the object is instantiated successfully and make remote procedure calls without any problem but sometimes it throws an exception like RPC Server is unavilable.
The COM Component i am using is written in VB and i am consuming that component in c#.
So, can anybody tell me the possible reasons for the problem(RPC Server is Unavailable) and solutions to this problem.
I am helpless with this issue by now.
So, Advance thanks if you can help me out

After reviewing my approach for COM implementation I found the bug. I was using a static class for initializing the COM instance and initialization stuff was taking place in static constructor. So, initialization was being done once per application session. In the case, when the com instance gets corrupted or is disposed, then making calling to COM methods throws exception (RPC Server is unavailable).
So, I used following approach for overcoming the issue
try
{
m_COMObject.SomeMethod();
}
Exception(exception exception)
{
DisposeCOMObject();
InitializeCOMOBject();
COMObject.Somethod();
}
public void DisposeCOMObject()
{
m_COMObject = null;
var process = Process.GetProcessesByNames("COM .exe").FirstDefault();
if(process != null)
{
process.kill();
}
}
public void InitializeCOMObject()
{
m_COMObject = null;
m_COMObject = new COMObject();
}
if instance of COM is unable to make call then dispose the instance and reinitialize the COM and get instance, then make call to RPC Server.

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.

Calling COM Interop API from a Background Thread?

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

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;
}

WPF Thread: "COM object that has been separated from its underlying RCW cannot be used."

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.

Killing COM object from C#

I have a COM object that we are calling from C#. This works great, and I have my own pool of objects that I can use whenever I want. Now I need to kill the object. I've tried releasing the COM object explicitly and then garbage collecting from another thread, but that does nothing. Does anyone have any other ideas to kill this object? Thanks for the help.
I've tried
System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);
GC.Collect();
GC.WaitForPendingFinalizers ();
myApp = null;
and I create it by
myApplication.ApplicationClass myApp = new myApplication.ApplicationClass();
this is the full code
this com object is written in vb6, below is the C# code calling vb6 component
myApplication.ApplicationClass myApp = new myApplication.ApplicationClass();
string user = this._User;
string pass = this._Pass;
string company = this.companyNumber;
try
{
if (myApp.Login(ref user, ref pass, ref company))
{
//Perform some action
}
else
{
throw new System.Exception(MESSAGE_LOGINERROR);
}
}
Finally
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);
GC.Collect();
GC.WaitForPendingFinalizers();
M2MApp = null;
}
Thanks,
Pradeep
maybe just swapping two lines, because myApp IS your reference to your object:
or try repeating until zero:
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp)>0)
;
myApp = null;
GC.Collect();
GC.WaitForPendingFinalizers ();
Make sure, you don't have any other variable referring (holding a reference) to myApp.
Alternatively, a call to FinalReleaseComObject could also be of help.
Note: I looked at the docs & suggesting this as an alternative. Use your discretion when making a call to this method.
I've had a similar problem in the past and attempted all the things you've mentioned, but was unable to resolve it. Eventually I had to forcefully terminate my process to kill the COM object.
In order to be able to completely release the COM object you need to load it in a separate AppDomain which you can unload after you have called ReleaseComObject() and done other proper cleanup for your object.
This has proved to be the only reliable way for me in the past.

Categories

Resources