Please see post #6. I found an easier way of doing this without needing to use Decimals.
Hi All,
I was inspired by a post by hwoarang over in the codebank to write a class (and a supporting bas) that I might actually use.
It's a class that provides the VBA LongLong type to VB6. Now, before everyone jumps on it, it's not "native" 8-byte integers, but it gets it all done as if it were. I'm sure it's slower than native 8-byte integers would be.
However, the only initial difference I can see from what I'm doing and what VBA does, is that I require the use of the "New" keyword when declaring variables. Here's some test code in a form:
Code:
Option Explicit
Private Sub Form_Load()
Dim LL1 As New LongLong
Dim LL2 As New LongLong
Dim LL3 As New LongLong
'
' Some simple tests.
'
LL1 = 5
LL2 = LL1
MsgBox LL1
MsgBox LL2
MsgBox LL1 + LL2
MsgBox LL1 * LL2
MsgBox LL1 / LL2
MsgBox LL1 ^ LL2
LL3 = LL1 ^ LL2
MsgBox LL3
'
' Some more rigorous tests.
'
LL1 = CLngLng("9223372036854775807") ' Largest LongLong value in VBA.
MsgBox LL1
'LL1 = LL1 + 1 ' This will be an overflow.
LL1 = CLngLng("-9223372036854775807") ' (Almost) 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.
End Sub
As you can see, the usage is VERY close to the way you'd use VBA LongLongs.
I've attached a sample project which includes the Class that gets it done, along with a supporting BAS module. In the BAS module, there's a CLngLng function that'll convert about anything to a Decimal (with overflow checks at LongLong ranges). This can be directly placed into a LongLong declared variable (as those are actually handed back as Decimals also so that math natively works).
I'm open to whatever critiques/bugs people would like to make/report about any of this. Maybe after I've polished it some more, I'll post it in the codebank.
Regards,
Elroy
Last edited by Elroy; Dec 2nd, 2016 at 02:39 PM.
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.
...
It's a class that provides the VBA LongLong type to VB6. Now, before everyone jumps on it, it's not "native" 16-byte integers, but it gets it all done as if it were. I'm sure it's slower than native 16-byte integers would be.
...
LL1 = CLngLng("9223372036854775807") ' Largest LongLong value in VBA.
...
"9223372036854775807" is 2^63-1, which would be a signed 8-byte value, not 16 bytes.
Ahhh, you're right. Sorry about that. Yeah, a LongLong is 8-bytes. I got carried away. I'll fix the comments.
EDIT1: The code is all fine. It was just my mistake in the verbiage of the OP.
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.
why not just use variant? you can assign large numbers with it.
the only different is the conversion of 9.22337203685477E+18, while your code will "round it" variant will just copy it as it is.
@ baka: Yeah, the Decimal type of a Variant does work quite well. There's a bit of a problem of getting initial values into them, but I've also got that worked out. I was just trying to see how close I could get to emulating the exact behavior of a VBA LongLong. The more I think about it, I'm not sure if I'd ever really use it or not. In VB6, Longs give me high enough integers that I doubt I'd ever overflow them. And, even in VBA, I don't think the LongLong types are used for much other than hanging onto memory pointers. It's probably just an academic exercise.
I've also already found a place where my methods don't exactly emulate the VBA LongLong, the use of the binary operators (And, Or, etc). They work with Decimals, but Decimals aren't stored as Twos-Compliment numbers, whereas all other integers are stored as Twos-Compliment. Therefore, anytime we're dealing with negative numbers, the bit patterns will be different.
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.
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
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.
Elroy, been messing around with this a bit. One of the limitations you mentioned is ULARGE_INTEGER compatibility and adding/subtracting when the high bit becomes in play.
Regarding the high bit, pertaining to longs, we know we can subtract the max unsigned value and XOR the high bit as needed. This is the method used in standard pointer arithmetic. The same logic could be used for the signed LongLong (aka LARGE_INTEGER). However, unlike standard Longs, where we can use the Double value 4294967296# (equivalent of UINT(max)+1) for subtraction, there really isn't a data type larger than LARGE_INTEGER or is there? We could use CDec for the subtraction: &H10000000000000000 is equivalent to ULARGE_INTEGER(max) + 1. But VB won't accept that hex string. It will accept its numerical equivalent in a CDec call: MyVariant = CDec("18446744073709551616")
Regarding ULARGE_INTEGER. That same CDec call can be used to represent a LARGE_INTEGER, with its high bit set, as a positive number.
Code:
' convert negative LARGE_INTEGER to ULARGE_INTEGER-like value
Debug.Print CLngLng(-1) + CDec("18446744073709551616")
When representing &H80808080 (long) as positive, a Long can't be used so we use a Double for example. Similar here, we can't use LARGE_INTEGER because of the sign bit, so we use CDec (VarType = vbDecimal).
Last note: CDec can use Or, Xor, And, etc with your LongLong, but still has the 64-bit limitation it seems.
Edited: Sample to help explain a bit
Code:
' for longs, we'd express a negative value as positive via a Double
MsgBox &H80808080 + 4294967296# ' UINT(max)+1
Code:
' for LongLong we'd express a negative value as positive via CDec variant
MsgBox CLngLng("&h8080808000000000") + CDec("18446744073709551616") ' ULARGE_INTEGER(max)+1
Likewise, some pointer-like math (wrapping around the high bit)
For typical longs, something like this:
Code:
Private Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double) As Long
Dim xAdj As Long, xShift As Long
Const SIGN_BIT = &H80000000
Const MAXINT_SIGNED As Double = 2147483647#
Const MAXINT_UNSIGNED As Double = 4294967296#
If AmountToAdjust > MAXINT_SIGNED Or Abs(AmountToAdjust) > MAXINT_SIGNED Then
xAdj = MAXINT_UNSIGNED - Abs(AmountToAdjust)
xShift = SIGN_BIT
Else
xAdj = Abs(AmountToAdjust)
End If
If AmountToAdjust > 0# Then
pvSafePointerAdd = ((thePointer Xor SIGN_BIT) + (xAdj Xor xShift)) Xor (SIGN_BIT Xor xShift)
ElseIf AmountToAdjust < 0# Then
pvSafePointerAdd = ((thePointer Xor SIGN_BIT) - (xAdj Xor xShift)) Xor (SIGN_BIT Xor xShift)
Else
pvSafePointerAdd = thePointer
End If
End Function
For LongLongs, this:
Code:
Private Function pvSafePointerAdd_LongLong(thePointer As Variant, AmountToAdjust As Variant) As Variant
' returns signed LongLong
Dim xShift As Variant, xAdj As Variant
Dim vDec As Variant, vSign As Variant
Const SIGN_BIT As String = "&H8000000000000000"
Const MAX_SIGNED As String = "&H7FFFFFFFFFFFFFFF"
Const MAX_UNSIGNED As String = "18446744073709551616"
xAdj = CLngLng(MAX_SIGNED)
vSign = CLngLng(SIGN_BIT)
' note: if adding this to your class; those 2 variables could be cached in Declarations section
' so that extra CPU cycles not needed for each call into this routine
If AmountToAdjust > xAdj Or Abs(AmountToAdjust) > xAdj Then
xAdj = CLngLng(Abs(AmountToAdjust) - CDec(MAX_UNSIGNED))
xShift = vSign
Else
xAdj = CLngLng(Abs(AmountToAdjust))
xShift = 0
End If
If AmountToAdjust > 0 Then
pvSafePointerAdd_LongLong = CLngLng(((thePointer Xor vSign) + (xAdj Xor xShift)) Xor (vSign Xor xShift))
ElseIf AmountToAdjust < 0 Then
pvSafePointerAdd_LongLong = CLngLng(((thePointer Xor vSign) - (xAdj Xor xShift)) Xor (vSign Xor xShift))
Else
pvSafePointerAdd_LongLong = CLngLng(thePointer)
End If
End Function
Last edited by LaVolpe; Dec 31st, 2017 at 04:38 PM.
Insomnia is just a byproduct of, "It can't be done"
@LaVolpe: Nice work. I'm not sure I've ever considered one of these LongLong variables being used as a pointer, especially in the case where VB6 will be manipulating them. But they certainly may be used that way. In 64-bit-Office-VBA (or, any 64-bit program for that matter), it seems that that's precisely why they were derived.
Hmm, also, I think it'll be a while before we encounter computers with enough memory where that sign bit will actually be problematic. Let's see. If &h7FFFFFFFFFFFFFFF is the largest (signed) LongLong positive number, that would be a computer with more than 7 exabytes; or, in language we're more used to hearing, a computer with more than 7,340,032 terabytes of memory! But hey, we'll be ready when it happens.
Happy New Year,
Elroy
EDIT1: Although VB6 will need to jump through some pretty high hurdles to actually address that memory (even below the 7 exabyte range but, say, above the 4 gig range).
Last edited by Elroy; Dec 31st, 2017 at 06:37 PM.
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.
Yeah, point taken. But they (both LARGE_INTEGER & ULARGE_INTEGER) are also used in IStream. Most implementations use the Currency trick, but that has limitations with add/subtracting. This becomes a problem when you start getting to values in excess of 372,036,854,775,807. Here's an interesting article regarding the usage of Currency for LARGE_INTEGER.
For my own education, I was simulating extremely large streams and bumped up against that Currency limitation.
Insomnia is just a byproduct of, "It can't be done"
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.
This becomes a problem when you start getting to values in excess of 372,036,854,775,807. ...
For my own education, I was simulating extremely large streams and bumped up against that Currency limitation.
Could you describe, how exactly you "bumped up against" that limit,
because I can't reproduce that here.
If I test the following in the VB-IDE-Immediate-Window (with regards to the "Display-Limit" the article mentions):
Code:
C=CCur("922337203685477"): ? CStr(C), CStr(-C)
Then the above works just fine (with nearly 3 times the value of 372,036,854,775,807).
Also the "hints" with regards to multiplication or division are somehow "weird" - because
once I have a valid Currency-Value, then I can multiply it with an integer directly just fine.
(not sure, where the factor 10000 should come into it).
example (again going far over the mentioned "Display-Range" of 372,036,854,775,807)
@LaVolpe: With respect to the problems I found with pvSafePointerAdd, I started looking into "fixing" it, but it started getting complicated. So I just re-wrote it. I believe the following resolves any/all problems other than "natural" overflows.
Code:
Public Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double) As Long
' The AmountToAdjust should always be an integer.
' Subtraction (a negative AmountToAdjust) works just fine.
' This is basically doing addition-or-subtraction on thePointer as if it's UNSIGNED.
Dim ptr As Variant
Dim sign As Variant
'
sign = CDec(2147483648#) ' The sign-bit of a Long, treated as a number (&h80000000).
'
ptr = CDec(thePointer And &H7FFFFFFF)
If thePointer < 0 Then ptr = ptr + sign ' Treat sign bit as more of the number, all positive.
'
ptr = ptr + AmountToAdjust ' Perform the adjustment; results is Decimal.
'
' The following may have "natural" overflows when outside of the range &h0 to &hFFFFFFFF.
If ptr >= sign Then
pvSafePointerAdd = CLng(ptr - sign) Or &H80000000
Else
pvSafePointerAdd = ptr
End If
End Function
I haven't started thinking about it just yet, but the same ideas may also work for the LongLong, but we'll still have problems if we want both thePointer and AmountToAdjust to have the possible range of a LongLong. I'll have to think about that one a bit.
Enjoy,
Elroy
EDIT1: Here, I improved it a bit more. This gives you a "wrap" option:
Code:
Public Function pvSafePointerAdd(thePointer As Long, AmountToAdjust As Double, Optional bWrap As Boolean) As Long
' The AmountToAdjust should always be an integer.
' Subtraction (a negative AmountToAdjust) works just fine.
' This is basically doing addition-or-subtraction on thePointer as if it's UNSIGNED.
Dim ptr As Variant
Dim sign As Variant
Dim signx2 As Variant
'
sign = CDec(2147483648#) ' The sign-bit of a Long, treated as a number (&h80000000).
signx2 = sign * 2 ' This is actually &h100000000, the wrap limit.
'
ptr = CDec(thePointer And &H7FFFFFFF) ' Strip any sign bit and convert to Decimal.
If thePointer < 0 Then ptr = ptr + sign ' Treat sign bit as more of the number, all positive.
'
ptr = ptr + AmountToAdjust ' Perform the adjustment; results is Decimal.
'
If bWrap Then
Do While ptr < 0
ptr = ptr + signx2
Loop
Do While ptr >= signx2
ptr = ptr - signx2
Loop
Else
If ptr < 0 Then Error 6
If ptr >= signx2 Then Error 6 ' This catches the one situation where CLng(ptr - sign) may leave only the sign bit turned on.
End If
'
If ptr >= sign Then
pvSafePointerAdd = CLng(ptr - sign) Or &H80000000
Else
pvSafePointerAdd = ptr
End If
End Function
Last edited by Elroy; Jan 1st, 2018 at 04:32 PM.
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.
Elroy, yep looks like I screwed the pooch a bit on fiddling with some pointer math. When I changed the function parameter from long to double, messed it up royally. Was attempting to allow friendly parameters, i.e., pass 2147483648 double instead of -2147483648 long. I'll tear that down again, but at a later time. Don't really need it for what I'm playing with now. Thanx.
@Olaf. The 10000 usage is due to the way Currency is stored. If passing a Currency variable to an API that expects an 8-byte value, one must divide the 'real' value by 10000, i.e., passing the currency value of 1@ is actually the value 10000L at memory address.
Code:
Dim L(1) As Long
CopyMemory L(0), 1@, 8
' L(0)=10000: L(1)=0
Insomnia is just a byproduct of, "It can't be done"