Results 1 to 34 of 34

Thread: Making pointer of a long variable for another variable

  1. #1

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Making pointer of a long variable for another variable

    My brains are having issues, so here we go:
    • I have a Long variable that I want to contain a pointer value.
    • I want to change the value of this variable to be pointer value to anywhere in the memory.
    • The value of another Long variable should change to be the contents of the pointer.

    I just can't get my head to do it... very simple, just a change to one pointer value in memory, but how to do that?

  2. #2
    I'm about to be a PowerPoster!
    Join Date
    Jan 2005
    Location
    Everywhere
    Posts
    13,647

    Re: Making pointer of a long variable for another variable

    I am not sure what you are trying to do. It sounds like just a simple dereference. But if that's all it is I don't suppose you would be posting here.

    Pseudocode example maybe?

  3. #3
    Frenzied Member
    Join Date
    Aug 2000
    Location
    O!
    Posts
    1,177

    Re: Making pointer of a long variable for another variable

    I think I understand what you are asking. Something like the following?
    VB Code:
    1. 'Drop this code in a new project form to test
    2.  
    3. Option Explicit
    4. Private Declare Sub CopyMemory Lib "kernel32" _
    5.                 Alias "RtlMoveMemory" (xDest As Any, _
    6.                                        xSource As Any, _
    7.                                        ByVal nbytes As Long)
    8.  
    9. Private Sub Form_Load()
    10.   Dim sPtr    As Long
    11.   Dim memByte As Byte
    12.   Dim testStr As String
    13.   Dim indx    As Integer
    14.  
    15.   testStr = "test"
    16.   sPtr = StrPtr(testStr)
    17.   For indx = 0 To LenB(testStr) - 1
    18.       CopyMemory memByte, ByVal sPtr, 1
    19.       Debug.Print memByte
    20.       sPtr = sPtr + 1
    21.   Next indx
    22.  
    23. End Sub
    Once the long contains the address of the string, this code moves and displays each byte of the string one byte at a time.

    The original value stored in the pointer by the StrPtr call is incremented by 1 for each pass through the loop. CopyMemory uses the address stored in the pointer (ByVal sPtr) to reference the sending field.

    I'm not quite sure I understand what you mean by
    The value of another Long variable should change to be the contents of the pointer
    I think you meant to say "should change to be the contents at the value of the pointer". Also, the change in the other variable will not be automatic.

    Can you provide more detail as to what you are trying to do? I've written a lot of C, PL/I and Pascal and am quite used to working with pointers.

    Addendum:
    You may find something helpful here.
    Last edited by ccoder; Jul 10th, 2006 at 01:04 PM. Reason: Added link

  4. #4
    Frenzied Member
    Join Date
    Jun 2006
    Posts
    1,098

    Re: Making pointer of a long variable for another variable

    I'm barely familiar with anything C, but I think Merri is implying indirection, such that the 'other' long variable would always return the value pointed to by the pointer variable. I have no idea how this might be done in VB.

  5. #5
    I'm about to be a PowerPoster! Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Making pointer of a long variable for another variable

    As ccoder already have mentioned you would need to use the CopyMemory API for that since VB doesn't have built in features for working with pointers.

  6. #6
    Oi, fat-rag! bushmobile's Avatar
    Join Date
    Mar 2004
    Location
    on the poop deck
    Posts
    5,592

    Re: Making pointer of a long variable for another variable

    I'm a bit confused - are you reading 4 bytes from a memory address? If so, you can use the GetMem function:
    VB Code:
    1. Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Long)
    there are also 1, 2 & 8 byte versions

  7. #7
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    you might be interested in this website.

    a little example using GetMem4

    VB Code:
    1. Private Declare Sub GetMem4 Lib "msvbvm60.dll" (Ptr As Any, RetVal As Long)
    2.  
    3. Public Sub PtrTest()
    4.     Dim lngArr(1) As Long
    5.     Dim lngPtr As Long, lngVal As Long
    6.    
    7.     lngArr(0) = 5
    8.     lngArr(1) = 1000
    9.    
    10.     lngPtr = VarPtr(lngArr(0))
    11.    
    12.     GetMem4 ByVal lngPtr, lngVal
    13.     Debug.Print lngVal  ' ouput is 5
    14.    
    15.     lngPtr = lngPtr + 4
    16.    
    17.     GetMem4 ByVal lngPtr, lngVal
    18.     Debug.Print lngVal  ' output is 1000
    19.    
    20. End Sub

  8. #8
    Frenzied Member
    Join Date
    Jun 2006
    Posts
    1,098

    Re: Making pointer of a long variable for another variable

    GetMem... hm, that is most definitely indirection, and certainly worth knowing.

  9. #9
    I'm about to be a PowerPoster! Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Making pointer of a long variable for another variable

    I don't really see the advantage of calling the undocumented GetMemN/PytMemN functions instead of using CopyMemory since they internally calls CopyMemory (or RtlMoveMemory if you wish) anyway.

  10. #10
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    GetMem doesn't call CopyMemory. it doesn't call ANY other APis and should be much faster than CopyMemory. look at the link i posted above.

  11. #11
    I'm about to be a PowerPoster! Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Making pointer of a long variable for another variable

    Well, I don't see how they would be faster... The assembly code show what happens when you call them, and that is the same thing you would get with CopyMemory. The only difference is that with CopyMemory you can specify the number of bytes to copy as an argument instead of having 4 different function calls. I don't see how you can tell from that assembly code that the functions isn't simply wrappers for CopyMemory. Even though they might not call CopyMemory the result is the same as if you did. How does that make them faster?

    If you want to use them then please go ahead and do so. The first time I noticed these functions a few years back I personally voted not to use them simply because they are undocumented and does the exact same thing as CopyMemory does.

  12. #12

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    The problem is that I can't my head over to how to find the pointer to a Long variable; I can access the value alright, I know where that resides, but I want to be able to change this place by changing another Long variable. It seems it would be easiest to make a SAFEARRAY, although I'd have just one item in it.

    What I'm trying to do is to have a fast bitwise access over a Single variable, but I don't want to use CopyMemory within the actual function because that is slow. I also need to have the result as a Single, so I'd have to use two CopyMemory calls, but doing these is by order of magnitude slower than the actual simple bitwise math that I want to do. And I'm looking for performance.

    I simply want to fake a Long variable to point to the Single variable by changing the pointer value of another Long variable.

  13. #13
    I'm about to be a PowerPoster! Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Making pointer of a long variable for another variable

    Is VarPtr what you're looking for????

  14. #14

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    No, it only returns where the actual value is located at. Anyways, I ended up using a SAFEARRAY instead. Here is the code I've been playing with:
    VB Code:
    1. Option Explicit
    2.  
    3. Private Const SA_DIMENSIONS = 0
    4. Private Const SA_BYTES = 1
    5. Private Const SA_LOCKS = 2
    6. Private Const SA_PTR = 3
    7. Private Const SA_ELEMENTS = 4
    8. Private Const SA_LBOUND = 5
    9.  
    10. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef lpvDest As Any, ByRef lpvSrc As Any, ByVal cbLen As Long)
    11. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() As Any) As Long
    12.  
    13. Private SA1D(5) As Long
    14. Private lngSQR() As Long
    15. Private lngSQRSngPtr As Long
    16. Private lngSQRDblPtr As Long
    17. Private BitShiftDbl(1) As Long
    18.  
    19. Public Sub CleanSQR()
    20.     CopyMemory ByVal VarPtrArray(lngSQR), 0&, 4&
    21. End Sub
    22. Public Sub InitSQR()
    23.     SA1D(SA_DIMENSIONS) = 1
    24.     SA1D(SA_BYTES) = 4
    25.     SA1D(SA_ELEMENTS) = 2
    26.     CopyMemory ByVal VarPtrArray(lngSQR), VarPtr(SA1D(0)), 4&
    27.     BitShiftDbl(1) = &H80000000
    28. End Sub
    29. Public Function BabylonianSqr(ByVal Value As Single) As Single
    30.     Dim sngPrev As Single, sngCur As Single
    31.     sngCur = Value
    32.     Do Until sngPrev = sngCur
    33.         sngPrev = sngCur
    34.         sngCur = 0.5 * (sngPrev + (Value / sngPrev))
    35.     Loop
    36.     BabylonianSqr = sngCur
    37. End Function
    38. Public Function SngSqr(ByVal Value As Single) As Single
    39.     If lngSQRSngPtr <> 0 Then
    40.         SA1D(SA_PTR) = lngSQRSngPtr
    41.         lngSQR(0) = (lngSQR(0) \ 2) + &H1FC00000
    42.         SngSqr = Value
    43.     Else
    44.         lngSQRSngPtr = VarPtr(Value)
    45.         SA1D(SA_PTR) = lngSQRSngPtr
    46.         lngSQR(0) = (lngSQR(0) \ 2) + &H1FC00000
    47.         SngSqr = Value
    48.     End If
    49. End Function
    50. Public Function DblSqr(ByVal Value As Double) As Double
    51.     If lngSQRDblPtr <> 0 Then
    52.         SA1D(SA_PTR) = lngSQRDblPtr
    53.         If lngSQR(0) >= 0 Then
    54.             lngSQR(0) = lngSQR(0) \ 2 Or BitShiftDbl(lngSQR(1) And &H1)
    55.         Else
    56.             lngSQR(0) = lngSQR(0) \ 2 Or BitShiftDbl(lngSQR(1) And &H1) Or &H40000000
    57.         End If
    58.         lngSQR(1) = (lngSQR(1) \ 2) + &H1FF80000
    59.         DblSqr = Value
    60.     Else
    61.         lngSQRDblPtr = VarPtr(Value)
    62.         SA1D(SA_PTR) = lngSQRDblPtr
    63.         If lngSQR(0) >= 0 Then
    64.             lngSQR(0) = lngSQR(0) \ 2 Or BitShiftDbl(lngSQR(1) And &H1)
    65.         Else
    66.             lngSQR(0) = lngSQR(0) \ 2 Or BitShiftDbl(lngSQR(1) And &H1) Or &H40000000
    67.         End If
    68.         lngSQR(1) = (lngSQR(1) \ 2) + &H1FF80000
    69.         DblSqr = Value
    70.     End If
    71. End Function

    SngSqr is 1.7 times faster than the native Sqr The purpose of the function isn't to give a perfect result, instead work faster and give something that is close enough (good enough for games, for example). At maximum the result is 6% off of the correct value.


    Edit
    Code updated to handle negative numbers correctly.

    Edit #2
    Removed negative number check; slowed down the code too much. However, currently SngSqr is two times faster than Sqr
    Last edited by Merri; Jul 11th, 2006 at 02:30 PM.

  15. #15
    Frenzied Member
    Join Date
    Aug 2000
    Location
    O!
    Posts
    1,177

    Re: Making pointer of a long variable for another variable

    I thought I should add one more point to this thread.

    True pointers are typed so that the compiler knows how to handle the data that they reference. The pointers created using StrPtr, VarPtr and ObjPtr are not. They simply point to an address in memory.

    To illustrate this point I modified the code that I posted earlier. The test string is now in a UDT and has a fixed length. I chose this method because CopyMemory will convert the string to ANSI during the copy process making it easier to see what I am attempting to explain. Also, the UDT contains the actual contents of the string whereas it would contain the BSTR (a long) of a regular string and we won't see any conversion take place.

    Here is the code
    VB Code:
    1. 'Drop this code in a new project form to test
    2.  
    3. Option Explicit
    4. Private Declare Sub CopyMemory Lib "kernel32" _
    5.                 Alias "RtlMoveMemory" (xDest As Any, _
    6.                                        xSource As Any, _
    7.                                        ByVal nbytes As Long)
    8. Private Type dclTestUDT
    9.         flag    As Integer
    10.         testStr As String * 5
    11. End Type
    12. Private Sub Form_Load()
    13.   Dim testUDT   As dclTestUDT
    14.   Dim vPtr      As Long
    15.   Dim memByte() As Byte
    16.   Dim indx      As Integer
    17.   Dim arryLen   As Integer
    18.  
    19.   arryLen = Len(testUDT)
    20.   ReDim memByte(arryLen - 1)
    21.  
    22.   testUDT.flag = 101
    23.   testUDT.testStr = "test1"
    24.  
    25.   Debug.Print "Test 1 - UDT copied to byte array"
    26.   CopyMemory memByte(0), testUDT, arryLen
    27.   Debug.Print "memByte contains: ";
    28.   For indx = 0 To arryLen - 1
    29.       Debug.Print memByte(indx);
    30.   Next indx
    31.   Debug.Print
    32.  
    33.   testUDT.flag = 202
    34.   testUDT.testStr = "test2"
    35.   vPtr = VarPtr(testUDT)
    36.  
    37.   Debug.Print "Test 2 - UDT copied to byte array via pointer"
    38.   CopyMemory memByte(0), ByVal vPtr, arryLen
    39.   Debug.Print "memByte contains: ";
    40.   For indx = 0 To arryLen - 1
    41.       Debug.Print memByte(indx);
    42.   Next indx
    43.  
    44. End Sub
    The runtime output is
    Code:
    Test 1 - UDT copied to byte array
    memByte contains:  101  0  116  101  115  116  49 
    Test 2 - UDT copied to byte array via pointer
    memByte contains:  202  0  116  0  101  0  115
    As you can see, CopyMemory 'understands' the data that is being moved when the copying the UDT and converted the UTF string to an ANSI string, but when referencing the UDT with the typeless pointer, it simply copied 7 contiguous bytes of memory.

  16. #16

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    That is only a normal VB behavior with API; it is VB that understands it, not CopyMemory. VB sees that you're passing UDT that contains strings, so it makes a new copy of the whole UDT and converts the string parts into ANSI for "backwards compatibility". So you're copying a copy of the UDT with CopyMemory, not the actual UDT, and the pointer passed for CopyMemory is the pointer of the copy of the UDT. In the other hand the result of VarPtr, Long, is seen by VB as just that, a Long variable, and no conversion takes place because it avoids the "intelligency" of VB.

    None of this really helps with the actual issue: there just is no way to avoid the use of a SAFEARRAY in this case. I'd mostly like to avoid SAFEARRAY because it is a slow structure (data is found behind a pointer in header of a pointer of the variable). I just can't make two plain variables be pointed to the same location in memory.

  17. #17
    Frenzied Member
    Join Date
    Jun 2006
    Posts
    1,098

    Re: Making pointer of a long variable for another variable

    Is this all you need?
    VB Code:
    1. Private x As Long
    2.  
    3. Private Sub Command1_Click()
    4.   Call MySub(x)
    5. End Sub
    6.  
    7. Private Sub MySub(ByRef y As Long)
    8.   x = 3
    9.   y = 4
    10.   MsgBox "x = " & x
    11. End Sub

    Nevermind... I realize you need them to be of different types.

  18. #18
    I'm about to be a PowerPoster! Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Making pointer of a long variable for another variable

    Quote Originally Posted by Merri
    I'd mostly like to avoid SAFEARRAY because it is a slow structure
    All arrays in VB are implemented as SAFEARRAYs.

  19. #19

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    That's why I didn't want to use an array in the first place because a Long variable is the same size as a Single; I only need a normal variable. But the only current (known) way to go is to use a SAFEARRAY. Of course, the only reason I'm even bothering on it is the possible speed gain, because a real simple function such as SngSqr would really gain of anything that is faster and a normal variable is faster than an array variable.

    I just got the crasiest idea. Variant. It is slow, but you can fake it to be another kind of variable simply by changing a number. Hmm... nope, it'd require more initialization and coercion so there would be no speed gain.

  20. #20
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    Merri i think i've found a solution. it's not exactly what you want but i think it's pretty close and fast.

    ok, let's take the SngSqr function as an example:

    VB Code:
    1. Public Function SngSqr(ByVal Value As Single) As Single

    this is what you have to call.

    VB Code:
    1. Public Function SngSqr(ByVal Value As Long) As Single

    this is what you would like to call but you can't because VB would convert Value from single to long, right?

    i found a way to replace this sub...

    VB Code:
    1. Public Sub SngSqr(value As Single, sngResult As Single)
    2.     Dim sngVal As Single
    3.     sngVal = value
    4.  
    5.     If lngSQRSngPtr <> 0 Then
    6.         SA1D(SA_PTR) = lngSQRSngPtr
    7.         lngSQR(0) = (lngSQR(0) \ 2) + &H1FC00000
    8.         sngResult = sngVal
    9.     Else
    10.         lngSQRSngPtr = VarPtr(sngVal)
    11.         SA1D(SA_PTR) = lngSQRSngPtr
    12.         lngSQR(0) = (lngSQR(0) \ 2) + &H1FC00000
    13.         sngResult = sngVal
    14.     End If
    15. End Sub

    with this one
    VB Code:
    1. Public Sub SngSqrNEW(lngValue As Long, lngResult As Long)
    2.  
    3.     lngResult = (lngValue \ 2) + &H1FC00000
    4.    
    5. End Sub

    without any data conversion. needless to say that the second one is faster

    it works by simply replacing both subs at runtime. ok, it's a dirty hack but i think you'll like it

    i attached a small demo project to show how it works.

    but this method has a few drawbacks as well. it currently only works with Subs, not with functions and it doesn't work in the IDE. you have to compile the code. and if you do something wrong your app will crash.

    i will do some more testing and try to make it work with functions and probably in the IDE too.
    Attached Files Attached Files

  21. #21
    Frenzied Member
    Join Date
    Aug 2000
    Location
    O!
    Posts
    1,177

    Re: Making pointer of a long variable for another variable

    Quote Originally Posted by Merri
    That is only a normal VB behavior with API; it is VB that understands it, not CopyMemory. VB sees that you're passing UDT that contains strings, so it makes a new copy of the whole UDT and converts the string parts into ANSI for "backwards compatibility".
    I wasn't aware of this until I was Googling for some solutions to your problem. I saw something about VB doing the conversion on UDTs passed to .dlls but hadn't put 2 and 2 together until your post. Since the parameters are the same and in the same order, I always assumed CopyMemory was like C's memcpy function: memcpy(obuff, (char *)start_ptr, ssize);

    This explains why I sometimes see comments about CopyMemory being slow although it appears that VB has a lot to do with that.

  22. #22

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    CopyMemory and all API function calls are slowish; the perfect example for this could be SetPixel and GetPixel. What these functions do? Set a single pixel or read one. How long it takes to handle a few thousand pixels with these functions? About a second. For comparison, you can handle the entire screen well enough within a second if you have access to it as an array (and the program is compiled).

    The more an API function can do at once, the more value you get for calling it: once the API function is rolling what it does, it is fast. In the case of SetPixel and GetPixel most of the time is spent with everything else but the most important thing. A lot of "useless" calls are done. This holds true for all API functions.

    I don't use CopyMemory to move stuff within an array, even if the task is as big as moving all items left or right by one step or more. This because CopyMemory is hardly any faster than compiled VB6 code with all optimizations turned on; and with small amount of items CopyMemory is slower.

    For a practical example, if I used CopyMemory within the SngSqr function, the function would be much slower.


    Agilaz: the code is interesting. I'm 99% sure the crashes under IDE are caused by the fact you don't restore the original state at any point. This simply corrupts memory and we have a lovely crash. Although, it might be within IDE/p-code the functions are also located differently.

    For some reason I don't today have much interest into coding, guess I slept badly.

  23. #23

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    I made an easy-to-use revision of Agilaz code, I got it kind of working (it doesn't crash IDE), but SngSqr returns wrong results at the moment. I had to go to work so I couldn't read the whole code and fix it, I guess I have a minor error somewhere (pass wrong memory address or similar). However, the good thing is that I have no crash under IDE

  24. #24
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    i only get crash in the IDE when i use the ReplaceSubByCopy method. ReplaceSubByJmp doesn't crash it but it seems to have no effect at all.

    one of the problems seems to be that "AddressOf somesub" doesn't return a pointer to the real sub. instead it returns a pointer to a small sub which is calling somesub. so the real address of somesub must be somewhere in this caller sub. unfortunately i don't have time to analyze it at the moment (have to go to work).

  25. #25

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    My current code:
    VB Code:
    1. Option Explicit
    2.  
    3. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    4. Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
    5. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    6. Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
    7. Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
    8.  
    9. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef lpvDest As Any, ByRef lpvSrc As Any, ByVal cbLen As Long)
    10.  
    11. Private Type FUNCTION_COPY
    12.     OldMemory() As Byte
    13.     Address As Long
    14.     Removed As Boolean
    15. End Type
    16.  
    17. Private FunctionCopy() As FUNCTION_COPY
    18.  
    19. Public Function AddFunction(ByVal hWnd As Long, ByVal DestAddressOf As Long, ByVal SrcAddressOf As Long, ByVal SrcAfterAddressOf As Long) As Long
    20.     Dim lngNewIndex As Long, blnArrayInit As Boolean
    21.     Dim lngProcessID As Long, lngProcessHandle As Long, lngWritten As Long
    22.    
    23.     GetWindowThreadProcessId hWnd, lngProcessID
    24.     lngProcessHandle = OpenProcess(&H1F0FFF, 0&, lngProcessID)
    25.     If lngProcessHandle = 0 Then AddFunction = -1: Exit Function
    26.    
    27.     blnArrayInit = Not ((Not FunctionCopy) = -1&)
    28.     On Error Resume Next: Debug.Assert CLng(0.1): On Error GoTo 0
    29.     If blnArrayInit Then lngNewIndex = UBound(FunctionCopy) + 1
    30.     ReDim Preserve FunctionCopy(lngNewIndex)
    31.     With FunctionCopy(lngNewIndex)
    32.         .Address = DestAddressOf
    33.         ReDim .OldMemory(Abs(SrcAfterAddressOf - SrcAddressOf) - 1)
    34.         ReadProcessMemory lngProcessHandle, ByVal .Address, ByVal VarPtr(.OldMemory(0)), UBound(.OldMemory) + 1, lngWritten
    35.         WriteProcessMemory lngProcessHandle, ByVal .Address, ByVal SrcAddressOf, ByVal UBound(.OldMemory) + 1, lngWritten
    36.         CloseHandle lngProcessHandle
    37.     End With
    38.     AddFunction = lngNewIndex
    39. End Function
    40. Public Function RemoveFunction(ByVal hWnd As Long, ByVal Index As Long) As Boolean
    41.     Dim blnArrayInit As Boolean, lngA As Long
    42.     Dim lngProcessID As Long, lngProcessHandle As Long, lngWritten As Long
    43.     blnArrayInit = Not ((Not FunctionCopy) = -1&)
    44.     If Not blnArrayInit Then Exit Function
    45.     If Index < 0 Or Index > UBound(FunctionCopy) Then Exit Function
    46.     If FunctionCopy(Index).Removed Then Exit Function
    47.    
    48.     GetWindowThreadProcessId hWnd, lngProcessID
    49.     lngProcessHandle = OpenProcess(&H1F0FFF, 0&, lngProcessID)
    50.     If lngProcessHandle = 0 Then Exit Function
    51.    
    52.     With FunctionCopy(Index)
    53.         WriteProcessMemory lngProcessHandle, ByVal .Address, ByVal VarPtr(.OldMemory(0)), ByVal UBound(.OldMemory) + 1, lngWritten
    54.         CloseHandle lngProcessHandle
    55.         Erase .OldMemory
    56.         .Removed = True
    57.     End With
    58.    
    59.     For lngA = 0 To UBound(FunctionCopy)
    60.         If Not FunctionCopy(lngA).Removed Then Exit For
    61.     Next lngA
    62.     If lngA > UBound(FunctionCopy) Then Erase FunctionCopy
    63.    
    64.     RemoveFunction = True
    65. End Function

    VB Code:
    1. Option Explicit
    2.  
    3. Public Sub SngSqr(ByVal sngValue As Single, ByRef sngResult As Single)
    4.     If sngValue > 0 Then sngResult = (sngValue / 2) + &H1FC00000
    5. End Sub
    6. Public Sub SngSqrREAL(ByVal lngValue As Long, ByRef lngResult As Long)
    7.     If lngValue > 0 Then lngResult = (lngValue \ 2) + &H1FC00000
    8. End Sub
    9. Public Sub SngSqrEND()
    10.    
    11. End Sub
    VB Code:
    1. Private Sub Command1_Click()
    2.     Dim sngTest As Single, sngResult As Single
    3.    
    4.     AddFunction Me.hWnd, AddressOf SngSqr, AddressOf SngSqrREAL, AddressOf SngSqrEND
    5.    
    6.     SngSqr 0, sngResult
    7.     MsgBox sngResult
    8.    
    9.     RemoveFunction Me.hWnd, 0
    10. End Sub

    It is safe under IDE, because it restores the original state. I believe if you under IDE go replacing the data within the pointers AddressOf returns, you're making whatever there is in the IDE mode corrupt and that's when you go crash boom bang.

    So, time to analyze what there is in the AddressOf the IDE returns...

  26. #26
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    this is the code at the address returned by AddressOf SngSqr...
    Code:
    :00000000 A1400BAB00              mov eax, dword ptr [00AB0B40]
    :00000005 0BC0                    or eax, eax
    :00000007 7413                    je 0000001C
    :00000009 B8E016A90F              mov eax, 0FA916E0
    :0000000E FFD0                    call eax
    :00000010 83F802                  cmp eax, 00000002
    :00000013 7407                    je 0000001C
    :00000015 B89A2C1E00              mov eax, 001E2C9A
    :0000001A FFE0                    jmp eax
    :0000001C 33C0                    xor eax, eax
    :0000001E C20800                  ret 0008
    replacing this code doesn't seem to have any effect. the IDE still executes the original code instead of the new one. maybe this code is only ment to be called from outside when you pass a function pointer to an API for for a callback procedure but not if you call a function or sub inside the program itself.

    so we have to find the address of the real SngSqr sub.
    if we look at the assembly code then we see that the caller function calls something at address 0FA916E0 and/or jumps to address 001E2C9A. one of those addresses could be the location of the real sub. or they are just other IDE related functions that call the real sub.

    oh well, looks like a lot of disassembling/debugging work is waiting

  27. #27
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    i've got it

    the pointer to the real function is here:
    Code:
    :00000015 B89A2C1E00              mov eax, 001E2C9A
    and here's my new code.
    works in the complied version and in the IDE (stable). no need to restore the old sub and pretty easy to use. the subs replace themself the first time they are called, but you can do it explicitly too. the subs can be in any order anywhere in the module.

    hmm...this technique can probably be used to run precompiled assembly code stored in a byte array without using CallWindowProc

    so far everything seems to work stable but of course it needs more testing.

    VB Code:
    1. Option Explicit
    2.  
    3. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    4. Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
    5. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    6. Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
    7. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef lpvDest As Any, ByRef lpvSrc As Any, ByVal cbLen As Long)
    8.  
    9.  
    10. Private Sub CopyProcMemory(lngPtrDes As Long, lngPtrSrc As Long, lngLen As Long)
    11.     Dim pid As Long, phandle As Long, bwritten As Long
    12.      
    13.     pid = GetCurrentProcessId
    14.     phandle = OpenProcess(&H1F0FFF, False, pid)
    15.     If phandle = 0 Then
    16.         MsgBox "Ooops!"
    17.         Exit Sub
    18.     End If
    19.     Call WriteProcessMemory(phandle, ByVal lngPtrDes, ByVal lngPtrSrc, ByVal lngLen, bwritten)
    20.     CloseHandle phandle
    21.    
    22. End Sub
    23.  
    24.  
    25. Public Sub ReplaceSubByJmp(lngFPtrOrg As Long, lngFPtrNew As Long)
    26.     Dim bytArr(4) As Byte, lngOffset As Long
    27.     Dim IsIDE As Boolean
    28.    
    29.     ' are we running in IDE mode?
    30.     On Error Resume Next
    31.     Debug.Print 1 / 0
    32.     IsIDE = Err.Number
    33.     On Error GoTo 0
    34.    
    35.     If IsIDE Then
    36.         CopyMemory lngFPtrOrg, ByVal lngFPtrOrg + &H16, ByVal 4
    37.         CopyMemory lngFPtrNew, ByVal lngFPtrNew + &H16, ByVal 4
    38.     End If
    39.                    
    40.     lngOffset = lngFPtrNew - lngFPtrOrg - 5
    41.    
    42.     bytArr(0) = &HE9 ' Assembly code for JMP (FAR)
    43.    
    44.     'add the offset to the new sub to the array
    45.     CopyMemory bytArr(1), lngOffset, ByVal 4
    46.    
    47.     CopyProcMemory lngFPtrOrg, VarPtr(bytArr(0)), 6
    48.  
    49. End Sub

    VB Code:
    1. Option Explicit
    2.  
    3. Private Type DoubleLong
    4.     lng1 As Long
    5.     lng2 As Long
    6. End Type
    7.  
    8. Public Sub SngSqrNEW(lngValue As Long, lngResult As Long)
    9.  
    10.     lngResult = (lngValue \ 2) + &H1FC00000
    11.    
    12. End Sub
    13.  
    14. Public Function BabylonianSqr(ByVal value As Single) As Single
    15.     Dim sngPrev As Single, sngCur As Single
    16.    
    17.     sngCur = value
    18.     Do Until sngPrev = sngCur
    19.         sngPrev = sngCur
    20.         sngCur = 0.5 * (sngPrev + (value / sngPrev))
    21.     Loop
    22.     BabylonianSqr = sngCur
    23. End Function
    24.  
    25.  
    26. Public Sub SngSqr(value As Single, sngResult As Single)
    27.  
    28.     ReplaceSubByJmp AddressOf SngSqr, AddressOf SngSqrNEW
    29.     SngSqr value, sngResult
    30.    
    31.     MsgBox "replaced SngSqr!"
    32. End Sub
    33.  
    34. Public Sub DblSqr(dblValue As Double, dblResult As Double)
    35.  
    36.     ReplaceSubByJmp AddressOf DblSqr, AddressOf DblSqrNEW
    37.     DblSqr dblValue, dblResult
    38.  
    39. End Sub
    40.  
    41. Public Sub DblSqrNEW(dblValue As DoubleLong, dblResult As DoubleLong)
    42.     Dim BitShiftDbl(1) As Long
    43.     BitShiftDbl(1) = &H80000000
    44.    
    45.     If dblValue.lng1 >= 0 Then
    46.         dblResult.lng1 = dblValue.lng1 \ 2 Or BitShiftDbl(dblValue.lng2 And &H1)
    47.     Else
    48.         dblResult.lng1 = dblValue.lng1 \ 2 Or BitShiftDbl(dblValue.lng2 And &H1) Or &H40000000
    49.     End If
    50.     dblResult.lng2 = (dblValue.lng2 \ 2) + &H1FF80000
    51. End Sub

    VB Code:
    1. Option Explicit
    2.  
    3. Private Sub Command1_Click()
    4.     Dim sngVal As Single
    5.  
    6.     SngSqr 20, sngVal
    7.     MsgBox sngVal
    8. End Sub
    9.  
    10. Private Sub Command2_Click()
    11.     Dim dblVal As Double
    12.    
    13.     DblSqr 20, dblVal
    14.     MsgBox dblVal
    15. End Sub

  28. #28

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    There is one problem that I noticed earlier on but I thought you'd catch it sooner or later:
    VB Code:
    1. CopyProcMemory lngFPtrOrg, VarPtr(bytArr(0)), 6

    The byte array has five bytes, yet the code is copying six. Is the sixth byte meant to be a zero? Now it might not always be.


    Time for testing once I get home. One of the most interesting features with this code is that Variants could be used much much faster... I guess I'll try benchmarking IfVar function (a replacement of IIf).

  29. #29

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    Good news! It is working for functions as well!

    Bad news: it seems to cause a bit problems, it makes variables a bit flawed, although this problem can be worked around. I believe use of CVar or CLng or whatever is appropriate for the datatype in case will resolve the problem.

    Another problem is that if I pass a constant in (and maybe invalid datatype) then the code will crash.


    Attached is a benchmarking test for DblSqr and IfVar. If you start simplifying the code that does benchmarking of IfVar, you'll soon notice some very odd things happening when you use the varResult variable...

    The thread is almost resolved, I guess the only thing remaining is the need to take a look into what is the problem with the function return variable.
    Attached Files Attached Files

  30. #30
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    errrm, that's a bug.

    of course it has to be
    VB Code:
    1. CopyProcMemory lngFPtrOrg, VarPtr(bytArr(0)), 5

    thanks for finding that one

  31. #31
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    getting functions to work will always be tricky because functions return different datatypes in differtent ways.

    i'll analyze that in more details as soon as i have time to.

  32. #32
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    Quote Originally Posted by Merri
    Bad news: it seems to cause a bit problems, it makes variables a bit flawed, although this problem can be worked around. I believe use of CVar or CLng or whatever is appropriate for the datatype in case will resolve the problem.
    i know what the problem with the IfVar function is.
    VB Code:
    1. varResult = IfVar(True, varTest1, varTest2)
    this doesn't 'copy' varTest1 to varResult. instead you make varResult point to the same data as varTest1. that's fine until you assign a new value to varResult or varTest1. if you change the value of varTest1 for example
    VB Code:
    1. varTest1 = "blabla"
    then VB will free memory where the old data was located and allocates new memory for the new value. but varResult still points to the memory location of the old data. so varResult points to unallocated memory or memory allocated for another purpose.

    the solution is to 'clean' varResult. you should know that from your safearray trick

    VB Code:
    1. Public Sub CleanVariant(varVar As Variant)
    2.     ReplaceSub AddressOf CleanVariant, AddressOf CleanVariantREAL
    3.     CleanVariant varVar
    4. End Sub
    5. Public Sub CleanVariantREAL(varVar As Long128)
    6.     varVar.LowLow = 0
    7.     varVar.UpLow = 0
    8. End Sub
    9.  
    10. Public Function ifVarTest()
    11.     Dim varVal1 As Variant, varVal2 As Variant, varResult As Variant
    12.    
    13.     varVal1 = "hello"
    14.     varVal2 = "world"
    15.    
    16.     'CleanVariant varResult
    17.     varResult = IfVar(True, varVal1, varVal2)
    18.    
    19.     'CleanVariant varResult
    20.     varResult = IfVar(True, varVal1, varVal2)
    21.    
    22.     'CleanVariant varResult
    23.     varVal1 = "oooooooooooooooooops"
    24.    
    25.     Debug.Print varResult
    26.     'CleanVariant varResult
    27. End Function

    ifVarTest will crash. uncomment the CleanVariant calls and it will work.

    so far the only real limitation i've found for replacing functions is that you can't replace a function that returns a floating point variable with a function that returns a non floating point variable.

    here is another code snipplet that might be useful

    VB Code:
    1. Public Function lptr2Long(ByVal lPtr As Long) As Long
    2.     ReplaceSubByJmp AddressOf modSQRT.lptr2Long, AddressOf modSQRT.lptr2LongREAL
    3.     lptr2Long = lptr2Long(lPtr)
    4. End Function
    5. Public Function lptr2LongREAL(lPtr As Long) As Long
    6.     lptr2LongREAL = lPtr
    7. End Function
    8. Public Sub lpttest()
    9.     Dim lngVal As Long, lngPtr As Long
    10.    
    11.     lngVal = 30
    12.     lngPtr = VarPtr(lngVal)
    13.     Debug.Print lptr2Long(lngPtr)
    14. End Sub
    Last edited by Agilaz; Jul 15th, 2006 at 05:21 AM. Reason: fixed VBCODE tags and typo

  33. #33
    Lively Member Agilaz's Avatar
    Join Date
    Jun 2006
    Posts
    98

    Re: Making pointer of a long variable for another variable

    here's a new project. it supports SHORT and FAR jumps. SORT jumps are a bit faster

    the project also incudes an example how to use this technique to load DLLs and call APIs at runtime. this method is faster than VBs standard API call method as it doesn't have VBs overhead. the only drawback: there is no automatic UNICODE -> ANSI conversion if you pass strings to an API. errm...and err.LastDllError will most likely not work.

    and last but not least it shows how to use this techniqe to run a compiled assembly function stored in a file. it's probably better to store it as a resource but i was to lazy to implement that :P
    Attached Files Attached Files

  34. #34

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Making pointer of a long variable for another variable

    Quote Originally Posted by Agilaz
    the only drawback: there is no automatic UNICODE -> ANSI conversion if you pass strings to an API.
    I consider this a major advantage over the native method of calling API. Finally I can use the wide version API calls easily without StrPtr

    Guess I'll be updating my Unicode user controls sooner or later.

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