Сплит двойной на длинный низший и длинный хайворд в VB6 - PullRequest
1 голос
/ 29 сентября 2019

Мне нужно использовать функцию SetFilePointer в kernel32, чтобы прочитать сектор диска, чей адрес содержится в двойном символе для определения размера.Я знаю, что функция ReadFile принимает loword как long и hiword как long параметры, но я не мог разделить свой двойной адрес на два слова.

Я пробовал несколько методов, используя Mod и Fix, но в конце концов ябыли только ошибки переполнения.

LoWord = CLng(dNum Mod CDbl(4294967295)) 'Dont care the number I use, I always get overflow error

или

LoWord = CLng(FMod(dNum, 4294967295#))
HiWord = CLng(dNum - (FMod(dNum, 4294967295#)))   'tryed different number to see the behaviour, don't care

где

Public Function FMod(a As Double, b As Double) As Double
   FMod = a - Fix(a / b) * b

   'http://en.wikipedia.org/wiki/Machine_epsilon
   'Unfortunately, this function can only be accurate when `a / b` is outside [-2.22E-16,+2.22E-16]
   'Without this correction, FMod(.66, .06) = 5.55111512312578E-17 when it should be 0
   If FMod >= -2 ^ -52 And FMod <= 2 ^ -52 Then '+/- 2.22E-16
       FMod = 0
   End If
End Function

Я пытался преобразовать double в byteArray или шестнадцатеричную строку, чтобы попробовать "руководство""сдвиг байтов, но безуспешно.

Я уже вижу Преобразование двойного в 8-байтовый массив , но образец без модификации всегда преобразует dNum = 1 в [0, 0, 0, 0, 0, 0, 240, 63] как результат, и он не кажется правильным.

У вас есть какой-нибудь совет или какой-то другой способ чтения секторов с большим адресом с диска в VB6?

Спасибо всем за чтение моего вопроса.

Чтобы лучше уточнить, что я делаю: я знаю, что, возможно, vb6 не лучший выбор, но теперь я начал с этого... Я прочитал номер сектора из INI-файла (является переменным) в шестнадцатеричном формате (как STкольцо), которое я конвертирую в Long (но должен ли он переноситься в два раза или как?), учитывая 512 байт на сектор.Количество байтов, которые я должен прочитать с диска, начиная с этого сектора, является постоянным.

Когда я использую функцию

Call SetFilePointer(hDevice, iStartSec * BytesPerSector, 0, FILE_BEGIN)

, мне нужно указать количество байтови затем я должен умножить на 512. Это вызывает у меня переполнение, которое я пытаюсь обойти.

Я пробовал также этот метод:

Private Type TKK_Dbl
    Value As Double
End Type

Private Type Dbl2Long
    LowVal As Long
    HighVal As Long
End Type

Private D As TKK_Dbl
Private L As Dbl2Long

в функции ...

D.Value = CDbl(iStartSec) * CDbl(BytesPerSector)
LSet L = D
Call SetFilePointer(hDevice, L.LowVal, L.HighVal, FILE_BEGIN)

Но у меня это не сработало.

Ответы [ 2 ]

2 голосов
/ 30 сентября 2019

Основываясь на ваших изменениях, я немного погуглил, чтобы посмотреть, есть ли Win API для сдвига влево, поскольку умножение на 512 - это просто сдвиг влево на 9.

Я столкнулсяRtlLargeIntegerShiftLeft, которая будет делать именно это (найдено здесь https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/rtlenlargedintegermultiply).

Далее, просто чтобы сэкономить вам время, я нашел образец VB6 объявления, в котором он используется (здесь: http://www.xbeat.net/vbspeed/c_ShiftLeft.htm)

Private Type LARGEINT
  Long1 As Long
  Long2 As Long
End Type

Private Declare Function RLIShiftLeft Lib "ntdll" Alias "RtlLargeIntegerShiftLeft" _
    (ByVal Val1 As Long, ByVal Val2 As Long, ByVal ShiftCount As Long) As LARGEINT

Удачи!

1 голос
/ 30 сентября 2019

Как отметил @tcarvin , тип данных Currency также составляет 8 байтов и имеет такую ​​же внутреннюю структуру, как LongLong, но с подразумеваемой десятичной запятой.Он также подписан, что хорошо, потому что SetFilePointer также принимает подписанные Long s для частей адреса.

Так что все, что вам нужно сделать, это использовать CCur и настроить для встроенного масштабирования 10000чтобы получить то же двоичное представление, которое имели бы два long:

dim sector_number_as_string as string
sector_number_as_string = "B3A73CF8186"  ' Read from file; decimal 12345678987654

dim iStartSec as currency
iStartSec = CCur("&h" & sector_number_as_string) / 10000@

'At this point iStartSec = 1234567898.7654

dim offset as currency
offset = iStartSec * 512@

Если бы SetFilePointer принял два указателя на два значения, вы могли бы уже вызвать его с этим, но так как он принимает lodword по значению и hidword поссылка, вы должны сделать LSet, как вы уже сделали, но с Currency:

Private Type TKK_Cur
    Value As Currency
End Type

Private Type Cur2Long
    LowVal As Long
    HighVal As Long
End Type

Private C As TKK_Cur
Private L As Cur2Long
C.value = offset
lset l = c
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...