QueryInterface fails at casting inside COM-interface implementation - c#

I am creating a tool in c# to retrieve messages of a CAN-network (network in a car) using an Dll written in C/C++. This dll is usable as a COM-interface.
My c#-formclass implements one of these COM-interfaces. And other variables are instantiated using these COM-interfaces (everything works perfect).
The problem: The interface my C#-form implements has 3 abstract functions. One of these functions is called -by the dll- and i need to implement it myself. In this function i wish to retrieve a property of a form-wide variable that is of a COM-type.
The COM library is CANSUPPORTLib
The form-wide variable:
private CANSUPPORTLib.ICanIOEx devices = new CANSUPPORTLib.CanIO();
This variable is also form-wide and is retrieved via the devices-variable:
canreceiver = (CANSUPPORTLib.IDirectCAN2)devices.get_DirectDispatch(receiverLogicalChannel);
The function that is called by the dll and implemented in c#
public void Message(double dTimeStamp)
{
Console.WriteLine("!!! message ontvangen !!!" + Environment.NewLine);
try
{
CANSUPPORTLib.can_msg_tag message = new CANSUPPORTLib.can_msg_tag();
message = (CANSUPPORTLib.can_msg_tag)System.Runtime.InteropServices.Marshal.PtrToStructure(canreceiver.RawMessage, message.GetType());
for (int i = 0; i < message.data.Length; i++)
{
Console.WriteLine("byte " + i + ": " + message.data[i]);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
The error rises at this line:
message = (CANSUPPORTLib.can_msg_tag)System.Runtime.InteropServices.Marshal.PtrToStructure(canreceiver.RawMessage, message.GetType());
Error:
Unable to cast COM object of type 'System.__ComObject' to interface type CANSUPPORTLib.IDirectCAN2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{33373EFC-DB42-48C4-A719-3730B7F228B5}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
Notes:
It is possible to have a timer-clock that checks every 100ms for the message i need. The message is then retrieved in the exact same way as i do now. This timer is started when the form starts. The checking is only done when Message(double) has put a variable to true (a message arrived).
When the timer-clock is started in the Message function, i have the same error as above
Starting another thread when the form starts, is also not possible.
Is there someone with experience with COM-interop ?
When this timer

I wonder if Message is called on a thread different from the one that created the canreceiver.
Do you know the threading model of CANSUPPORTLib.CanIO? If it's apartment-threaded, you may need to marshal a reference from the main UI thread to the thread called by Message somehow.
Alternatively, assuming you can change the source code of the C++ dll, and depending on your other threading requirements and constraints, you could change it to be free-threaded, in which case an object can be simultaneously accessed from multiple threads without marshalling.

Related

Shell32 call fails from an event handler, but works from Main

I hope the explanation makes sense.
I have a line
(new Shell()).NameSpace(TargetDirectory).CopyHere(SourceFile, 16 | 2048);
It works just fine when I'm calling it sequentially. Meaning I have my Main method and in that, I call the method that contains the line.
What doesn't work is calling the containing method from an event. I have a FileSystemWatcher and in it's Changed event, I call the method containing this line. Then I get
System.InvalidCastException: 'Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{34936BA1-67AD-4C41-99B8-8C12DFF1E974}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).'
And here's where I'm stuck. I now that line itself works just fine so I've no idea what to repair and how.

DirectShow ISampleGrabber.SetCallback throws InvalidCastException after showing a C# MessageBox

I am using DirectShow's SampleGrabber to capture an image from a webcam video. Codes are writing in C# and use .NET wrappers of DirectShow's interfaces to do COM communication. The following BufferCB, first, copies image data to a local array variable, disables SampleGrabber callback, processes a received image data, and uses MessageBox to shows a result.
public int BufferCB(double sampleTime, IntPtr pBuffer, int bufferLen)
{
if (Monitor.TryEnter(lockSync))
{
try
{
if ((pBuffer != IntPtr.Zero))
{
this.ImageData = new byte[bufferLen];
Marshal.Copy(pBuffer, this.ImageData, 0, bufferLen);
// Disable callback
sampleGrabber.SetCallback(null, 1);
// Process image data
var result = this.Decode(new Bitmap(this.ImageData));
if (result != null)
{
MessageBox.Show(result.ToString());
}
// Enable callback
sampleGrabber.SetCallback(this,1);
}
}
finally
{
Monitor.Exit(this.parent.lockSync);
}
}
return 0;
}
Now, if the result = null, hence the MessageBox.Show never runs, both clamping calls sampleGrabber.SetCallback() will run without any issue. Once the result != null, and the MessageBox shows up, the call sampleGrabber.SetCallback(this,1) will throw the InvalidCastException as below:
Unable to cast COM object of type 'System.__ComObject' to interface type 'DirectShowInterfaces.ISampleGrabber'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{6B652FFF-11FE-4FCE-92AD-0266B5D7C78F}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
If I stop in a VS debugger and add a watch of ((ISampleGrabber)sampleGrabber).SetCallback(sampleGrabber, 1), I will get the ArgumentException with the message of "Cannot find the method on the object instance." instead.
May someone experience the same issue can give me some advise. Thank you.
BufferCB and SampleCB calls take place on worker threads, which typically belong to MTA. Your graph initialization on the other hand typically takes place on STA thread. DirectShow API and filters volantarily ignore COM threading rules while .NET enforces thread checks an raises exception on attempts to use COM interface pointer on a wrong thread. You are hitting exactly this issue.
You don't need to reset callback and then set it back. Use SampleCB callback instead and it happens as a blocking call. Before you complete the processing the rest of the streaming is on hold.

C# Dynamic COM in Windows Server 2012

Got an issue where I am a COM type in c# using
this.rtwbType = Type.GetTypeFromProgID(progId, true);
this.rtwb = Activator.CreateInstance(this.rtwbType);
I am then doing some stuff, and when I am done I call exit on the rtwb - so it can close down, and then calling:
Marshal.ReleaseComObject(this.rtwb);
In 2008 R2 this is fine and dandy - but the instance we take to 2012 an exception is thrown here.
System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
like I say works fine elsewhere.
Any pointers?
The following code reproduces this error (at least under Windows 8.1):
var type = Type.GetTypeFromProgID("InternetExplorer.Application", true);
dynamic ie = Activator.CreateInstance(type);
ie.Quit(); // this disconnects the COM proxy from the out-of-proc IE object
Marshal.ReleaseComObject(ie); // throws
Apparently, the implementation of ReleaseComObject is doing something more than calling IUnknown::Release, when it deals with a COM proxy object. My guess is, it may be calling IRemUnknown::RemRelease, which returns HRESULT with an error, so ReleaseComObject throws, because the out-of-proc object has been already disconnected and destroyed with ie.Quit().
Presumably, this behavior was introduced in Windows Server 2012.
The best thing you could probably do is to ignore this specific error:
try
{
Marshal.ReleaseComObject(ie);
}
catch (COMException ex)
{
// I'm getting 0x80010108, rather than 0x800706BA
// The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED)
if ((uint)ex.ErrorCode != 0x80010108)
throw;
}
Updated: this is the expected behavior for an API like Quit, it doesn't violate any COM rules. The goal of Quit is to provide an API for explicit shutdown. I believe it uses CoDisconnectObject internally, as a part of the shutdown process. This disconnects all external proxy references, so the server knows it can shut down safely.
The fact that ReleaseComObject is still trying to do some stuff after that (like, most likely, calling IUnknown::QueryInterface on the disconnected proxy) is not a fault of the COM server.
I'm assuming that rtwb is an InternetExplorer.Application, as Noseratio's test seems to replicate the issue rather well.
It seems Internet Explorer's Quit() method is not really safe to call outside the application's context, e.g. through out-of-process automation. It violates the rules of COM server applications.
For instance, Office applications keep running while there are external references, even after Quit(), so this is really a bit unexpected.
For safety, you can let the reference go when quitting, using the OnQuit event:
using System;
using System.Threading;
using System.Runtime.InteropServices;
public class TestIE
{
public static void Main()
{
Console.WriteLine("Creating application");
dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
Console.WriteLine("Created application");
app.Visible = true;
app.OnQuit += new Action(() => {
Console.WriteLine("Entered OnQuit");
Marshal.ReleaseComObject(app);
app = null;
Console.WriteLine("Leaving OnQuit");
});
Console.WriteLine("Sleeping");
Thread.Sleep(5000); // enough time to see if iexplore.exe is running
Console.WriteLine("Slept");
Console.WriteLine("Quitting");
app.Quit();
Console.WriteLine("Quit");
Console.WriteLine("Sleeping");
Thread.Sleep(5000); // enough time to see if iexplore.exe is running
Console.WriteLine("Slept");
Console.WriteLine(app == null);
}
}
If you really need to use ReleaseComObject, discard any exceptions it might throw. I'm not sure if you should discard only COMException, try it out during development.

C# .NET Error Attaching to Running Instance of Office Application

I am working on an Excel Addin using C#, Visual Studio 2012. I am trying to get the instance of Excel's Application object so as to keep track of the currently active workbook and worksheet (ActiveWorkbook, ActiveWorksheet).
I see that most other related questions on SO have replies suggesting to use the following:
(Excel.Application)Marshal.GetActiveObject("Excel.Application");
I have also tried using this:
(Excel.Application)Globals.ThisAddIn.Application;
In both of the cases I get NullReferenceException. After looking at the workaround suggested here: http://support.microsoft.com/kb/316125/en-us, I tried the following to test both methods.
public CurrentSpreadSheet()
{
try
{
this.CurrentApplication = (Excel.Application)Globals.ThisAddIn.Application;
}
catch (NullReferenceException)
{
MessageBox.Show("Excel application object not registered. Trying plan B..");
//Getting Excel's application object instance
int iSection = 0, iTries = 0;
tryAgain:
try
{
iSection = 1; //Attempting GetActiveObject
this.CurrentApplication = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
iSection = 0; //GetActiveObject succeeded so resume or go for normal error handling if needed
this.CurrentApplication.Visible = true;
}
catch (Exception err)
{
System.Console.WriteLine("Visual C# .NET Error Attaching to Running Instance of Office Application .. yet.");
if (iSection == 1)
{
//GetObject may have failed because the
//Shell function is asynchronous; enough time has not elapsed
//for GetObject to find the running Office application. Wait
//1/2 seconds and retry the GetObject. If we try 20 times
//and GetObject still fails, we assume some other reason
//for GetObject failing and exit the procedure.
iTries++;
if (iTries < 20)
{
System.Threading.Thread.Sleep(500); // Wait 1/2 seconds.
goto tryAgain; //resume code at the GetObject line
}
else
{
MessageBox.Show("GetObject still failing. Process ended.");
}
}
else
{
//iSection == 0 so normal error handling
MessageBox.Show(err.Message);
}
}
}
}
The output is:
Excel application object not registered. Trying plan B..
GetObject still failing. Process ended.
In some rare cases "plan B" does work; I don't see the second message box.
CurrentSpreadSheet is a singleton and I intend to update it during startup from the provided class ThisAddIn.
In ThisAddIn I have something like:
private CurrentSpreadSheet css = CurrentSpreadSheet.Instance;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
///...Some code
css.updateCurrentSpreadSheet();
}
Is there a better way of getting the Application object? If this is not possible right during the startup, is there a better way by which I can keep track of currently active worksheet/workbook right from the startup of excel/my add-in? Currently I am depending on the Application object (e.g. (Excel.Workbook)this.CurrentApplication.ActiveWorkbook;) and some event handlers to keep track of the current workbook and worksheet.
I tried using ExcelDNA:
this.CurrentApplication = (Excel.Application)ExcelDna.Integration.ExcelDnaUtil.Application;
This works some of the times but mostly gives this error:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> ExcelDna.Integration.XlCallException: Exception of type 'ExcelDna.Integration.XlCallException' was thrown.
Keep in mind that an Office add-in runs inside the Office process. So you never want to find an external process. The boilerplate ThisAddIn class that you get when you use a Office project template hands you the Application object you are looking for on a silver platter, use its Application property. The first time you can get to it is in the Startup event, make sure you don't try it earlier. So don't do anything drastic in your constructor and don't initialize class members with initialization expressions, wait until Startup fires.
The relevant MSDN page is here, "Accessing the Object Model of the Host Application" section.
You might take a closer look at http://netoffice.codeplex.com/ There you can find more stuff about Add-In's for Word/Excel/Powerpoint.
Also keep in mind the GuidAttribute when you load your Add-In, because depending on your Office version it won't work anymore and also, check to kept your project whether 32/64-bit, best is Any CPU. Also, keep in mind to register your Add-In into the registry for further usage. If you want further information, don't hesitate to write me a mail.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.guidattribute.aspx

Error notification from COM event handler, using interop

I'm consuming a com object in c#, using com-interop. I provide an event handler to the object, which it calls after doing its thing. Here's the problem. I have some sanity-check code in the handler that throws an exception if all is not well with the world:
_comObj.OnRequestCompleted += (int requestID, RequestStatus status) =>
{
if (<sanity check fails>)
throw new Exception("This is insane!!!");
...
}
The com object is apparently swallowing those exceptions, because I never see them in my app. What do I need to do to "surface" those exceptions so that I am notified when they occur?
This is not unexpected behavior if the event is raised by the COM server. Exceptions are not allowed to cross the COM divide, there is no binary interop standard for exceptions. The CLR installs a catch-all exception handler in its interop layer, the code that calls your event handler. The exception you raised is translated to an HRESULT, the standard way in which COM code reports failure. Which will be 0x80131500 for a generic Exception object.
What happens next is entirely up to the COM server, it needs to do something useful with the HRESULT it got. Not seeing it do anything it all is not terribly unexpected, function call return values are often ignored, especially so with notification style calls. You certainly won't get anything resembling a managed exception.
Work with the COM component vendor or author to work something out, if possible. Which isn't very often the case. Not much you can do beyond just logging the failure then. Call Environment.Exit() if this is a gross problem that cannot possibly be ignored. This isn't really any different from having your program terminated with an unhandled exception.
You could use the Application.Current.Dispatcher.Invoke() method, by supplying a delegate in which you throw your exception.
If you look at the standard C++ COM server implementation that's generated by all Visual Studio versions (what you get using the wizards like this: ATL Tutoral Step 5: Adding an Event), the C++ code for raising an event (named a "connection point" in COM terms) looks like this.
HRESULT Fire_MyCustomEvent()
{
...
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
CComVariant varResult;
DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
}
return hr;
}
So, hr does contain an error code but in general, the COM server implementer just does
Fire_MyCustomEvent()
Because in general, there can be more than one caller. What would you do if one caller fail? Fail everyone? In the general case, nothing. See this interesting discussion here on a similar subject: Observers Should Never Throw Exceptions
That said, if you own the COM server, you can catch Invoke error, and in this example, pass EXCEPINFO as the 7th argument to invoke. It will contain the .NET Exception text "This is insane!!!".

Categories