在VB6中将Double拆分为Long loword和long hiword

Sil*_*vio 4 vb6

我需要使用kernel32的SetFilePointer函数来读取磁盘的扇区,该扇区的地址包含在用于大小问题的double中。我知道ReadFile函数接受loword和hiword一样长的参数,但是我无法将我的双精度地址拆分成两个单词。

我尝试了使用Mod和Fix的几种方法,但最后我只有溢出错误。

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

要么

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

哪里

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
Run Code Online (Sandbox Code Playgroud)

我试图将双精度型转换为byteArray或十六进制字符串,以尝试“手动”字节移位,但是没有运气。

我已经看到了将Double Double转换为8个字节的数组,但是未经修改的示例始终将dNum = 1转换为[0,0,0,0,0,0,240,63],结果似乎不正确一。

您是否有一些技巧或其他方法从VB6的磁盘中读取地址较大的扇区?

谢谢大家阅读我的问题。

为了更好地说明我在做什么:我知道vb6并不是最好的选择,但是现在我已经开始了……我从INI文件(是变量)中以十六进制格式(如考虑到每个扇区512字节,我将其转换为Long(但是应该以双精度还是其他形式携带)。从该扇区开始,我必须从磁盘读取的字节数是一个常数。

当我使用功能

Call SetFilePointer(hDevice, iStartSec * BytesPerSector, 0, FILE_BEGIN)
Run Code Online (Sandbox Code Playgroud)

我必须指定字节数,然后必须乘以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
Run Code Online (Sandbox Code Playgroud)

在功能上...

D.Value = CDbl(iStartSec) * CDbl(BytesPerSector)
LSet L = D
Call SetFilePointer(hDevice, L.LowVal, L.HighVal, FILE_BEGIN)
Run Code Online (Sandbox Code Playgroud)

但这对我不起作用。

tca*_*vin 5

根据您所做的编辑,我做了一些谷歌搜索,看看是否有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
Run Code Online (Sandbox Code Playgroud)

祝你好运!


GSe*_*erg 5

正如@tcarvin所指出的Currency数据类型也是8个字节,它的内部结构与LongLong相同,但是带有一个隐含的十进制逗号。它也经过签名,这很好,因为SetFilePointer也接受Long地址部分的s。

如果可以保证从文件中读取的扇区号永远不会大于922,337,203,685,477(十六进制346DC5D638865),则可以Currency直接使用并简单地调整内置的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@
Run Code Online (Sandbox Code Playgroud)

但是,如果您的值可以大于346DC5D638865,则将需要自定义解析器,因为CCur它将溢出:

Public Type TKK_Cur
    Value As Currency
End Type

Public Type Cur2Long
    LowVal As Long
    HighVal As Long
End Type

' Returns already scaled value, no need to divide by 10000
Public Function ParseLongHex(ByVal s As String) As Currency
    Dim c As TKK_Cur
    Dim l As Cur2Long

    l.LowVal = CLng("&h" & Right$(s, 8))
    If Len(s) > 8 Then l.HighVal = CLng("&h" & Left$(s, Len(s) - 8))

    LSet c = l
    ParseLongHex = c.Value
End Function
Run Code Online (Sandbox Code Playgroud)
dim sector_number_as_string as string
sector_number_as_string = "B3A73CF8186"  ' Read from file; decimal 12345678987654

dim iStartSec as currency
iStartSec = ParseLongHex(sector_number_as_string)

'At this point iStartSec = 1234567898.7654

dim offset as currency
offset = iStartSec * 512@
Run Code Online (Sandbox Code Playgroud)

如果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
Run Code Online (Sandbox Code Playgroud)
C.value = offset
lset l = c
Run Code Online (Sandbox Code Playgroud)