How to add Overlay Objects to display from a worker thread in C#


I’m fighting with trying to add Overlay Objects to Display, but from a worker thread. AddOverlayObjectNET() returns always with a true, but nothing is in shown in the display. Here my code:

Cvb.Plugin.TStaticTextOutPlugInData textData = new Cvb.Plugin.TStaticTextOutPlugInData("dummy text", false);
Cvb.SharedPixelList pixelList;
Cvb.Image.CreatePixelList(3, out pixelList);
double[] position = new double[2];
position[0] = 100;
position[1] = 100;
Cvb.Image.AddPixel(pixelList, position);

if (cvDisp1.InvokeRequired)
  cvDisp1.Invoke((MethodInvoker)delegate () { cvDisp1.RemoveAllOverlayObjects(); });
  cvDisp1.Invoke((MethodInvoker)delegate () { cvDisp1.AddOverlayObjectNET("StaticTextOut", "bla", false, false, 0xFF, 0xFF00, true, 1, pixelList, textData.ToIntPtr()); });
  cvDisp1.Invoke((MethodInvoker)delegate () { cvDisp1.Refresh(); });

This code works perfect as long I have it implemented in the main thread (of course then without the invokes).

Any idea what is wrong there?


Hi @Greg,

we have investigated this and it turns out that this is in fact not a multithreading issue but a pointer aliasing problem. In the method AddOverlayObjectNET the last parameter (the pointer to the plugin data) has been marked as void* in the display control’s MIDL description (in the .Net wrappers generated by tlbimp and aximp this parameter is exposed as an IntPtr). We fully expected this to result in 64 bit being passed to the control via the IDispatch interface, but as it turns out, this is marshaled as a variant of type VT_HANDLE which is internally treated as an integer (long story short: you pass in a pointer which is converted to an integer by the CLR and then passed to the control which converts it back to a pointer from which the upper 32 bits are garbage).

The fact that you are now seeing this in you non-UI threads is coincidence more than anything else - it just happens that the overlay plugin data structure seems to usually end up inside the 32 bit address range in your UI thread and - maybe only slighty - above that in your non-UI threads.

Unfortunately this is impossible to fix without adding a new function (or modifying the existing one, which is something we decided against so that we don’t break existing applications - after all the issue is non-existent in a 32 bit process), which is what we did. The fixed ActiveX control will ship with the new version (13.1) of :cvb: which is due to be available soon (currently we’re on what will hopefully be the final release candidate). The new display control should also run fine on a 13.0 installation (if you need the OCX before the 13.1 release please PM me). Sorry for the inconvenience!

By the way: Any call that does not pass a plugin data structure will not suffer from the flaw in AddOverlayObjectNET, nor will for example AddLabel be affected. So one possibility would be to work around the issue with a label instead of the StaticTextout plugin until the fix is available.

On a different matter: I’d advise to rearrange your lambdas so that you don’t waste performance on superfluous thread transitions and synchronizations (you can do all three operations in one go):

if (cvDisp1.InvokeRequired)
  cvDisp1.BeginInvoke((MethodInvoker)delegate () 
    cvDisp1.AddOverlayObjectNET("StaticTextOut", "bla", false, false, 0xFF, 0xFF00, true, 1, pixelList, textData.ToIntPtr());

As :cvb: 13.01.000 is out now, this can finally be addressed properly: The Display control has a new method, AddOverlayObjectNET2 which is used just like AddOverlayObjectNET except for the last parameter that caused the issue (the last parameter no longer expects an IntPtr that it later on mutilated but uses long (the C# 64 bit sized type long…) across the board.

Note that starting with :cvb: 13.01.000 AddOverlayObjectNET in 64 bit applications will always return false (at first sight this might look fairly inconvenient, but I think it’s better to make the problem appear quickly than lull the user in a false sense of security).

If AddOverlayObjectNET2 doesn’t show up immediately in your programming environment, please close Visual Studio, then delete the bin and obj folder of your project then open your project again.