Страница: 1 |
Страница: 1 |
Вопрос: структуры
Добавлено: 01.07.06 19:33
Автор вопроса: Sacred Phoenix | ICQ: 304238252
Есть такая структура данных (специально привёл под vb6):
Private Type SPort
High As Byte
Low As Byte
Gravity As Byte
FreezeZonks As Byte
FreezeEnemyes As Byte
Unused As Byte
End Type
Private Type Level
Data(59, 23) As Byte
Unused1 As Long
Gravitation As Byte
Unused2 As Byte
Title(22) As Byte
FreezeZonks As Byte
InfotronsNeeded As Byte
SPortsNumber As Byte
SPorts(9) As SPort
Unused3 As Long
End Type
Как нетрудно вычислить, размер она имеет 1536 байт. Вопрос: как "перегнать" структуру под vb2005 и под ним же читать бинарный файл (данные в нём разложены на такие структуры)?
Ответы
Всего ответов: 7
Номер ответа: 1
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #1
Добавлено: 02.07.06 17:53
Тут вся беда в Data(59, 23) As Byte ...
Размер структуры определяется через Marshal.SizeOf, и при определении этого 2-мерного массива можно было бы указать аттрибут
но в этом случае при передаче структуры через Marshal.Copy теряется размерность массива и он становится 1-мерным. Поэтому я был вынужден пометить массив как SafeArray. Но тогда не получается корректно определить размер структуры, поэтому я ввел в структуру константу..
Вообщем смотри, что у меня получилось, разбирайся...
З.Ы. Если найдешь, как грамотно обойти 2-мерный (чтобы и размерность не терялась и корректно определялся размер)массив-отпишись..
Номер ответа: 2
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #2
Добавлено: 02.07.06 17:53
Imports System.Text
Imports System.Text.Encoding
Imports System.Runtime.InteropServices
Public Class Form1
<StructLayout(LayoutKind.Sequential)> _
Structure SPort
Public High As Byte
Public Low As Byte
Public Gravity As Byte
Public FreezeZonks As Byte
Public FreezeEnemyes As Byte
Public Unused As Byte
End Structure
<StructLayout(LayoutKind.Sequential)> _
Structure Level
Sub New(ByVal Initialize As Boolean)
If Initialize Then
ReDim Me.Data(59, 23)
ReDim Me.Title(22)
ReDim Me.SPorts(9)
End If
End Sub
<MarshalAs(UnmanagedType.SafeArray)> _
Public Data(,) As Byte '1440
Public Unused1 As Int32 '4
Public Gravitation As Byte '1
Public Unused2 As Byte '1
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=23)> _
Public Title() As Byte '23
Public FreezeZonks As Byte '1
Public InfotronsNeeded As Byte '1
Public SPortsNumber As Byte '1
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=10)> _
Public SPorts() As SPort '10*6=60
Public Unused3 As Int32 '4
Public Shared Length As Int32 = 1536
End Structure
'// ЗАГЛУШКА
'// Реальный массив байт будешь читать из Stream файла
Dim bytesStructure(Level.Length - 1) As Byte
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'// Создаем заглушку
'// Создаем экземляр структуры заполняем его данными
Dim tempLevel As New Level(True)
'// Байтовое представление строки
Dim bytesTitle() As Byte = UTF8.GetBytes("Test structure Level" '// <= 23 bytes
With tempLevel
'// Копируем в структуру. (через присвоение нельзя, иначе изменится размер)
Array.Copy(bytesTitle, .Title, bytesTitle.Length)
.Gravitation = 7
.InfotronsNeeded = 3
.Unused1 = 12345
.Data(12, 12) = 222
End With
'// Выделяем блок памяти нужного размера и получаем Handle
Dim memoryHandle As IntPtr = Marshal.AllocCoTaskMem(Level.Length)
'// Копируем структуру в память
Marshal.StructureToPtr(tempLevel, memoryHandle, True)
'// Берем из памяти блок байт и переносим его в наш массив-заглушку
Marshal.Copy(memoryHandle, bytesStructure, 0, Level.Length)
'// Освобождаем память
Marshal.FreeCoTaskMem(memoryHandle)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'// Выделяем блок памяти нужного размера и полyчaем Handle
Dim memoryHandle As IntPtr = Marshal.AllocCoTaskMem(Level.Length)
'// Копируем массив-заглушку по указанному адресу
Marshal.Copy(bytesStructure, 0, memoryHandle, Level.Length)
'// Заполняем нашу структуру
Dim myLevel As Level = CType(Marshal.PtrToStructure(memoryHandle, GetType(Level)), Level)
'// Освобождаем память
Marshal.FreeCoTaskMem(memoryHandle)
'// Check & Show Results
Dim stringTitle As String = UTF8.GetString(myLevel.Title)
'// Обрезаем до нужной длинны
Dim NullCharPos As Int32 = stringTitle.IndexOf(vbNullChar)
If NullCharPos <> -1 Then stringTitle = stringTitle.Substring(0, NullCharPos)
'// Формируем строку
Dim stringBuilder As New StringBuilder
With stringBuilder
.AppendLine(String.Format("Title: {0}", stringTitle))
.AppendLine(String.Format("Gravitation: {0}", myLevel.Gravitation))
.AppendLine(String.Format("InfotronsNeeded: {0}", myLevel.InfotronsNeeded))
.AppendLine(String.Format("Unused1: {0}", myLevel.Unused1))
.AppendLine(String.Format("ata(12, 12): {0}", myLevel.Data(12, 12)))
End With
MessageBox.Show(stringBuilder.ToString, "Check & Show Results", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
End Class
Номер ответа: 3
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #3
Добавлено: 02.07.06 17:58
прочитать файл и разбить его на блоки по 1536 байт, я думаю, для тебя труда не составит..
Номер ответа: 4
Автор ответа:
Sacred Phoenix
ICQ: 304238252
Вопросов: 52
Ответов: 927
Профиль | | #4
Добавлено: 02.07.06 21:21
2 EROS: пасиба большое, щас будем разбираться...
Кста, теоретически, двумерный массив можно заменить одномерным как Data(1439) As Byte. На задачу это не повлияет.
Номер ответа: 5
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #5
Добавлено: 02.07.06 21:33
Тогда вообще никаких проблем!!
Тебе надо будет именить следующе..
-Изиенить аттрибут у Data
Public Data() As Byte
-Изменить Инициализацию массива
-Изменить тестовые строки
.AppendLine(String.Format("ata(12, 12): {0}", myLevel.Data(12, 12)))
И после этого.. Marshal.SizeOf корректно просчитает размер.. так что константа Length уже, в принципе, не нужна..
Номер ответа: 6
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #6
Добавлено: 02.07.06 21:35
Да, и вот еще что.. Объявления структур вынеси ЗА пределы формы...
Номер ответа: 7
Автор ответа:
EROS
Вопросов: 58
Ответов: 4255
Профиль | | #7
Добавлено: 02.07.06 21:48
Еще как вариант, если не хочешь на прямую работать с памятью, мо можно сделать следующее:
В структуре сделать конструктор, который на вход получает массив байт (длиной 1536). И непосредственно в самом конструктуре разложить все эти байты по полочкам.. к примеру: первый 1440 байт это Data, затем берем следующие 4 байта, делаем из них Int32 и загоняем в Unused1. Ну и т.д.. до тех пор, пока не заполним всю структуру..
Но сам понимаешь, это будет несколько медленне, чем через Marshal.Copy...