none
<VBFixedArray> scheint nicht (mehr) zu funktionieren ... RRS feed

  • Frage

  • Ich definiere eine STruktur mit einem 8-Byte grosse Byte-Array ..

    Structure CANRawMsg
      ''' <summary>
      ''' Gibt den CAN-Identifier wieder. Entweder 29-Bit (CAN2.0B) oder 11-Bit (CAN2.0A)lang
      ''' </summary>
      ''' <remarks>derzeit repräsentieren die Bits 31 und 30 extended, rsp. remote Nachrichten</remarks>
      Public CANID As UInt32
      ''' <summary>
      ''' Data length code - Anzahl der Nutzbytes 
      ''' </summary>
      ''' <remarks>Erlaubt sind Zahlen zwischen 0 und 8</remarks>
      Public DLC As Byte
    
      ''' <summary>
      ''' Die eigentlichen Nutzdaten der Nachricht
      ''' </summary>
      ''' <remarks>Es sind zwischen 0 und 8 Byte Nutzdaten vorgesehen</remarks>
      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> _
      Public Data() As Byte
      ''' <summary>
      ''' enthält nähere Angaben zur Art des Paketes
      ''' </summary>
      ''' <remarks></remarks>
      Public PacketType As PacketType
    
      ''' <summary>
      ''' gibt an, ob es sich bei dem Paket um ein CAN 2.0A oder 2.0B-Paket handelt
      ''' </summary>
      ''' <remarks>Diese Angabe sollte eigentlich später nicht mehr erforderlich sein und unterbleiben!</remarks>
      Public Extended As FrameType
      Public Remote As Boolean
     End Structure
    

    Problematisch ist die Zeile mit <Marshal...   Zuvor habe ich <VBFixedArray> verwendet.

    In meinem Testcode wird nun folgender Code verwandt:

    Dim testmsg As CAN2USB.CAN2USB.CANRawMsg, i As UInt16
      With testmsg
       ReDim .Data(7)
       .Extended = CAN2USB.CAN2USB.FrameType.CAN20B
       .PacketType = CAN2USB.CAN2USB.PacketType.UART_Test
       .Data(0) = 0
       .Data(1) = 1
       .Remote = False
      End With
    

    Lasse ich die Redim-Anweisung weg, dann erhalte ich eine NullReference-Exception beim Zugriff auf Data(0).

    Mir scheint der Versuch, das Array fest auf eine Grösse von 8 Byte zu setzen bleibt wirkunglos.

     

    Warum ?

    Sonntag, 11. Juli 2010 12:24

Antworten

  • Hallo Nico,

    Problematisch ist die Zeile mit <Marshal...   Zuvor habe ich <VBFixedArray> verwendet.

    .NET unterstützt keine Arrays mit festen Längen. VBFixedArray bzw. MarshalAs sind nur in InterOp Szenarien von Bedeutung. Hier übernimmt die Runtime dann die entsprechende Dimensionierung und Übergabe. Somit bleibt nur die Möglichkeit das Array zuvor über die ReDim Anweisung zu dimensionieren.


    Thorsten Dörfler
    Microsoft MVP Visual Basic
    vb-faq.de
    Sonntag, 11. Juli 2010 13:15
    Moderator
  • Hallo Nico,

    Auch für VBFixedArray gilt schon:

    [VBFixedArrayAttribute-Klasse (Microsoft.VisualBasic)]
    http://msdn.microsoft.com/de-de/library/microsoft.visualbasic.vbfixedarrayattribute.aspx

    VBFixedArrayAttribute dient lediglich der Information und reserviert keinen Speicher. Es verändert die Verwendung von Arrays in Strukturen und nicht lokalen Variablen durch Methoden oder API-Aufrufe, die VBFixedArrayAttribute erkennen. Beachten Sie, dass dieses Attribut kein Array mit variabler Länge in ein Array fester Länge umwandelt, und dass Sie mithilfe der Dim-Anweisung oder der ReDim-Anweisung Arrayspeicher zuordnen müssen.
    _____________________

     

    Nur beiläufig erwähnt ... es gäbe unter .NET zum Beispiel: [Puffer fester Größe (C#-Programmierhandbuch)]
    http://msdn.microsoft.com/de-de/library/zycewsya.aspx

    ist ja aber für einen VB-Entwickler hier wohl eher uninteressant.

     


    ciao Frank
    Sonntag, 11. Juli 2010 14:13
  • Hallo Nico,

    warum es mit VBFixedArray bzw. MarshalAs nicht klappt, hat Dir Thorsten bereits erläutert.
    Und da Du nach Deinen anderen Beiträgen die Übertragung über den SerialPort erfolgt,
    haben die Attribute keine Bedeutung.

    Um das Intialisieren des Puffers nicht zu vergessen, bleiben Dir zwei Möglichkeiten:
    Entweder Du verwendest spezielle Konstrukturen - eher häßlich wenn es mehr als eine
    Handvoll Meldungen sind, zumal immer alle Felder gesetzt werden müssen - siehe Strukturen

    Oder Methoden für die einzelnen Meldungen, wo man via Hilfsmethoden das gängigste
    vereinfachen kann - an Deinen Deinen Code angelehnt könnte das z. B. aussehen:

      Public Sub InitializeData()
        Data = New Byte(7) {}
      End Sub
    
      Public Sub InitializeData(ByVal ParamArray bytes() As Byte)
        Data = New Byte(7) {}
        If bytes IsNot Nothing AndAlso bytes.Length > 0 Then
          If bytes.Length > 8 Then
            Throw New ArgumentException("Maximal 8 Bytes in einer Nachricht zugelassen")
          End If
          For index As Integer = 0 To bytes.Length - 1
            Data(index) = bytes(index)
          Next
        End If
      End Sub
    
      Public Shared Sub SendTestMessage()
        Dim testmsg As New CANRawMsg
    
        ' ... weitere Parameter ausgelassen
    
        testmsg.InitializeData()
        ' oder 
        testmsg.InitializeData(0, 1)
    
      End Sub
    
    

    Letztere weist die Bytes entsprechend zu und reduziert das obige

      ReDim .Data(7)<br/>
      .Data(0) = 0<br/>
      .Data(1) = 1<br/>
    
    
    auf eine Anweisung. Wobei Du auch schreiben kannst:

     testmsg.InitializeData(0, 1, 2, 3, 4, 5, 6, 7)<br/>
    
    

    Zu empfehlenen ist Data als Private zu erklären, damit man keine Stellen vergisst,
    und ausschliesslich über die Methoden zugreift (bei Mogeln gibts sonst schnell wieder Probleme).

    Eine Alternative wäre anstatt einer Structure eine Class zu verwenden.
    Damit hast Du ein wenig mehr Overhead - siehe obige Einführung.
    Dann darfst aber auch etwas schreiben wie:

      Private Data As Byte() = New Byte(7) {}
    

    Wobei ich den Weg über die InitializeData Methode (den Namen darfst Du gerne ändern)
    gehen würde. Denn versendest Du viele Meldungen ist eine Anlage auf dem Stack schon
    ressourcenschonender. Und erfordert einfach ein klein wenig mehr an Disziplin.

    Redim bringt an der Stelle übrigens auch Overhead, da es in einen (überflüssigen) Methodenaufruf
    umgesetzt wird, der hier letztendlich durch ein New Byte(7) {} effizienter realisiert werden kann.

    Gruß Elmar

    Sonntag, 11. Juli 2010 16:08
    Beantworter

Alle Antworten

  • Hallo Nico,

    Problematisch ist die Zeile mit <Marshal...   Zuvor habe ich <VBFixedArray> verwendet.

    .NET unterstützt keine Arrays mit festen Längen. VBFixedArray bzw. MarshalAs sind nur in InterOp Szenarien von Bedeutung. Hier übernimmt die Runtime dann die entsprechende Dimensionierung und Übergabe. Somit bleibt nur die Möglichkeit das Array zuvor über die ReDim Anweisung zu dimensionieren.


    Thorsten Dörfler
    Microsoft MVP Visual Basic
    vb-faq.de
    Sonntag, 11. Juli 2010 13:15
    Moderator
  • Hallo Nico,

    Auch für VBFixedArray gilt schon:

    [VBFixedArrayAttribute-Klasse (Microsoft.VisualBasic)]
    http://msdn.microsoft.com/de-de/library/microsoft.visualbasic.vbfixedarrayattribute.aspx

    VBFixedArrayAttribute dient lediglich der Information und reserviert keinen Speicher. Es verändert die Verwendung von Arrays in Strukturen und nicht lokalen Variablen durch Methoden oder API-Aufrufe, die VBFixedArrayAttribute erkennen. Beachten Sie, dass dieses Attribut kein Array mit variabler Länge in ein Array fester Länge umwandelt, und dass Sie mithilfe der Dim-Anweisung oder der ReDim-Anweisung Arrayspeicher zuordnen müssen.
    _____________________

     

    Nur beiläufig erwähnt ... es gäbe unter .NET zum Beispiel: [Puffer fester Größe (C#-Programmierhandbuch)]
    http://msdn.microsoft.com/de-de/library/zycewsya.aspx

    ist ja aber für einen VB-Entwickler hier wohl eher uninteressant.

     


    ciao Frank
    Sonntag, 11. Juli 2010 14:13
  • Hallo Nico,

    warum es mit VBFixedArray bzw. MarshalAs nicht klappt, hat Dir Thorsten bereits erläutert.
    Und da Du nach Deinen anderen Beiträgen die Übertragung über den SerialPort erfolgt,
    haben die Attribute keine Bedeutung.

    Um das Intialisieren des Puffers nicht zu vergessen, bleiben Dir zwei Möglichkeiten:
    Entweder Du verwendest spezielle Konstrukturen - eher häßlich wenn es mehr als eine
    Handvoll Meldungen sind, zumal immer alle Felder gesetzt werden müssen - siehe Strukturen

    Oder Methoden für die einzelnen Meldungen, wo man via Hilfsmethoden das gängigste
    vereinfachen kann - an Deinen Deinen Code angelehnt könnte das z. B. aussehen:

      Public Sub InitializeData()
        Data = New Byte(7) {}
      End Sub
    
      Public Sub InitializeData(ByVal ParamArray bytes() As Byte)
        Data = New Byte(7) {}
        If bytes IsNot Nothing AndAlso bytes.Length > 0 Then
          If bytes.Length > 8 Then
            Throw New ArgumentException("Maximal 8 Bytes in einer Nachricht zugelassen")
          End If
          For index As Integer = 0 To bytes.Length - 1
            Data(index) = bytes(index)
          Next
        End If
      End Sub
    
      Public Shared Sub SendTestMessage()
        Dim testmsg As New CANRawMsg
    
        ' ... weitere Parameter ausgelassen
    
        testmsg.InitializeData()
        ' oder 
        testmsg.InitializeData(0, 1)
    
      End Sub
    
    

    Letztere weist die Bytes entsprechend zu und reduziert das obige

      ReDim .Data(7)<br/>
      .Data(0) = 0<br/>
      .Data(1) = 1<br/>
    
    
    auf eine Anweisung. Wobei Du auch schreiben kannst:

     testmsg.InitializeData(0, 1, 2, 3, 4, 5, 6, 7)<br/>
    
    

    Zu empfehlenen ist Data als Private zu erklären, damit man keine Stellen vergisst,
    und ausschliesslich über die Methoden zugreift (bei Mogeln gibts sonst schnell wieder Probleme).

    Eine Alternative wäre anstatt einer Structure eine Class zu verwenden.
    Damit hast Du ein wenig mehr Overhead - siehe obige Einführung.
    Dann darfst aber auch etwas schreiben wie:

      Private Data As Byte() = New Byte(7) {}
    

    Wobei ich den Weg über die InitializeData Methode (den Namen darfst Du gerne ändern)
    gehen würde. Denn versendest Du viele Meldungen ist eine Anlage auf dem Stack schon
    ressourcenschonender. Und erfordert einfach ein klein wenig mehr an Disziplin.

    Redim bringt an der Stelle übrigens auch Overhead, da es in einen (überflüssigen) Methodenaufruf
    umgesetzt wird, der hier letztendlich durch ein New Byte(7) {} effizienter realisiert werden kann.

    Gruß Elmar

    Sonntag, 11. Juli 2010 16:08
    Beantworter
  • Vielen Dank für die ausführlichen Antworten.

    Ich werde wohl damit leben können, dass eine neue Struktur erst ge-redimt werden muss.

     

    Mittwoch, 14. Juli 2010 12:25
  • Hallo Nico,

    nicht "redimenisionieren" sondern gleich dimensionieren ;-)

    Eine Array Variable hat anfangs keinen Wert (Nothing) und das bereits gezeigte

     Private Data As Byte() = New Byte(7) {}
    
    
    erzeugt das ersten (und bei Dir wohl auch letzte) Array.
    Redim ist nur dort von Wert, wo vorher bereits ein Array zugewiesen war/erzeugt wurde
    und dieses in der Größe verändert (und der Inhalt erhalten bleiben soll).
    In allen anderen Fällen erzeugt es unnötigen Overhead.

    Gruß Elmar

    Mittwoch, 14. Juli 2010 13:03
    Beantworter