Results 1 to 14 of 14

Thread: LongLong

Threaded View

  1. #6

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: LongLong

    Okay, I've discovered that there's a much easier way to do this. To my surprise, the LongLong type is actually available to VB6. This may be dependent on the version of oleaut32.dll you have, but I'm on Win7-64, and it's working fine.

    If anyone would want to test on an old true 32-bit machine, I'd be curious to see if this would all work.

    Here's my latest LongLong class (posted in full, so use Notepad to save the module).

    Code:
    VERSION 1.0 CLASS
    BEGIN
      MultiUse = -1  'True
      Persistable = 0  'NotPersistable
      DataBindingBehavior = 0  'vbNone
      DataSourceBehavior  = 0  'vbNone
      MTSTransactionMode  = 0  'NotAnMTSObject
    END
    Attribute VB_Name = "LongLong"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = True
    Attribute VB_PredeclaredId = False
    Attribute VB_Exposed = False
    '
    ' A class that allows access to the LongLong type (signed).
    '
    ' +, -, *       These all work as expected.
    ' /             Converts to a Double, with possible loss of precision.
    ' \, Mod        Works as expected.
    ' ^             Converts to a Double, with possible loss of precision.
    ' Int()         Works as expected.
    ' Fix()         Works as expected.
    ' Abs()         Works as expected.
    ' And, Or, etc  Works as expected.
    ' Sgn()         Works as expected, although return is an Integer.
    ' Sqr(), etc.   Most of these functions (including trig) return Double, not Decimal, so some precision may be lost.
    '
    Option Explicit
    '
    ' https://msdn.microsoft.com/en-us/library/cc237865.aspx for all the types.
    Private Declare Function VariantChangeTypeEx Lib "oleaut32" (ByRef pvargDest As Variant, ByRef pvarSrc As Variant, ByVal lcid As Long, ByVal wFlags As Integer, ByVal vt As Integer) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, ByVal numbytes As Long)
    '
    Private Const VT_I8 As Integer = &H14
    Private Const VT_ERROR As Integer = &HA
    Private Const LOCALE_INVARIANT As Long = &H7F&
    '
    Private Const LLOverflow = "Overflow - In LongLong procedures"
    Private Const LLTypeMismatch = "Type mismatch - In LongLong procedures"
    Private Const LLApiTypeError = "VariantChangeTypeEx error - In LongLong procedures"
    Private Const LLStrConst = "LongLong"
    '
    Dim mllValue As Variant  ' THIS is the "master" value.
    '
    
    Friend Function Ptr() As Long
        Ptr = VarPtr(mllValue) + 8 ' This points directly at the LongLong, and not the variant.
    End Function
    
    Public Property Get Value() As Variant
    Attribute Value.VB_UserMemId = 0
    Attribute Value.VB_MemberFlags = "200"
        Value = mllValue
    End Property
    
    Public Property Let Value(v As Variant)
        '
        ' Warning about strings with HEX in them.
        ' In "almost" all cases, using this method will work absolutely fine for these LongLong types.
        ' However, if your HEX string is 10 characters (8 characters of HEX, eg. "&Hffffffff"),
        ' and the Long (not LongLong) sign bit is on, the number will be interpreted as negative.
        ' To prevent this, do assignment with HexVal property.
        '
        Dim vt As Integer
        ' Make sure it's a type we can deal with.
        vt = VarType(v)
        If (vt = vbArray) Or _
           (vt = vbObject) Or _
           (vt = vbUserDefinedType) Or _
           (vt = vbEmpty) Or _
           (vt = vbNull) Or _
           (vt = vbError) Or _
           (vt = vbVariant) Or _
           (vt = vbDataObject) Then
                Err.Raise 13, LLStrConst, LLTypeMismatch
                Exit Property
        End If
        '
        If VarType(mllValue) <> VT_I8 Then SetToLongLongZero ' This should happen only if there's been an error.
        mllValue = v
        If VarType(mllValue) <> VT_I8 Then
            mllValue = Format$(mllValue) ' This helps with overflow, especially when it's a large Double.
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
        End If
    End Property
    
    Friend Property Get HexVal() As String
        Dim lLo As Long
        Dim lHi As Long
        '
        If VarType(mllValue) <> VT_I8 Then Exit Sub ' This should happen only if there's been an error.
        '
        CopyMemory lHi, ByVal VarPtr(mllValue) + 12, 4
        CopyMemory lLo, ByVal VarPtr(mllValue) + 8, 4
        '
        If lHi = 0 Then
            HexVal = Hex$(lLo)
        Else
            HexVal = Hex$(lHi) & Right$("0000000" & Hex$(lLo), 8)
        End If
    End Property
    
    Friend Property Let HexVal(s As String)
        If UCase$(Left$(s, 2)) <> "&H" Then
            Err.Raise 13, LLStrConst, LLTypeMismatch
            Exit Property
        End If
        If Len(s) <> 10 Then
            mllValue = s
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
        Else    ' We'll just go around the Long sign bit.
            mllValue = s & "0"
            CheckForErrors VariantChangeTypeEx(mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8)
            mllValue = mllValue \ &H10
        End If
    End Property
    
    Friend Property Get CurrNoDec() As Currency
        ' This one lets you return a Currency while ignoring the implicit 4th decimal place.
        If VarType(mllValue) <> VT_I8 Then SetToLongLongZero ' This should happen only if there's been an error.
        CopyMemory CurrNoDec, ByVal VarPtr(mllValue) + 8, 8
    End Property
    
    Friend Property Let CurrNoDec(c As Currency)
        ' This one lets you assign a Currency while ignoring the implicit 4th decimal place.
        CopyMemory mllValue, VT_I8, 2
        CopyMemory ByVal VarPtr(mllValue) + 8, c, 8
    End Property
    
    '*****************************************************
    '*****************************************************
    ' Everything is private from here down.
    '*****************************************************
    '*****************************************************
    '
    Private Sub Class_Initialize()
        SetToLongLongZero
    End Sub
    
    Private Sub SetToLongLongZero()
        mllValue = 0&
        VariantChangeTypeEx mllValue, mllValue, LOCALE_INVARIANT, 0, VT_I8
    End Sub
    
    Private Sub CheckForErrors(lRet As Long)
        Const S_OK = 0&
        Const DISP_E_BADVARTYPE = &H80020008
        Const DISP_E_OVERFLOW = &H8002000A
        Const DISP_E_TYPEMISMATCH = &H80020005
        Const E_INVALIDARG = &H80070057
        Const E_OUTOFMEMORY = &H8007000E
        '
        Select Case lRet
        Case S_OK
            ' Nothing to do here, all good.
        Case DISP_E_BADVARTYPE
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case DISP_E_OVERFLOW
            Err.Raise 6, LLStrConst, LLOverflow
        Case DISP_E_TYPEMISMATCH
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case E_INVALIDARG
            Err.Raise lRet, LLStrConst, LLApiTypeError
        Case E_OUTOFMEMORY
            Err.Raise lRet, LLStrConst, LLApiTypeError
        End Select
    End Sub
    And here's a bit of test code to throw into a Form1 (just paste this one into Form1 in IDE):

    Code:
    
    Option Explicit
    
    Private Type WIN32_FIND_DATA
       dwFileAttributes As Long
       ftCreationTime   As Currency
       ftLastAccessTime As Currency
       ftLastWriteTime  As Currency
       nFileSize        As Currency
       dwReserved       As Currency
       cFileName        As String * 260
       cAlternate       As String * 14
    End Type
    '
    Private Type SYSTEMTIME
       wYear            As Integer
       wMonth           As Integer
       wDayOfWeek       As Integer
       wDay             As Integer
       wHour            As Integer
       wMinute          As Integer
       wSecond          As Integer
       wMilliseconds    As Integer
    End Type
    '
    Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long
    Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
    Private Declare Function FileTimeToSystemTimePtr Lib "kernel32" Alias "FileTimeToSystemTime" (lpFileTimePtr As Long, lpSystemTime As SYSTEMTIME) As Long
    Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFileTime As Currency, lpSystemTime As SYSTEMTIME) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, ByVal numbytes As Long)
    '
    
    Private Sub Form_Load()
        Dim LL1 As New LongLong
        Dim LL2 As New LongLong
        Dim LL3 As New LongLong
    
    
        Const sLargeFileSpec = "C:\Users\Elroy\Desktop\test.txt"
    
    
    
        '
        ' Some simple tests.
        '
        LL1 = 5
        LL2 = LL1 And 4
        MsgBox LL2
        MsgBox LL1 + LL2
        MsgBox LL1 * LL2
        MsgBox LL1 / LL2 ' This will result in a Double.
        MsgBox LL1 \ LL2
        MsgBox LL1 ^ LL2
        LL3 = LL1 ^ LL2
        MsgBox LL3
    
        LL1.HexVal = "&Hffffffff8"
        MsgBox LL1
        MsgBox LL1.HexVal
    
    
    
    
        '
        ' Some more rigorous tests.
        '
        LL1 = "9223372036854775807" ' Largest LongLong value in VBA.
        MsgBox LL1
        'LL1 = LL1 + 1   ' This will be an overflow.
    
        LL1 = "-9223372036854775807" ' Smallest LongLong value in VBA.
        MsgBox LL1
        LL1 = LL1 - 1   ' This not overflow.
        MsgBox LL1
        'LL1 = LL1 - 1   ' This will be an overflow.
    
        Dim d As Double
        d = 9.22337203685477E+18
        LL1 = d
        MsgBox LL1
        'LL1 = LL1 * 2 ' This will overflow.
    
    
    
        '
        ' Test the use of the pointer with API calls.
        '
        Dim hSearch As Long
        Dim wfd As WIN32_FIND_DATA
    
        hSearch = FindFirstFile(sLargeFileSpec, wfd)
        FindClose hSearch
    
        LL1.CurrNoDec = SwapLow4AndHigh4(wfd.nFileSize) ' FileSize comes in with low 4 and high 4 reversed.  FileTime is correct.
        MsgBox "FileSize: " & LL1
    
        Dim st As SYSTEMTIME
        FileTimeToSystemTime wfd.ftCreationTime, st
        MsgBox "Old way, creation date: " & DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond)
    
        LL1.CurrNoDec = wfd.ftCreationTime
        FileTimeToSystemTimePtr ByVal LL1.Ptr, st                 ' <---- This actually tests the use of the ptr property of the LongLong.
        MsgBox "Creation Date: " & DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond)
    
    
    
        Unload Me
    End Sub
    
    Public Function SwapLow4AndHigh4(c As Currency) As Currency
        CopyMemory SwapLow4AndHigh4, ByVal VarPtr(c) + 4, 4
        CopyMemory ByVal VarPtr(SwapLow4AndHigh4) + 4, c, 4
    End Function
    
    
    I've also attached it as a ZIP file.

    Enjoy, and let me know what you think,
    Elroy
    Attached Files Attached Files
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width