VB.NET e i tipi invisibili nella libreria libpxcclr.dll

In questo articolo vedremo come risolvere un annoso problema che "attanaglia" coloro che utilizzano la dll libpxcclr.dll e il linguaggio Visual Basic .NET.

Cominciamo con l'esporre il problema: la libreria in questione, ad in particolare tipi e membri di classi in essa contenuta, hanno un approccio poco standard al mondo .NET. In sostanza esistono classi al cui interno sono definiti membri (ad esempio proprietà) e classi innestate che hanno lo stesso nome e che differiscono per una maiuscola o una minuscola.

Questo non crea alcun problema a linguaggi, come C#, di tipo case sensitive, ma è devastante per Visual Basic facendo si che nè il tipo innestato, nè la proprietà, siano visibili.

Facciamo subito un esempio: prendiamo la classe PXCMImage, come mostrato nella seguente figura, all'interno di questa troviamo la proprietà imageInfo e la struttura ImageInfo.

Se proviamo ad accedere alla proprietà imageInfo in una funzione VB.NET, otteniamo:

Non è visibile, quindi nè la proprietà, nè la struttura interna alla classe.

Per provare che, effettivamente ci sono, è sufficiente verificare cosa accade nel caso di C#:

Come possiamo, quindi, recuperare i dati della struttura "invisibile" in VB.NET?

Un modo per risolvere il problema è quello di ricorrere alla Reflection sapendo il nome della proprietà e come è strutturata la struttura.

Realizziamo due semplici metodi che restituiscono il valore, rispettivamente di una proprietà e di un campo ricorrendo alla reflection:

Private Function GetPropertyValue(Of T)(obj As Object, propertyName As String) As T
    If obj Is Nothing Then Throw New ArgumentNullException("obj")
    If String.IsNullOrWhiteSpace(propertyName) Then Throw New ArgumentNullException("propertyName")
    Dim objtype = obj.GetType()
    Dim propInfo = objtype.GetProperty(propertyName)
    If propInfo IsNot Nothing Then
        Return CType(propInfo.GetValue(obj), T)
    End If
    Return Nothing
End Function 
Private Function GetFieldValue(Of T)(obj As Object, fieldName As String) As T
    If obj Is Nothing Then Throw New ArgumentNullException("obj")
    If String.IsNullOrWhiteSpace(fieldName) Then Throw New ArgumentNullException("fieldName")
    Dim objtype = obj.GetType()
    Dim fieldInfo = objtype.GetField(fieldName)
    If fieldInfo IsNot Nothing Then
        Return CType(fieldInfo.GetValue(obj), T)
    End If
    Return Nothing
End Function
 

Una volta definite le funzioni di base, possiamo creare un paio di extension method da applicare alla clase PXCMImage:

<Extension>
Public Function GetImageInfo(image As PXCMImage) As Object
    If image Is Nothing Then Throw New NullReferenceException()
    Return GetPropertyValue(Of Object)(image, "imageInfo")
End Function
<Extension>
Public Function GetImageInfo(image As PXCMImage,
                             ByRef width As UInt32,
                             ByRef height As UInt32,
                             ByRef colorFormat As PXCMImage.ColorFormat) As Boolean
    If image Is Nothing Then Throw New NullReferenceException()
    Dim imageInfo = image.GetImageInfo()
    If imageInfo IsNot Nothing Then
        width = GetFieldValue(Of UInt32)(imageInfo, "width")
        height = GetFieldValue(Of UInt32)(imageInfo, "height")
        colorFormat = GetFieldValue(Of PXCMImage.ColorFormat)(imageInfo, "format")
        Return True
    Else
        Return False
    End If
End Function
 

In questo modo possiamo scrivere:

Dim data As PXCMImage.ImageData
Dim status = image.AcquireAccess(PXCMImage.Access.ACCESS_READ, PXCMImage.ColorFormat.COLOR_FORMAT_RGB32, data)
If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
    Dim width As UInt32
    Dim height As UInt32
    Dim colorFormat As PXCMImage.ColorFormat
    If image.GetImageInfo(width, height, colorFormat) Then
        Dim bitmap As Bitmap = data.ToBitmap(width, height)
        form.DisplayWebCamImage(bitmap)
        image.ReleaseAccess(data)
    End If
End If
 

Un altro modo per risolvere il problema è quello di inserire nella nostra solution un progetto C# in cui definire gli extension method opportuni.

public static class PXCMImageExtensions
{
    public static bool GetImageInfo(this PXCMImage image,
                                    out UInt32 width,
                                    out UInt32 height,
                                    out PXCMImage.ColorFormat colorFormat)
    {
        if (image == null) throw new NullReferenceException();
        colorFormat = image.imageInfo.format;
        width = image.imageInfo.width;
        height = image.imageInfo.height;
        return true;
    }
}
 

A questo punto ci basterà referenziare il progetto per avere il metodo di estensione sulla classe PXCMImage senza utilizzare reflection.

Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.