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

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.

Related

Passing a Delegate to a callback function in unmanaged code using c#

I have to register a callback function written in unmanaged code from c#. I have written the following codes:
//Declaration for c++ code
[DllImport("sdk.dll")]
public static extern void sdkSetDequeueCallback(DequeueCallback cbfunc);
//call back delegate
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DequeueCallback(OutData data);
GCHandle dequeueCallbackPinHandle;
Deque = new DequeueCallback(CallBackMethod);
{//try out
// Getting error following line (1 line) , Error : Object contains non-primitive or non-blittable data.
//dequeueCallbackPinHandle = GCHandle.Alloc(Deque, GCHandleType.Pinned);
//GC.KeepAlive(Deque);
}
SetDequeueCallback(Deque);
//Call back method in c#
public async void CallBackMethod(OutData data)
{}
The above code is working without the tryout block, but the issue is application is getting stopped after a random period of time( some times 30 mins, 1 Hr , 8 hr etc). No error detected in try..catch blocks, but getting following NullReferenceException error from the Windows Event Log(application):
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException. I think
this
can be the issue, but tryout codes are not working. Please help me to resolve the issue
You need to insure your 'Deque' reference is alive and not overwritten as long as the callback is being used in C++. You are responsible for holding delegate C# object alive as long as the corresponding C++ callback is used.
Better yet, just use Scapix Language Bridge, it generates C++ to C# bindings (including callbacks), completely automatically. Disclaimer: I am the author of Scapix Language Bridge.

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!!!".

QueryInterface fails at casting inside COM-interface implementation

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.

GetIconInfo function not working properly

I have written an application which is getting the Icon info of current cursor using GetIconInfo function of user32.dll, It works fine for some time, but after some time it starts providing wrong information in ICONINFO.hbmMask (some negative value), and when on the next line I try to get Bitmap object from Bitmap.HBitmap(bitmask), it throws an exception:
A Generic error occured in GDI+.
From there onwords, it continuously gives this exception, as GetIconInfo always return negative value (all this code is working in a loop)..
Can any one tell me what this problem is? and how to avoid the next iteration exception?
Here is the code
while (true)
{
//DLLimport User32.dll
PlatformInvokeUSER32.ICONINFO temp;
//Get the current cursor
IntPtr curInfo = GetCurrentCursor();
Cursor cur;
Icon ic;
if (curInfo != null && curInfo.ToInt32() != 0 && isOSelected)
{
cur = CheckForCusrors(curInfo);
try
{
//Dllimport User32.dll
//after some time the temp.hbmMask begin to get -ive vlaue from following function call
PlatformInvokeUSER32.GetIconInfo(curInfo, out temp);
if (temp.hbmMask != IntPtr.Zero)
{
//due to negative value of hbmMask the following function generates an exception
Bitmap bitmask = Bitmap.FromHbitmap(temp.hbmMask);
ic = Icon.FromHandle(curInfo);
Bitmap bmpCur = ic.ToBitmap();
}
}
catch (Exception ee)
{
//Exception message is
//A Generic error occured in GDI+
//and this loop begins to throw exception continuously
}
}
}// while ended
How large is your loop? GDI+ resources are OS resources and are limited in availability.
You can find out if this is your problem by monitoring the HANDLEs allocated by your process. If GDI+ starts to complain when a certain handle count (HBITMAP or HICON) reaches a limit, then you know you have to deal with your resources more intelligently. You can start by using Task Manager to do this but might want to switch to more sophisticated software like Process Explorer.
If this is your problem, then you need to read about IDisposable and make sure you call Dispose on your objects after you're done with them (won't be rendered anymore). Bitmaps and Icons and most GDI+ objects implement IDisposable.
Furthermore, it's unclear to me, but you may need to call DeleteObject on some of the raw GDI objects themselves (all depends upon where you got their handles).
Check out this PInvoke sample, are you properly deleting the objects you are pulling in through unmanaged code?

Categories