Вызов класса против ошибки структуры брошены


Может кто-нибудь объяснить, почему это работает:

 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Public Class MEMORYSTATUSEX

         Public Sub New()
            Me.dwLength = CType(Marshal.SizeOf(GetType(MEMORYSTATUSEX)), UInt32)
        End Sub

        Public dwLength As UInt32

        Public dwMemoryLoad As UInt32

        Public ullTotalPhys As UInt64

        Public ullAvailPhys As UInt64

        Public ullTotalPageFile As UInt64

        Public ullAvailPageFile As UInt64

        Public ullTotalVirtual As UInt64

        Public ullAvailVirtual As UInt64

        Public ullAvailExtendedVirtual As UInt64
    End Class

А это не так:

 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Structure MEMORYSTATUSEX

        Public Sub New(ByVal dwlength As UInt32)
            Me.dwLength = dwlength
        End Sub

        Public dwLength As UInt32

        Public dwMemoryLoad As UInt32

        Public ullTotalPhys As UInt64

        Public ullAvailPhys As UInt64

        Public ullAvailPageFile As UInt64

        Public ullTotalVirtual As UInt64

        Public ullAvailVirtual As UInt64

        Public ullAvailExtendedVirtual As UInt64
    End Structure

При вызове обеих структур / класса из другого класса, например:

 Dim newpoint As New Structures.MEMORYSTATUSEX()

        GlobalMemoryStatusEx(newpoint) 

И 2-й:

 Dim newpoint As New Structures.MEMORYSTATUSEX(CType(Marshal.SizeOf(GetType(Structures.MEMORYSTATUSEX)), UInt32))

            GlobalMemoryStatusEx(newpoint) 

Они оба находятся внутри класса, когда я вызываю второй с параметром size, он вызывает" первый шанс AccessViolationException " на вызове GlobalMemoryStatusEx(newpoint) и завершает работу приложения.

Я не могу понять, почему, так как значение dwLength инициализируется на обоих в конструкторе? Я так ведь?

Причина, по которой я хочу изменить первый пример, заключается в том, что я перемещаю все структуры в класс только структуры, и думал, что это было бы хорошей идеей, пока я не смог понять, почему это не сработало, так как одно и то же значение устанавливается перед вызовом API.

P / Invoke объявления:

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Private Shared Function GlobalMemoryStatusEx(lpBuffer As Structures.MEMORYSTATUSEX) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

Подробности ошибки:

Первое случайное исключение типа ' System.AccessViolationException ' произошло в клиенте.exe

Дополнительная информация: попытка чтения или запишите защищенную память...

Если я нажму кнопку Продолжить:

Первое случайное исключение типа ' System.Отражение.TargetInvocationException ' произошло в mscorlib.dll

Дополнительная информация: исключение было вызвано целью вызова

1 2

1 ответ:

  Private Shared Function GlobalMemoryStatusEx(lpBuffer As Structures.MEMORYSTATUSEX) ...

Аргумент lpBuffer являетсяуказателем . Требуется, чтобы функция могла записывать элементы структуры. Указатели хорошо спрятаны внутри VB.NET, они взрывают программы в пух и прах, когда они не используются должным образом. Множество возможных неудач, исключение AccessViolationException-это более мягкая разновидность.

Два основных способа получить указатель в VB.NET можно объявить аргумент ByRef, если аргумент является типом значения. Или если это ссылочный тип (ключевое слово класса), то ссылка автоматически становится указателем во время выполнения и должен быть передан ByVal.

Таким образом, допустимые комбинации - это структура, передаваемая ByRef, или класс, передаваемый ByVal. Класс также требует явного <StructLayout>, чтобы принудить CLR упорядочивать свои поля предсказуемым образом. Вы получаете его бесплатно со структурой. Что, следовательно, является логическим выбором.

Вы должны использовать My.Computer.Info, это уже pinvokes GlobalMemoryStatusEx () для вас.