Pointers, like in C?-VBForums
Results 1 to 28 of 28

Thread: Pointers, like in C?

  1. #1

    Thread Starter
    New Member
    Join Date
    Jun 2005
    Posts
    2

    Question Pointers, like in C?

    Hi everyone:

    I was wondering if anyone knew how to define and call pointers to functions/subs in VB like we used to do in C? I tried to use the AddressOf opperator, but I can't seem to get it to work. I attempted a simple test: When a command button is clicked, it calls a function by pointer...

    I tried various arangements, but the closest I came was -

    [In a form (with 1 text box and one command button...) -]
    Private Sub Command1_Click()
    Dim FnPtr As Long

    FnPtr = GetFnPtr(AddressOf testfunction)
    Call FnPtr '<-Didn't work; gave error
    End Sub

    [In a module -]
    'Function wrapper?
    Public Sub testfunction(testval As Integer)
    Form1.Text1.Text = "DID IT!"
    End Sub

    'Assign address
    Public Function GetFnPtr(ByVal ptr As Long) As Long
    GetFnPtr = ptr
    End Function

    Any ideas?
    Share on Google+

  2. #2
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    The AddressOf keyword was added to VB5 so we could call API functions that uses callbacks, like EnumWindows. But according to the MSDN:
    Pointers to Visual Basic functions cannot be passed within Visual Basic itself. Currently, only pointers from Visual Basic to a DLL function are supported.
    When it comes to the DLL part it states:
    You can create your own call-back function prototypes in DLLs compiled with Visual C++ (or similar tools). To work with AddressOf, your prototype must use the __stdcall calling convention. The default calling convention (_cdecl) will not work with AddressOf.
    Basically it doesn't work with ActiveX DLLs that you create in VB.

    However Public functions/subs declared in a Form or a Class module can be called using the CallByName function.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  3. #3
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Here are a few Undocumented VB things :

    VarPtr - Returns the address of a variable.

    VarPtrArray - Returns the address of an array.

    StrPtr - Returns the address of the UNICODE string buffer.

    VarPtrStringArray - Returns the address of an array of strings.

    ObjPtr - Returns the pointer to the interface referenced by an object variable.

    Have a look at this: http://support.microsoft.com/kb/q199824/

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  4. #4
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    All of those return the address of a storage, and you could use those together with CopyMemory but fact remains that you can't call a function from within VB using a function address.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  5. #5
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    I played around with the thought that what if there actually was a way to call a function by using the address in VB how would that look? Well Microsoft claims that it's impossible so there is obviously no native way of doing it. So I started thinking if there was any API function at all that takes an address to call a function and then it hit me... Hey, I've subclassed more Forms then I can count and I always makes a call to CallWindowProc when I do so. That's of course used to call the origional WndProc but how does Window know where that function resides... Simply because the first argument is the address to that function.

    So can Windows really know if there actually is a WndProc at that memory location? No, I thought it can of course not know if I use it as a message pump or not so I tried it out and you can actually call any function by address using this approach!

    The only drawback is the number of arguments. A WndProc has 4 arguments and CallWindowProc put these on the stack so your function have to pull them of there. So the function you want to call must have exactly 4 arguments, even if it doesn't use them. The great thing is that these arguments can be of any type as long as it's acceptable to pass the values ByRef. Arguments of the type Long can be passed either ByRef or ByVal but all other types or objects must be called ByRef since you would then pass a pointer to them.

    Well there is one other drawback... Callback procedures can only reside in regular BAS modules not in Form or Class modules.

    Let's try this out... It's FUN!!!! Create a new project and add a BAS module to it. Copy and paste the following code:
    VB Code:
    1. Private Declare Function CallWindowProc _
    2.  Lib "user32.dll" Alias "CallWindowProcA" ( _
    3.  ByVal lpPrevWndFunc As Long, _
    4.  ByVal hwnd As Long, _
    5.  ByVal msg As Long, _
    6.  ByVal wParam As Long, _
    7.  ByVal lParam As Long) As Long
    8.  
    9. Private Sub ShowMessage( _
    10.  msg As String, _
    11.  ByVal nUnused1 As Long, _
    12.  ByVal nUnused2 As Long, _
    13.  ByVal nUnused3 As Long)
    14.     'This is the Sub we will call by address
    15.     'it only use one argument but we need to pull the others
    16.     'from the stack, so they are just declared as Long values
    17.     MsgBox msg
    18. End Sub
    19.  
    20. Private Function ProcPtr(ByVal nAddress As Long) As Long
    21.     'Just return the address we just got
    22.     ProcPtr = nAddress
    23. End Function
    24.  
    25. Public Sub YouCantDoThisInVB()
    26.     Dim sMessage As String
    27.     Dim nSubAddress As Long
    28.    
    29.     'This message will be passed to our Sub as an argument
    30.     sMessage = InputBox("Please input a short message")
    31.     'Get the address to the sub we are going to call
    32.     nSubAddress = ProcPtr(AddressOf ShowMessage)
    33.     'Do the magic!
    34.     CallWindowProc nSubAddress, VarPtr(sMessage), 0&, 0&, 0&
    35. End Sub
    Now just call the YouCantDoThisInVB() Sub, that's where the magic happens.

    I admit that my example is just a very cumbersome way of showing a simple MsgBox, but just think of the potential new world that just opened. How we now actually can create generic sort classes in VB by using a callback that does the comparison instead of using Variant data types. Such things C programmers always have done.

    You have to excuse me, I usually don't blow my own horn, but this was really COOL.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  6. #6
    Lurker
    Join Date
    Jan 2005
    Location
    Everywhere
    Posts
    13,651

    Re: Pointers, like in C?

    Sweet!

    I had been compiling a C++ DLL to call functions using addresses, but of course that's a much easier way of doing it (although it essentially comes to the same thing in the end).

    And also, to get around the 4 arguments limitations, you could pass a pointer to a SAFEARRAY containing your arguments list and read the data out of that. Obviously the method isn't suitable for common use, but for those occasions when you need it it's great.

    Now if only we could find a way to get around the BAS module limitation... I know you can do it using machine code, so maybe there are ways to do that too.
    Share on Google+

  7. #7
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Quote Originally Posted by penagate
    Now if only we could find a way to get around the BAS module limitation... I know you can do it using machine code, so maybe there are ways to do that too.
    The AddressOf keyword only works with procedures in modules. The reason MS implemented it this way is because normally callbacks are called from an API function that runs in a separate thread and as such they can't be used with classes or forms that's created by the main thread in VB.

    Since besides the AddressOf keyword there isn't any other native way to get the address of a function I doubt you will find a way. However I don't think that is much of a limitation. The fact that the callback needs four arguments is not much of a limitation either. All callbacks have a determent signature that you must follow so that doesn't matter much. If you need more arguments you can simply do as the API functions does, pass the address of a UDT (struct).
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  8. #8
    Lurker
    Join Date
    Jan 2005
    Location
    Everywhere
    Posts
    13,651

    Re: Pointers, like in C?

    Quote Originally Posted by Joacim Andersson
    The reason MS implemented it this way is because normally callbacks are called from an API function that runs in a separate thread and as such they can't be used with classes or forms that's created by the main thread in VB.
    Why is that? Surely it is possible to get the base address of the class and then add the offset of the function? Or is it because VB shifts them around in memory, so they're not always in the same place?
    Share on Google+

  9. #9
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    A function in a class becomes a method of the object. Every new instance of the object will get it's own copy.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  10. #10
    Lurker
    Join Date
    Jan 2005
    Location
    Everywhere
    Posts
    13,651

    Re: Pointers, like in C?

    Quote Originally Posted by Joacim Andersson
    A function in a class becomes a method of the object. Every new instance of the object will get it's own copy.
    Yes but suppose you want a callback from a class module. Say you write a class to subclass a form (i.e. you use a property to attach the form to that class). You could do it by putting the WndProc in a .bas module, and in that module have a collection of pointers to classes, and with each callback call the method in the appropriate class. Long-winded, it works, but it's clumsy.

    Now, wouldn't it be much easier if we could just stick the WndProc in the class module, and use something like AddressOf(Me.WndProc) from within that class? Surely each instance knows where its own functions are, otherwise the code wouldn't work at all because none of the calls would be resolved.
    Share on Google+

  11. #11
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    I agree with you Penagate you don't have to try to convince me . But a class method can not be called by a separate thread.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  12. #12
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    I don't have much knowledge of pointers. But I just know that memory is allocated to a variable only when it is used for the first time. But this seems to be something different.
    I do this:
    VB Code:
    1. Private Sub Command1_Click()
    2.     Dim i As Long, s As String
    3.     For i = 1 To 10
    4.         Debug.Print StrPtr(CStr(i))
    5.     Next
    6. End Sub
    And get this result:
    1804836
    1961516
    1804836
    1961516
    1804836
    1961516
    1804836
    1961516
    1804836
    1961516

    NO matter how many times I run this code, it seems to be working with only 2 memory locations. Shouldn't it be either a fixed location or random location?

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  13. #13
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Nope. The Integer is stored in the same location all the time but in the loop you're creating a new string each time using the CStr function. That string is stored in a new location each time. That VB can reuse the same memory location every other time just means that the compiler does something right .

    If you want to see the storage of the integer you need to use code simular to this:
    VB Code:
    1. Private Sub Command1_Click()
    2.     Dim i As Long
    3.     For i = 1 To 10
    4.         Debug.Print VarPtr(i)
    5.     Next
    6. End Sub
    That would result in the same address all ten times, however it might not be the same each time the procedure runs, but if you just keep clicking on the button it probably will be.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  14. #14
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    No, what I was wondering with the StrPtr(CStr(i)) was that Shouldn't it be a different location every time. Why it is exactly 2 locations each time (not more, not less).

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  15. #15
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Simply because the compiler is somewhat smart. When you create the new string the other falls out of scope since there is no variable pointing to it. So that memory location can then be reused.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  16. #16
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Quote Originally Posted by Joacim Andersson
    Simply because the compiler is somewhat smart. When you create the new string the other falls out of scope since there is no variable pointing to it. So that memory location can then be reused.
    Can't it work with just one location, since there is no other memory allocation being done other than this line StrPtr(CStr(i)) ?

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  17. #17
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    No, since you're in a loop the first string doesn't fall out of scope until the next string is created.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  18. #18
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Oh No! I think I'm a bit confused.
    It is reusing the same memory location (just one location).
    I was under the impression that StrPtr returns the address of a STRING variable. Isn't his true?

    This clarifies it:
    VB Code:
    1. Private Sub Command1_Click()
    2.     Dim i As Long
    3.     For i = 1 To 10
    4.         Debug.Print VarPtr(CStr(i)), StrPtr(CStr(i))
    5.     Next
    6. End Sub
    This is the result:

    1241268 1692964
    1241268 1493316
    1241268 1961476
    1241268 1493316
    1241268 1961476
    1241268 1493316
    1241268 1961476
    1241268 1493316
    1241268 1961476
    1241268 1493316

    But what does StrPtr do then??

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  19. #19
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    It returns the address where a string is located. You create a string by calling the CStr function, the string returned by that function must be stored somewhere and it's in the location returned by StrPtr.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  20. #20
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    BTW, your last example still shows that the CStr function will alternate which address is used to store the memory location. In this case you just call it twice directly after each other but there is still two different addresses used. First after the string has fallen out of scope can the address be reused, which in this case will be in the next loop turned since you've already recreated a string out of the i variable.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  21. #21
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Quote Originally Posted by Joacim Andersson
    It returns the address where a string is located. You create a string by calling the CStr function, the string returned by that function must be stored somewhere and it's in the location returned by StrPtr.
    No it doesn't seem to be:
    This thing:
    VB Code:
    1. Private Sub Command1_Click()
    2.     Dim i As Long, str1 As String
    3.     str1 = "SOMETHING"   'allocated it the memory
    4.     For i = 1 To 10
    5.         str1 = "SOMETHING"
    6.         Debug.Print VarPtr(str1), StrPtr(str1)
    7.     Next
    8. End Sub
    9.  
    10. Result:
    11.  1241276       1747140
    12.  1241276       1804836
    13.  1241276       1747140
    14.  1241276       1804836
    15.  1241276       1747140
    16.  1241276       1804836
    17.  1241276       1747140
    18.  1241276       1804836
    19.  1241276       1747140
    20.  1241276       1804836

    Obviously the string has been allocated memory before teh debug.print statement now. The StrPtr and VarPtr are just retrieving the values. But they seem to point to different memory locations for the same variable. Or the StrPtr function retrieves something else, not the address of this variable.

    Pradeep
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  22. #22
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Well a VB string is stored as a BSTR, if you would use VarPtr on a VB String you would get the address of the BSTR, which is a pointer to a pointer of the string. The StrPtr however will get the address of the first Unicode character in the string.

    The origional pointer (the pointer that points to the pointer that points to the characters, hmm... Pointers to Pointers are confusing) doesn't change. However if you assign a new value to the string a new string is created and the StrPtr in the string is changed to point to that location. The reason your StrPtr is changing is because you constantly give it a new value (which happens to be exactly the same text, but that string is recreated in every loop turn) inside the loop.

    I drawed a simple image for you that probably explains this a lot better.
    Attached Images Attached Images  
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  23. #23
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Thank u so much.
    This clears all the doubts.

    So I should never rely on StrPtr for address of any string. Instead I should use VarPtr to get address of any variable - whether string or not.


    EDIT: Uuhh.. I cant rate ur post!! It says - You must spread some Reputation around before giving it to Joacim Andersson again.
    Nevertheless this was very helpful.

    Pradeep
    Last edited by Pradeep1210; Jun 11th, 2005 at 03:30 PM.
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  24. #24
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    No! If you want the address of the actual text you need to use StrPtr because that is where the text is stored. But assigning a new string (even if it's happens to be the same text) will of course change the pointer so it now points to where this text is stored in memory.

    So it depends on if you want the address of the text or the address of the BSTR (the address that points to an address of the text).
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  25. #25
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,600

    Re: Pointers, like in C?

    Thanks, I got it now
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...
    Share on Google+

  26. #26
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Just to make this clear (for anyone else that might be reading this thread) I just want to give an example of when you would need to use StrPtr. VB internally stores any strings in Unicode format. However as soon as you pass a string to any API function VB will replace that with an Ansi string. So if you want to call an API function that requires a string like GetUserName or GetWindowsDirectory you would use the Ansi versions of those APIs instead of the wide (or Unicode) version of those functions.

    So let's take the GetUserName function as an example. You would probable wrap up this call into a regular VB function that would return the user name. So it would probably look something like this:
    VB Code:
    1. Private Declare Function GetUserName _
    2.  Lib "advapi32.dll" Alias "GetUserNameA" ( _
    3.  ByVal lpBuffer As String, _
    4.  ByRef nSize As Long) As Long
    5.  
    6. Public Function CurrentUser() As String
    7.     Dim sBuff As String
    8.     'make sure this buffer string is big enough to hold
    9.     'the return value.
    10.     sBuff = Space$(256) 'or sBuff = String(256, vbNullChar)... it does not matter
    11.     Call GetUserName(sBuff, 256)
    12.     CurrentUser = Left$(sBuff, InStr(sBuff, vbNullChar))
    13. End Function
    So we have declared the ANSI version of GetUserName and we have also wrapped the call up in a public function called CurrentUser. Inside this function we've declared a string and also made sure that it is big enough to hold the returned value.

    So what does VB do now? It will take our Unicode string, called sBuff, and replace that with an ANSI version of it. So it creates a new string before the GetUserName is called. It also have to turn the returned string back into a Unicode string again so we can return the result.

    That means that we during this time have created no less then 5 strings. The first string is created as soon as we declare the sBuff variable. It will point to an empty string (you have to understand that an empty string is still a string). It then creates another string that is returned by our call to the Space function (so StrPtr is now shifted to the new string containing a lot of spaces). This was the second string. We now call the API function in which VB has to take our Unicode string (containing nothing but spaces) and create a new Ansi string (that would be our third string).

    This string is now filled with the current user name (no new string have been created to do this, the memory location of the string have just been modified). VB now have to turned back this Ansi string into a Unicode string (this is the forth string that VB will create) and assign that back to sBuff.

    The fifth string is created by our Left$ call that trims of the NULL value and the returned value of this string is then pushed on to the stack as the return value of our function.

    Everyone that have used VB for a while knows that calling an API function is normally much faster then doing the "pure" VB way. This is normally true but what will VB do more then create all of those string above when we call the GetUserName API? For each and every API call we make VB will also make another API call. This call will be to the GetLastError API function. VB will always call this function after each and every API call you make and the return value will be used to set the Err.LastDLLError property.

    You should never call the GetLastError function from VB, just check the LastDLLError of the Err object instead! The simple reason for this is that VB internally will make a lot of API calls so if you would call the GetLastError function you would not know if the returned value was because of an error you caused or because of an error caused by VB itself.

    With that said, let's see how we can make a Unicode call to the GetUserName function that will be a lot faster since less strings have to be created. In this case you would use StrPtr to pass the pointer to the string (if you would use VarPtr here you would probably cause a GPF and VB + your app would crash and burn). Since StrPtr returns an address rather then the string itself you need to change the declaration of the function.
    VB Code:
    1. Private Declare Function GetUserName _
    2.  Lib "advapi32.dll" Alias "GetUserName[b][color=red]W[/color][/b]" ( _
    3.  ByVal lpBuffer As [b][color=red]Long[/color][/b], _
    4.  ByRef nSize As Long) As Long
    5.  
    6. Public Function CurrentUser() As String
    7.     Dim sBuff As String
    8.     'make sure this buffer string is big enough to hold
    9.     'the return value.
    10.     sBuff = String(256, vbNullChar)
    11.     Call GetUserName(StrPtr(sBuff), 256)
    12.     CurrentUser = Left$(sBuff, InStr(sBuff, vbNullChar))
    13. End Function
    In this case you only pass a pointer (or a Long integer value) to the GetUserName function so there is no new strings created by VB. Since there is no conversion in the first place from a Unicode string to an Ansi string, VB doesn't have to do the opposite either so there is 2 strings here that never will be created.

    Needless to say the above code will run much faster since it contains less string creations. So why don't we always use this approach in VB? The simple answer is that Win9x/ME expects the Ansi string and not a Unicode string. So if you develop for those platforms you can't use the above approach.

    Before anyone else points this out I just want to make it clear that I'm fully aware that there is an error in my statement above about how VB creates the initial empty string when you declare a String variable. This error in my statement was intentional and the one that figures out what it is will win the grand price (what that is will be determent later ).

    Anyway the whole idea of all this was to show that if you would use VarPtr instead of StrPtr in the Unicode call above your application would have crashed and burned since Windows would then have written the string (the user name in this example) to the wrong memory address.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

  27. #27
    New Member
    Join Date
    May 2014
    Posts
    7

    Re: Pointers, like in C?

    A variation on the function pointer method using a UDT as parameter.
    Put the following code in a module, drop a test button on a form and call Start from the click event.

    Code:
    Option Explicit
    
    Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    
    Public Type MySubParams
        ID As Long
        Name As String
    End Type
    
    Sub MySub1(p As MySubParams, a As Long, b As Long, c As Long)
        Debug.Print VarPtr(p)
        MsgBox "MySub1 " & p.ID & " " & p.Name
    End Sub
    
    Sub MySub2(p As MySubParams, a As Long, b As Long, c As Long)
        Debug.Print VarPtr(p)
        MsgBox "MySub2 " & p.ID & " " & p.Name
    End Sub
    
    Function CallSub(address As Long, params As Long)
        CallSub = CallWindowProc(address, params, 0&, 0&, 0&)
    End Function
    
    Sub Start()
        Dim a As Long
        Dim p As MySubParams
        p.ID = 1
        p.Name = "Test1"
        Debug.Print VarPtr(p)
        Debug.Print CallSub(AddressOf MySub1, VarPtr(p))
        p.ID = 2
        p.Name = "Test2"
        Debug.Print CallSub(AddressOf MySub2, VarPtr(p))
    End Sub
    Share on Google+

  28. #28
    Super Moderator Joacim Andersson's Avatar
    Join Date
    Jan 1999
    Location
    Sweden
    Posts
    14,649

    Re: Pointers, like in C?

    Do you realize that you just brought a 9 year old thread back to life?

    Thread closed.
    Joacim Andersson
    If anyone's answer has helped you, please show your appreciation by rating that answer.
    I'd rather run ScriptBrix...
    Joacim's view on stuff.

    MVP
    Share on Google+

Posting Permissions

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



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.