本节介绍通过高级接口实验台进行的一个HID编程实例。
在USB设备软件的开发过程中,借助于一些工具软件的测试会对USB设备的信息获取和通信过程又更深入的理解。USB测试软件有很多,如USBView、BusHound等,下面针对一个具体的HID设备的API通信软件的开发,对这两个工具软件作一简单介绍。
高级接口实验台中,有一个HID实验,在该实验中,实验台通过固件软件设计了一个简单的HID仿真设备,在PC的API通信软件的开发过程中,可以借助USB工具软件对实验台的HID设备进行测试。
6.1 获得描述符
将高级接口实验台通过USB电缆与PC连接后,在实验台上通过菜单选择HID实验,实验台显示器显示器上会显示HID实验界面。
实验台的HID实验是一个为了学习HID编程而专门设计的一个简单的HID仿真设备,设备中设计了8个寄存器(R1~R8),可以通过USB接口与主机交换数据。其中R1和R2两个寄存器只是数据存储单元,主机可以对这两个寄存器进行附值,也可以读取寄存器的值。寄存器R3~R8构成了一个日期和时钟,6个寄存器的值分别表示年、月、日、时、分、秒。时钟在当前值的基础上运行,可以通过主机对时钟进行设置,也可以读取当前的时钟值。
在实验台上设计了自动回传功能,如果自动回传打开,时钟每一秒向主机传送一次报表。实验台可以显示发送和接收的有效数据字节数。
图6-1 高级接口实验台HID实验界面
USBView是Microsoft提供的一个简单的USB测试软件,该工具软件是一个完全的绿色软件,只有一个EXE程序文将,不需要安装,在Windows环境下直接运行。
USBView其主要功能是获得USB设备的各个描述符。
可以运行USBView获得实验台仿真的HID设备的描述符,运行USBView后显示以下程序界面。
图6-2 USBView运行界面
窗口的左侧显示全部的USB设备连接树,在其中找到一个显示“USB人体工程学设备”的分支,选中后右侧窗口显示全部的(报表描述符除外)描述符。USBView显示的高级接口实验台的HID仿真设备的描述符如下。
- Device Descriptor:
- bcdUSB: 0x0110
- bDeviceClass: 0x00
- bDeviceSubClass: 0x00
- bDeviceProtocol: 0x00
- bMaxPacketSize0: 0x10 (16)
- idVendor: 0x045E (Microsoft Corporation)
- idProduct: 0x930A
- bcdDevice: 0x0100
- iManufacturer: 0x01
- iProduct: 0x02
- iSerialNumber: 0x03
- bNumConfigurations: 0x01
-
- ConnectionStatus: DeviceConnected
- Current Config Value: 0x01
- Device Bus Speed: Full
- Device Address: 0x02
- Open Pipes: 4
-
- Endpoint Descriptor:
- bEndpointAddress: 0x81
- Transfer Type: Bulk
- wMaxPacketSize: 0x0010 (16)
- bInterval: 0x0A
-
- Endpoint Descriptor:
- bEndpointAddress: 0x01
- Transfer Type: Bulk
- wMaxPacketSize: 0x0010 (16)
- bInterval: 0x0A
-
- Endpoint Descriptor:
- bEndpointAddress: 0x82
- Transfer Type: Interrupt
- wMaxPacketSize: 0x0040 (64)
- bInterval: 0x01
-
- Endpoint Descriptor:
- bEndpointAddress: 0x02
- Transfer Type: Interrupt
- wMaxPacketSize: 0x0040 (64)
- bInterval: 0x01
6.2 设备的初始化
在将高级接口实验台连接到计算机并操作实验台进入HID接口实验后,Windows系统会发现设备并读取设备的各种描述符,可以通过一个USB工具软件截取Windows和设备之间的请求应答过程。工具软件BusHound可以实现这个功能。
BunHound是一个功能全面的总线分析仪软件,可以实现对计算机通过各种接口连接的设备的通信过程进行截取和分析。图8-9是BusHound的设备连接树(Devices)界面。
当高级接口实验台与计算机通过USB接口连接并操作实验台进入HID实验,可以在BusHound的设备连接树中找到一个“USB人体工程学设备”项目。选中该项目可以实现对设备的通信过程的截取。
选中高级接口实验台的HID设备项,进入到采集(Capture)界面,先控制实验台退出HID实验,按BusHound的采集界面的Stop按钮,再按Run按钮,然后控制实验台进入HID实验界面。Windows系统发现设备并请求设备的各种描述符,然后完成对设备的必要的设置,BusHound可以采集到以上通信过程。
图6-3 BusHound的设备树显示界面
6-4 BusHound的采集显示界面
下面分析BusHound截取的数据内容。
• 主机发送:80 06 00 01 00 00 12 00
Get_Descriptor请求,请求设备回传设备描述符。
• 设备发送:12 01 10 01 00 00 00 10 5e 04 0a 93 00 01 01 02 03 01
设备描述符内容:USB版本=1.1、类别/协议码=0、EP0的最大包尺寸=10、VID=045E、PID=930A、版本=1.0、厂商、产品和序列号字符串索引、配置数=1。
• 主机发送:80 06 00 02 00 00 09 00
Get_Descriptor请求,请求设备回传配置描述符。
• 设备发送:09 02 37 00 01 01 04 80 32
配置描述符内容:该描述符及后续描述符总长度=55、支持接口数=1、配置标示符=1、总线供电、最大100mA。
• 主机发送:80 06 00 02 00 00 37 00
Get_Descriptor请求,请求设备回传配置、接口、HID和端点描述符。
• 设备发送:09 02 37 00 01 01 04 80 32 09 04 00 00 04 03 00 00 05 09 21 01 01 00 01 22 34 00 07 05 81 02 10 00 0a 07 05 01 02 10 00 0a 07 05 82 03 40 00 01 07 05 02 03 40 00 01 按顺序为:
9字节配置描述符:含义同上。
9字节接口描述符:标示符=0、支持的端点=4、类别=HID。
9字节HID描述符:版本=1.01、有1个报表描述符(长度为52)
7字节端点描述符:1号批量输入,包尺寸=16。
7字节端点描述符:1号批量输出,包尺寸=16。
7字节端点描述符:2号中断输入,包尺寸=64、1ms轮询。
7字节端点描述符:2号中断输出,包尺寸=64、1ms轮询。
• 主机发送:00 09 01 00 00 00 00 00
Set_Configuation请求,配置号=1。
• 主机发送:21 0a 00 00 00 00 00 00
Set_Idle请求,设定间隔时间为0。
• 设备发送:04 00 00 c0
STALL,不支持请求。
• 主机发送:81 06 00 22 00 00 74 00
Get_Descriptor请求,请求设备回传报表描述符。
• 设备发送:06 a0 ff 09 01 a1 01 09 02 a1 00 06 a1 ff 09 03 09 04 15 80 25 7f 35 00 45 ff 75 08 95 40 81 02 09 05 09 06 15 80 25 7f 35 00 45 ff 75 08 95 40 91 02 c0 c0
报表描述符。
6.3 HID测试程序的实现
使用Visual Basic编写一个针对高级接口实验台的应用程序,实现与设备的通信。该测试程序实现以下功能:
• 查找设备:根据指定的VID和PID实现设备查找;
• 获得设备能力:调用相应的API,获得设备的能力;
• 发送报表:将界面输入的R1~R8的数值按照报表的指定格式传送到设备;
• 接收报表:将设备的界面显示的R1~R8的数值读取到应用程序;
• 显示API函数调用信息:显示每一个API函数的调用结果;
• 关闭设备。
下图是程序运行的界面显示:
图6-5 针对实验台的HID测试程序显示界面(VB)
在获得能力的API函数的调用结果中,输入报表和输出报表的长度为65字节,在发送和接收报表时的长度设定都需要设为65。但在该实验中真正用到的有效数据只有8个寄存器的数据,占用其中的8个字节,报表的首字节(第0个)为0,表示Report ID。第1、2个字节分别55h、AAh为用户定义的标示字。第3、4字节为用户定义命令编码,输出报表中为01、08,输入报表中为02、08。从第5到第12字节顺序为R1、R2、…R8的值。之后的数据为保留字节。
-
-
-
-
- Option Explicit
-
-
- Dim Capabilities As HIDP_CAPS
- Dim DataString As String
- Dim DetailData As Long
- Dim DetailDataBuffer() As Byte
- Dim DeviceAttributes As HIDD_ATTRIBUTES
- Dim DevicePathName As String
- Dim DeviceInfoSet As Long
- Dim ErrorString As String
- Dim HidDevice As Long
- Dim LastDevice As Boolean
- Dim MyDeviceDetected As Boolean
- Dim MyDeviceInfoData As SP_DEVINFO_DATA
- Dim MyDeviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA
- Dim MyDeviceInterfaceData As SP_DEVICE_INTERFACE_DATA
- Dim Needed As Long
- Dim OutputReportData(7) As Byte
- Dim PreparsedData As Long
- Dim Result As Long
- Dim Timeout As Boolean
-
- Dim MyVendorID As Long
- Dim MyProductID As Long
-
-
-
-
-
-
- Function FindTheHid() As Boolean
-
- Dim Count As Integer
- Dim GUIDString As String
- Dim HidGuid As GUID
- Dim MemberIndex As Long
-
- LastDevice = False
- MyDeviceDetected = False
-
-
-
- Result = HidD_GetHidGuid(HidGuid)
- Call DisplayResultOfAPICall("GetHidGuid")
-
- GUIDString = Hex$(HidGuid.Data1) & "-" & Hex$(HidGuid.Data2) & "-" & Hex$(HidGuid.Data3) & "-"
-
- For Count = 0 To 7
- If HidGuid.Data4(Count) >= &H10 Then
- GUIDString = GUIDString & Hex$(HidGuid.Data4(Count)) & " "
- Else
- GUIDString = GUIDString & "0" & Hex$(HidGuid.Data4(Count)) & " "
- End If
- Next Count
-
- lstResults.AddItem " GUID for system HIDs: "
- lstResults.AddItem " " & GUIDString
-
-
-
- DeviceInfoSet = SetupDiGetClassDevs(HidGuid, vbNullString, 0, (DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE))
-
- Call DisplayResultOfAPICall("SetupDiClassDevs")
- DataString = GetDataString(DeviceInfoSet, 32)
- lstResults.AddItem " DeviceInfoSet:" & DeviceInfoSet
-
-
-
- MemberIndex = 0
-
- Do
-
-
- MyDeviceInterfaceData.cbSize = LenB(MyDeviceInterfaceData)
- Result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, HidGuid, MemberIndex, MyDeviceInterfaceData)
-
- Call DisplayResultOfAPICall("SetupDiEnumDeviceInterfaces")
- If Result = 0 Then LastDevice = True
-
-
- If Result <> 0 Then
-
- lstResults.AddItem " DeviceInfoSet for device #" & CStr(MemberIndex) & ": "
- lstResults.AddItem " cbSize = " & CStr(MyDeviceInterfaceData.cbSize)
- lstResults.AddItem " InterfaceClassGuid.Data1 _
- = " & Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data1)
- lstResults.AddItem " InterfaceClassGuid.Data2 _
- = " & Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data2)
- lstResults.AddItem " InterfaceClassGuid.Data3 _
- = " & Hex$(MyDeviceInterfaceData.InterfaceClassGuid.Data3)
- lstResults.AddItem " Flags = " & Hex$(MyDeviceInterfaceData.Flags)
-
-
-
-
-
- MyDeviceInfoData.cbSize = Len(MyDeviceInfoData)
- Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, MyDeviceInterfaceData, 0, 0, Needed, 0)
-
- DetailData = Needed
-
- Call DisplayResultOfAPICall("SetupDiGetDeviceInterfaceDetail")
- lstResults.AddItem " (OK to say too small)"
- lstResults.AddItem " Required buffer size for the data: " & Needed
-
-
- MyDeviceInterfaceDetailData.cbSize = Len(MyDeviceInterfaceDetailData)
-
- ReDim DetailDataBuffer(Needed)
-
-
- Call RtlMoveMemory(DetailDataBuffer(0), MyDeviceInterfaceDetailData, 4)
-
-
- Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, _
- MyDeviceInterfaceData, VarPtr(DetailDataBuffer(0)), DetailData, Needed, 0)
-
- Call DisplayResultOfAPICall(" Result of second call: ")
- lstResults.AddItem " MyDeviceInterfaceDetailData.cbSize: " & CStr(MyDeviceInterfaceDetailData.cbSize)
-
- DevicePathName = CStr(DetailDataBuffer())
- DevicePathName = StrConv(DevicePathName, vbUnicode)
- DevicePathName = Right$(DevicePathName, Len(DevicePathName) - 4)
- lstResults.AddItem " Device pathname: "
- lstResults.AddItem " " & DevicePathName
-
-
-
- HidDevice = CreateFile(DevicePathName, GENERIC_READ Or GENERIC_WRITE, _
- (FILE_SHARE_READ Or FILE_SHARE_WRITE), 0, OPEN_EXISTING, 0, 0)
-
- Call DisplayResultOfAPICall("CreateFile")
- lstResults.AddItem " Returned handle: " & Hex$(HidDevice) & "h"
-
-
-
- DeviceAttributes.Size = LenB(DeviceAttributes)
- Result = HidD_GetAttributes(HidDevice, DeviceAttributes)
-
- Call DisplayResultOfAPICall("HidD_GetAttributes")
- If Result <> 0 Then
- lstResults.AddItem " HIDD_ATTRIBUTES structure filled without error."
- Else
- lstResults.AddItem " Error in filling HIDD_ATTRIBUTES structure."
- End If
-
- lstResults.AddItem " Structure size: " & DeviceAttributes.Size
- lstResults.AddItem " Vendor ID: " & Hex$(DeviceAttributes.VendorID)
- lstResults.AddItem " Product ID: " & Hex$(DeviceAttributes.ProductID)
- lstResults.AddItem " Version Number: " & Hex$(DeviceAttributes.VersionNumber)
-
-
- If (DeviceAttributes.VendorID = MyVendorID) And (DeviceAttributes.ProductID = MyProductID) Then
- lstResults.AddItem " My device detected "
- lstResults.AddItem " -------------------------------------------------------------------------------------------"
- lblHID.Caption = "HID Find"
- MyDeviceDetected = True
- cmdGetCaps.Enabled = True
- cmdClose.Enabled = True
- txtVendorID.Enabled = False
- txtProductID.Enabled = False
- Else
- MyDeviceDetected = False
- Result = CloseHandle(HidDevice)
- DisplayResultOfAPICall ("CloseHandle")
- End If
- End If
-
- MemberIndex = MemberIndex + 1
-
- Loop Until (LastDevice = True) Or (MyDeviceDetected = True)
-
- End Function
-
-
-
-
-
- Private Function GetErrorString(ByVal LastError As Long) As String
-
- Dim Bytes As Long
- Dim ErrorString As String
- ErrorString = String$(129, 0)
- Bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, LastError, 0, ErrorString$, 128, 0)
-
- If Bytes > 2 Then
- GetErrorString = Left$(ErrorString, Bytes - 2)
- End If
-
- End Function
-
-
-
-
-
- Private Sub cmdClear_Click()
-
- txtR1.Text = ""
- txtR2.Text = ""
- txtYear.Text = ""
- txtMonth.Text = ""
- txtDay.Text = ""
- txtHour.Text = ""
- txtMinute.Text = ""
- txtSecond.Text = ""
- End Sub
-
-
-
-
-
- Private Sub cmdFindHID_Click()
- Call FindTheHid
- End Sub
-
-
-
-
-
- Private Sub DisplayResultOfAPICall(FunctionName As String)
-
- Dim ErrorString As String
- lstResults.AddItem ""
- ErrorString = GetErrorString(Err.LastDllError)
- lstResults.AddItem FunctionName
- lstResults.AddItem " Result = " & ErrorString
- End Sub
-
-
-
-
-
- Private Sub Form_Load()
- frmMain.Show
- tmrDelay.Enabled = False
- lstResults.Clear
- MyVendorID = &H45E
- MyProductID = &H930A
- End Sub
-
-
-
-
-
- Private Sub cmdGetCaps_click()
-
- Dim ppData(29) As Byte
- Dim ppDataString As Variant
-
-
-
- Result = HidD_GetPreparsedData(HidDevice, PreparsedData)
- Call DisplayResultOfAPICall("HidD_GetPreparsedData")
-
- Result = RtlMoveMemory(ppData(0), PreparsedData, 30)
- Call DisplayResultOfAPICall("RtlMoveMemory")
-
- ppDataString = ppData()
- ppDataString = StrConv(ppDataString, vbUnicode)
-
-
-
- Result = HidP_GetCaps(PreparsedData, Capabilities)
-
- Call DisplayResultOfAPICall("HidP_GetCaps")
- lstResults.AddItem " Last error: " & ErrorString
- lstResults.AddItem " Usage: " & Hex$(Capabilities.Usage)
- lstResults.AddItem " Usage Page: " & Hex$(Capabilities.UsagePage)
- lstResults.AddItem " Input Report Byte Length: " & Capabilities.InputReportByteLength
- lstResults.AddItem " Output Report Byte Length: " & Capabilities.OutputReportByteLength
- lstResults.AddItem " Feature Report Byte Length: " & Capabilities.FeatureReportByteLength
- lstResults.AddItem " Number of Link Collection Nodes: " & Capabilities.NumberLinkCollectionNodes
- lstResults.AddItem " Number of Input Button Caps: " & Capabilities.NumberInputButtonCaps
- lstResults.AddItem " Number of Input Value Caps: " & Capabilities.NumberInputValueCaps
- lstResults.AddItem " Number of Input Data Indices: " & Capabilities.NumberInputDataIndices
- lstResults.AddItem " Number of Output Button Caps: " & Capabilities.NumberOutputButtonCaps
- lstResults.AddItem " Number of Output Value Caps: " & Capabilities.NumberOutputValueCaps
- lstResults.AddItem " Number of Output Data Indices: " & Capabilities.NumberOutputDataIndices
- lstResults.AddItem " Number of Feature Button Caps: " & Capabilities.NumberFeatureButtonCaps
- lstResults.AddItem " Number of Feature Value Caps: " & Capabilities.NumberFeatureValueCaps
- lstResults.AddItem " Number of Feature Data Indices: " & Capabilities.NumberFeatureDataIndices
-
-
-
- Dim ValueCaps(1023) As Byte
-
- Result = HidP_GetValueCaps(HidP_Input, ValueCaps(0), Capabilities.NumberInputValueCaps, PreparsedData)
-
- Call DisplayResultOfAPICall("HidP_GetValueCaps")
- lstResults.AddItem " -------------------------------------------------------------------------------------------"
- lblCaps.Caption = "Get Caps Ok"
-
- cmdTrans.Enabled = True
- cmdReceive.Enabled = True
- End Sub
-
-
-
-
-
- Private Sub cmdTrans_Click()
-
-
- Dim Count As Integer
- Dim NumberOfBytesToSend As Long
- Dim NumberOfBytesWritten As Long
- Dim SendBuffer() As Byte
- ReDim SendBuffer(Capabilities.OutputReportByteLength - 1)
-
-
- Count = 0
- SendBuffer(Count) = 0
- Count = Count + 1
- SendBuffer(Count) = &H55
- Count = Count + 1
- SendBuffer(Count) = &HAA
- Count = Count + 1
- SendBuffer(Count) = &H1
- Count = Count + 1
- SendBuffer(Count) = &H8
- Count = Count + 1
- SendBuffer(Count) = Val("&H" + txtR1.Text)
- Count = Count + 1
- SendBuffer(Count) = Val("&H" + txtR2.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtYear.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtMonth.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtDay.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtHour.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtMinute.Text)
- Count = Count + 1
- SendBuffer(Count) = Val(txtSecond.Text)
- Count = Count + 1
-
-
-
- NumberOfBytesWritten = 0
- Result = WriteFile(HidDevice, SendBuffer(0), CLng(Capabilities.OutputReportByteLength), NumberOfBytesWritten, 0)
-
- Call DisplayResultOfAPICall("WriteFile")
- lstResults.AddItem " Output Report" + Str(NumberOfBytesWritten) + " bytes"
- End Sub
-
-
-
-
-
-
- Private Sub cmdReceive_click()
-
- Dim Count
- Dim NumberOfBytesRead As Long
- Dim ReadBuffer() As Byte
- ReDim ReadBuffer(Capabilities.InputReportByteLength - 1)
-
-
- Result = ReadFile(HidDevice, ReadBuffer(0), CLng(Capabilities.InputReportByteLength), NumberOfBytesRead, 0)
-
- Call DisplayResultOfAPICall("ReadFile")
- lstResults.AddItem " Input Report" + Str(NumberOfBytesRead) + " bytes"
-
-
- txtR1.Text = Hex$(ReadBuffer(5))
- txtR2.Text = Hex$(ReadBuffer(6))
- txtYear.Text = IIf(ReadBuffer(7) < 10, "0" + Trim(Str$(ReadBuffer(7))), Trim(Str$(ReadBuffer(7))))
- txtMonth.Text = IIf(ReadBuffer(8) < 10, "0" + Trim(Str$(ReadBuffer(8))), Trim(Str$(ReadBuffer(8))))
- txtDay.Text = IIf(ReadBuffer(9) < 10, "0" + Trim(Str$(ReadBuffer(9))), Trim(Str$(ReadBuffer(9))))
- txtHour.Text = IIf(ReadBuffer(10) < 10, "0" + Trim(Str$(ReadBuffer(10))), Trim(Str$(ReadBuffer(10))))
- txtMinute.Text = IIf(ReadBuffer(11) < 10, "0" + Trim(Str$(ReadBuffer(11))), Trim(Str$(ReadBuffer(11))))
- txtSecond.Text = IIf(ReadBuffer(12) < 10, "0" + Trim(Str$(ReadBuffer(12))), Trim(Str$(ReadBuffer(12))))
- End Sub
-
-
-
-
-
- Private Sub cmdClose_Click()
-
-
-
-
- Result = CloseHandle(HidDevice)
- Call DisplayResultOfAPICall("CloseHandle (HidDevice)")
-
-
-
- Result = SetupDiDestroyDeviceInfoList(DeviceInfoSet)
- Call DisplayResultOfAPICall("DestroyDeviceInfoList")
- Result = HidD_FreePreparsedData(PreparsedData)
- Call DisplayResultOfAPICall("HidD_FreePreparsedData")
-
- lstResults.Clear
- cmdClose.Enabled = False
- cmdGetCaps.Enabled = False
- cmdTrans.Enabled = False
- cmdReceive.Enabled = False
- lblHID.Caption = ""
- lblCaps.Caption = ""
- txtVendorID.Enabled = True
- txtProductID.Enabled = True
- End Sub
-
-
-
-
-
- Private Sub cmdNow_Click()
-
- txtHour.Text = IIf(Hour(Now()) < 10, "0" + Hour(Now()), Hour(Now()))
- txtMinute.Text = IIf(Minute(Now()) < 10, "0" + Trim(Str(Minute(Now()))), Minute(Now()))
- txtSecond.Text = IIf(Second(Now()) < 10, "0" + Trim(Str(Second(Now()))), Second(Now()))
- txtYear.Text = IIf((Year(Now()) - 2000) < 10, "0" + Trim(Str(Year(Now()) - 2000)), Str(Year(Now()) - 2000))
- txtMonth.Text = IIf(Month(Now()) < 10, "0" + Trim(Str(Month(Now()))), Str(Month(Now())))
- txtDay.Text = IIf(Day(Now()) < 10, "0" + Trim(Str(Day(Now()))), Day(Now()))
- End Sub
-
-
-
-
-
- Private Function GetDataString(Address As Long, Bytes As Long) As String
-
- Dim Offset As Integer
- Dim Result$
- Dim ThisByte As Byte
-
- For Offset = 0 To Bytes - 1
- Call RtlMoveMemory(ByVal VarPtr(ThisByte), ByVal Address + Offset, 1)
- If (ThisByte And &HF0) = 0 Then
- Result$ = Result$ & "0"
- End If
- Result$ = Result$ & Hex$(ThisByte) & " "
- Next Offset
-
- GetDataString = Result$
- End Function
|