ActiveX Controls

<< Click to Display Table of Contents >>

Navigation:  Programming with Common Vision Blox > Multi-Platform > Type System and Breaking Changes >

ActiveX Controls

 

The Type Library Conundrum

When programming for Win64 and Win32 with the CVB ActiveX controls, you will stumble across one obstacle that will affect C++, Delphi and .Net programmers alike:
Practically all CVB ActiveX controls at some point map handles to properties or function parameters of type long.
On the Win32 platforms this did not hurt, as handles and long are both 32 bits wide.
For the Win64 platforms, however, handles are supposed to be 64 bits wide.

 

The best workaround for this was to use the __int3264 MIDL type for these properties.
When compiling the CVB ActiveX controls for Win32 this type maps to long, when compiling for Win64 it becomes a __int64 in the ActiveX control’s type library.
That way, the compiler generates ActiveX controls that have appropriate type libraries for their target systems, but unfortunately the type library needs to be different between the Win32 and the Win64 version of the ActiveX control.

 

This has consequences for developers using the ActiveX control because usually a development environment uses the ActiveX control’s type library to generate a wrapper class for the object.
If the type library differs between the Win32 and the Win64 then this means that the wrapper classes will differ as well – which makes it difficult to write portable code.
To make things worse, the wizard that generates the C++ ActiveX wrapper classes cannot handle __int64 properties correctly.

 

The C++ Solution

To alleviate the situation we have provided modified wrapper classes for C++, based on what the Visual Studio wizard generates.
These files are located in the folder %CVB%\Wow6432\Visual C++ OCX Wrappers.
Simply replace the files that the Visual Studio wizard generated with these modified files and you will be able to use the same source for the Win32 and Win64 version of your application.
You might need to modify the class names as the Visual Studio wizard by default appends an ordinal number to the class name, which has been omitted in our modified wrappers.

 

C++ is affected by yet another source of trouble with three ActiveX events on the Display OCX (UserPaint) and the DigIO OCX (Listener, Toggler).
These events pass handle/pointer types in their signature, so they again need to pass 4 bytes on the 32bit platform and 8 bytes on the 64bit platform.
In C++ the handlers for ActiveX events are generated at design time and (unlike with .Net) are not part of the ActiveX wrappers.
This means that whenever generating a handler for one of these three events, you will need to use pre-processor switches to generate the proper statements for the target platform.
For the UserPaint event this would like something like this:

 

BEGIN_EVENTSINK_MAP(CMyDlg, CDialogEx)
#ifdef _WIN64
  ON_EVENT(CMyDlg, IDC_CVDISPLAYCTRL1, 15, CMyDlg::UserPaintCvdisplayctrl1, 
           VTS_I4 VTS_I4 VTS_I8 VTS_BOOL VTS_I8)
#else
  ON_EVENT(CMyDlg, IDC_CVDISPLAYCTRL1, 15, CMyDlg::UserPaintCvdisplayctrl1, 
           VTS_I4 VTS_I4 VTS_I4 VTS_BOOL VTS_I4)
#endif
END_EVENTSINK_MAP()
 
void CMyDlg::UserPaintCvdisplayctrl1(long ID, long NumVertices, intptr_t Vertices, 
                                     BOOL Dragging, intptr_t hDC)
{
  // TODO: Add your message handler code here
}

 

The Delphi Solution

As mentioned previously, Delphi differs from Visual Studio in that it generates the ActiveX wrappers only once, the moment they are added to the Development Environment’s toolbar.
Programs that use one of these ActiveX controls can then simply link the pre-generated wrapper.
During wrapper generation Delphi also makes a small mistake (or misinterpretation, depending on how you look at it) when it interprets arguments of type long* (MIDL) as var LongInt (Delphi) in method signatures.
In most cases this interpretation fits the use case, however for example the Display OCX uses parameters of type long* to pass actual pointers to the OCX which contradicts the var LongInt approach.
Therefore CVB 2011 comes with a set of modified wrapper files to work around this issue.
These are located in the folder %CVB%\Lib\Delphi.

 

The .Net Solution

When creating a .Net application, Visual Studio frequently re-generates the ActiveX wrappers that have been generated by aximp and tlbimp.
Manually modifying these files is partly possible, but with Visual Studio redoing them on each project build this is a tedium.
Therefore we suggest another approach: When building your application on either Win32 or Win64 simply use the ActiveX wrappers that have been generated by Visual Studio as they are.
This means that on Win32 the Image property will have type System.Int32 and on Win64 the Image property will have type System.Int64.
Of course this means that when you ship your application for Win32, you will need to ship it with the ActiveX wrappers generated on the Win32 platform – and likewise with Win64.

 

In your application you will typically convert between the Image property and variables of either Cvb.Image.IMG or (recommended) Cvb.SharedImg.
If you route the Image property through a variable of type System.IntPtr you’re on the safe side: You can write platform independent code and your assembly will be usable on Win32 as well as on Win64 – only the wrapper DLLs (AxInterop.<Control Name>.dll and Interop.<Control Name>.dll) will need to match the operating system.
The easiest way to get a matching wrapper DLL for the system on which you are deploying is probably to copy them from any of the .Net Tutorials that come with CVB.

 

To sum it up: Rather than writing e.g.

    public void ProcessImage(Cvb.Image.IMG img)
    {
      
    }
    private void cvImg_ImageUpdated(object sender, EventArgs e)
    {
      ProcessImage(Cvb.Image.ToCvbIMG(cvImg.Image));
    }

 

you should code like this:

    public void ProcessImage(Cvb.SharedImg img)
    {
      
    }
    private void cvImg_ImageUpdated(object sender, EventArgs e)
    {
      ProcessImage(new Cvb.SharedImg(cvImg.Image));
    }

 

This short sample assumes that cvImg is an object of type AxCVIMAGELib.AxCVimage.