Compilers Demystified: Function Pointers in Visual Basic 6.0

Figure 1: Imagine such code being legal in VB.

Introduction

The purpose of this investigation is to enable applications written in VB6 to use function pointers. Other advantages may be discovered, such as embedding native code in VB applications, thus extending the world of possibilities without the need of external DLLs. For the sake of keeping this as brief and concise as possible, other variations of the techniques used have been ignored and the focus have been maintained on detailing the one methodology that handles more common situations. Before reading a comprehensive examination, I'm assuming you'd like to actually see a working sample project (see the download at the end of this article).

Did You Say Function Pointers?

Because pointers in general aren't VB6 specific, it might sound crazy to talk about function pointers in VB6. So, you should first see how you usually get function pointers in VB6. With an AddressOf operator? Yes, but not only! There's also the GetProcAddress Win32 API from kernel32.dll that can be used at runtime to retrieve addresses of functions exported by other DLLs. Again, not only... There're other scenarios! But, why would one use runtime loading when you simply can use a Declare statement? For the same reasons a C/C++ programmer would, for example, by using different DLLs (or versions of the same DLL) depending on the environment in which the application is running. In fact, the whole concept of plugins is based on loading, probably on demand, external components that export the expected functionality. For example, some codecs are even downloaded on demand, and then loaded by the application that uses them. Also, a function pointer might be given by an external module as a callback (aka delegate) and its value may depend on the state of some objects. In OOP, a well-known behavioral pattern called 'Template Method' is characterized by changing control flow at runtime. Using function pointers may considerably reduce the effort put in its implementation by reducing the number of classes that need to be defined.

Using Pointers that Are Not Type Safe

If you ever used EnumWindows API in VB6, you may have noticed that there's nothing that enforced you to pass the address of a function with a correct prototype as a callback. The code will fail at runtime but compile without complaining. Delegates in VB .NET overcome this issue, although their main purpose might have been to ensure availability of code, because CLR may discard compiled code that is not referenced. Although other languages have a way to declare type safe pointers (as typedef in C/C++), in VB6 you can only treat them as signed 4-byte numbers. The developer will be responsible for type checking, without any help from the compiler.

Making the Call

The method of choice for calling addresses stored as Long is replacing entries in the 'Virtual Function Table' (VFT) of a class because it provides enough flexibility, while it is still easy to use. It has the same behaviour in IDE, Native code and P-Code; this helps debugging. A vftable is a mechanism used in programming language implementations to support dynamic polymorphism; in other words, runtime method binding. Where some online resources describe how the VC++ 6.0 compiler creates vftables for classes, I couldn't find anything regarding the VB 6.0 compiler. Typically, a compiler creates a separate vftable for each class and stores its pointer as a hidden member of each base class, often as the first member. The VC++ compiler builds the vftable for each class in a read-only page of the code section in a similar way to string literals.

To modify its contents, one needs to use VirtualProtect API to temporarily change the access rights. The first address in the vftable created by VC++ points to the scalar destructor function of the class, and it is followed by the addresses of virtual functions, in their order of declaration. Although VC++ supports multiple inheritance and handles pure virtual function calls, your objective can be simply achieved by identifying how VB retrieves the address of a public method, given the address of an instance from a class. A visual inspection of a class instance was required to establish the location and content of the vftable. By displaying the memory content from the addresses of two instances of the same class, you should be able to identify which is the pointer to the vftable. Because both instances point to the same vftable, the pointer value must be the same and must belong to your VB module (by default, the starting address is 0x00400000). As you can see, the pointer to the vftable is stored as the first 4 bytes within the class instances.

Figure 2: Two objects sharing the same VFT.

Figure 3: Addresses at offset &H1C belong to your module.

The first seven addresses in the vftable point to code within 'msvbvm60.dll'. Modifying the class definition by adding more public methods will change the vftable content starting with offset &H1C. To consolidate the theory, I wrote an external DLL for breaking into VB calls to methods of an object and reading the disassembly with the VC++ debugger. This is much easier than it sounds. Have a global procedure creating an instance of a VB class, and call its first public method. At runtime, display the address of the global function, the address of the class instance, and the pointer to the vftable. Load the process with the VC++ debugger by pressing F11. Write down the current instruction pointer (eip of the application's entry point) and change it to the address of the global procedure (type it in the Registers window and press Enter). Set a breakpoint at this address. Change the eip to the old value and resume execution. When the breakpoint is reached, step through the code and observe the Registry values until the method of the class is called. The screenshots show how the eax register is getting the address of the object (0x14BE70), from which 4 bytes are copied into the ecx register, representing the address of the VFT (0x4033B8). The instruction at 0x402391 is the call to the first public method of that class and, as you can see, its offset in the vftable is &H1C.

Figure 4: Disassembly showing how to get addresses from vftable.

My investigation continued to see whether private member data or methods affect the location or content of the vftable. The answer was no, but public member variables change the contents of the vftable by inserting accessors and modifiers that will be called when using the member data outside the class. For proof of concept, I wrote the following test:

VERSION 1.0 CLASS
Attribute VB_Name = "DynamicVFT"
'Byte offset of first index in Virtual Function Table (VFT).
Private Const OffsetToVFT = &H1C
'Swaps the addresses of 2 public methods with the same prototype.
Private Sub SwapPlayers()
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address.
   Dim fnAddress As Long
   'get AddressOf Play.
   CopyMemory fnAddress, ByVal pVFT + OffsetToVFT, 4
   CopyMemory ByVal pVFT + OffsetToVFT, _
      'replace Play address with Replay address.
      ByVal pVFT + OffsetToVFT + 4, 4
   'replace Replay address with Play address.
   CopyMemory ByVal pVFT + OffsetToVFT + 4, fnAddress, 4
End Sub
'Address of this method can be found at first index in the VFT.
Public Sub Play()
   Debug.Print "Dynamic VFT plays: White move."
   Call SwapPlayers
End Sub
'Address of this method can be found at second index in the VFT.
Public Sub Replay()
   Debug.Print "Dynamic VFT plays: Black move."
   Call SwapPlayers
End Sub
Sub Main()
   'Phase 1: Making the call.
   Dim dynObj As New DynamicVFT
   Dim idx As Long
   For idx = 0 To 9
      dynObj.Play
   Next idx
End Sub
Note: These methods had the same prototype. Swapping the addresses in the vftable worked as expected and the output of the above code is shown below:

Dynamic VFT: White move.
Dynamic VFT: Black move.
Dynamic VFT: White move.
Dynamic VFT: Black move.
...

So, changing the values in the VFT of a VB6 class will replace the methods of that class. One more important thing to remember when modifying vftables is that they are shared by all instances of that class.

Compilers Demystified: Function Pointers in Visual Basic 6.0

Passing Parameters

Assuming that you'll change the addresses in the VFT of a class, further examination is required on how these methods are called. This section will describe the calling convention used as well as the position of the parameters on the stack. Displaying from the VFT an address of a member procedure taking one Long parameter (4 bytes on the stack) and loading the process in VC++ debugger, the assembly at that address can be observed. It belongs to a 'jump table':

00401451 jmp         004025A0

Each entry of the jump table points to the location of the compiled code for the member method defined:

004025A0 push        ebp
004025A1 mov         ebp,esp
004025A3 sub         esp,0Ch
004025A6 push        401136h
004025AB mov         eax,fs:[00000000]
004025B1 push        eax
004025B2 mov         dword ptr fs:[0],esp
004025B9 sub         esp,8
004025BC push        ebx
004025BD push        esi
004025BE push        edi
004025BF mov         dword ptr [ebp-0Ch],esp
004025C2 mov         dword ptr [ebp-8],401118h
004025C9 mov         dword ptr [ebp-4],0
004025D0 mov         eax,dword ptr [ebp+8]
004025D3 push        eax
004025D4 mov         ecx,dword ptr [eax]
004025D6 call        dword ptr [ecx+4]
004025D9 mov         eax,dword ptr [ebp+8]
004025DC push        eax
004025DD mov         edx,dword ptr [eax]
004025DF call        dword ptr [edx+8]
004025E2 mov         eax,dword ptr [ebp-4]
004025E5 mov         ecx,dword ptr [ebp-14h]
004025E8 pop         edi
004025E9 pop         esi
004025EA mov         dword ptr fs:[0],ecx
004025F1 pop         ebx
004025F2 mov         esp,ebp
004025F4 pop         ebp
004025F5 ret         8

[NativeCode_Stacks.jpg]

Figure 5: Stack differences.

For experienced assembly readers, the above listing is quite straightforward. At this point, the emphasis is on the instructions modifying the stack. In fact, the last instruction tells us almost all we need to know. First, the callee is cleaning the stack, not the caller, thus the calling convention cannot be __cdecl, nor __fastcall. This method takes only one Long parameter (4 bytes in size), so why is the method removing 8 bytes from the stack? Because there's an extra parameter pushed onto stack before the call: the pointer to the object for which you're calling the procedure (also known as the 'this' pointer). To confirm that the __thiscall calling convention isn't used, have another look at the assembly listing and you'll see that first time the ecx register is used, at address 4025D4, it is being written, not read. Thus, the pointer to the object isn't passed through the ecx register, nor other registers. Without even looking at what the caller does, you already can consider the calling convention as __stdcall with object pointer passed as the last parameter on the stack. No surprise here, because VB6 is known to use it extensively. Another test is in order; it is supposed to confirm the value of the last, extra parameter pushed onto stack. Remember, in the __stdcall calling convention, parameters are pushed from right to left, as they are declared.

VERSION 1.0 CLASS
Attribute VB_Name = "MemberVsGlobal"
'Replaces address of MemberProcedure with given address at which
'should reside a procedure using __stdcall calling convention and
'accepts 2 parameters of type Long;
'restores the original address of MemberProcedure when given
'address is 0
Private Sub ReplaceMemberWithGlobal(ByVal fnAddress As Long)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
   Static oldAddress As Long    'static variable which stores the
   If (oldAddress = 0) Then     'original MemberProcedure address
   'get MemberProcedure address
      CopyMemory oldAddress, ByVal pVFT + OffsetToVFT, 4
   End If
   If (fnAddress = 0) Then
   'restores original MemberProcedure address
      CopyMemory ByVal pVFT + OffsetToVFT, oldAddress, 4
   Else
      CopyMemory ByVal pVFT + OffsetToVFT, _
         'replace MemberProcedure address with given global address
         fnAddress, 4
   End If
End Sub
'Restores the original MemberProcedure address
Private Sub Class_Terminate()
   ReplaceMemberWithGlobal 0
End Sub
'Its address in the VFT will be replaced with given fnAddress after
'the first call, thus becoming inaccessible after its first return
Public Sub MemberProcedure(ByVal fnAddress As Long)
   Debug.Print Hex$(ObjPtr(Me)) & _
      ".MemberProcedure(0x" & Hex$(fnAddress) & ")"
   ReplaceMemberWithGlobal fnAddress
End Sub
'Procedure for replacing a member procedure
Private Sub GlobalProcedure(ByVal objInstance As Long, _
   ByVal parameter As Long)
   Debug.Print Hex$(objInstance) & _
      ".GlobalProcedure(0x" & Hex$(parameter) & ")"
End Sub
Sub Main()
   'Phase 2: Passing parameters
   Dim MvsG As New MemberVsGlobal
   MvsG.MemberProcedure AddressOf GlobalProcedure
   MvsG.MemberProcedure &HB0A
End Sub

Notice in the output that the same value obtained by ObjPtr(Me) in the class' method as the objInstance parameter for the global procedure that replaced it, thus confirming your theory that a pointer to the object is pushed onto the stack before the call:

2499D8.MemberProcedure(0xAB16B4)
2499D8.GlobalProcedure(0xB0A)

An interesting behaviour was observed when running the last example under IDE. If you remove the Class_Terminate implementation, running the code twice will not call the original MemberProcedure if you don't either remake (Alt+F K) the executable or reload the project. An examination of this behaviour under IDE isn't required for this purpose, but it is good to know that in case you observe a corrupt object while debugging, you should remake it before restarting execution.

Embedding Native Code

At this point, you know that you can use a global procedure to replace a member procedure of a class, if the global procedure has the same prototype but with an extra parameter: the pointer to the object for which the method is being called. Hmm, but what you need is almost the opposite! You won't need to call global procedures taking as the first parameter a pointer to an object. How do you remove the extra parameter before the call reaches the global procedure? You will embed some native code written in assembly. Such an implementation is known as a stub or proxy. In other situations, it can be used for logging or subclassing. Your purpose is to adapt the call as expected. When the call to the member procedure has been made, the stack will contain the return address, the pointer to the object, followed by the parameters for the method called. Your stub is supposed to remove the pointer to the object, such as the return address that is immediately followed by the parameters, and then jump to the desired forwarding address. Remember that you assume at this stage that the forwarding address points to a procedure, not a function (does not return anything), and its calling convention is __stdcall. The assembly that will do the job follows:

   pop    eax              // remove return address from stack
   pop    ecx              // remove pointer to object from stack
   push   eax              // push return address onto stack
   mov    eax,XXXXXXXXh    // XXXXXXXX can be replaced here with
                           // any forwarding address
   jmp    eax              // jump to forwarding address

You can use any assembler to produce native code from the above. I have written it as a VC++ '__asm' block and then copied the native code produced from the disassembly view. The native code also can be found in the associated '*.cod' file (assembly listing file) if you set the VC++ compiler option '/FAc' or '/FAcs' (Listing file type: 'Assembly with Machine Code' or 'Assembly, Machine Code, and Source'). How do you 'embed' the native code in VB6? Copy the listing and then remove the addresses at the beginning of each line and the assembly source, keeping only the machine code bytes as hex characters; remove any spacing between them; format it as a VB constant string and you should obtain something like this: "58" & "59" & "50" & "B8XXXXXXXX" & "FFE0".

The XXXXXXXX can have any value you want because it will be replaced at runtime with the forwarding address that you need to call, your function pointer. Declare a hex string as a constant; convert it to a Byte array; allocate the same number of bytes with the GlobalAlloc API and copy the Byte array; use the memory handle as the address to your native code; discard the allocation with GlobalFree API when the native code is not required anymore. To demonstrate how embedding native code works and that a stub can successfully replace a member procedure with a non-member procedure of the same prototype, I am providing a more general approach in the following class implementation and sample test of using the class:

VERSION 1.0 CLASS
Attribute VB_Name = "StubCallToSub"
Private Const hexStub4ProcedureCall = "58" & "59" & _
   "50" & "B822114000" & "FFE0"
'An array that saves the original addresses to the member procedures
Private VFTable() As Long
'An array that saves addresses of allocations made for stubs,
'in order to be freed
Private VFArray() As Long
'Sets initial state of the private arrays, thus UBound will not
'fail on first call
Private Sub Class_Initialize()
   ReDim VFTable(0)
   VFTable(0) = 0
   ReDim VFArray(0)
   VFArray(0) = 0
End Sub
'Removes only existing stub for member specified by index
Private Sub RemoveStub(ByVal index As Long)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
   If (index < 1) Then Exit Sub
   If (index > UBound(VFTable)) Then Exit Sub
   If (VFTable(index) <> 0) Then     'stub exists for this member
      Dim oldAddress As Long
      oldAddress = VFTable(index)
      CopyMemory ByVal pVFT + OffsetToVFT + index * 4, oldAddress, _
         4 'restore original member address
      VFTable(index) = 0
      GlobalFree VFArray(index)     'discard the allocated memory
      VFArray(index) = 0            'for stub implementation
   End If
End Sub
'Replaces / restores the address of a member procedure of this class
Public Sub ReplaceMemberSubWithStub(ByVal index As Long, _
                                    ByVal fnAddress As Long)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
   'restore all the original addresses
   If (index < 1) Then
      For index = 1 To UBound(VFTable)
        RemoveStub index
      Next index
   Else
   'restore only the address for the index specified
      If (fnAddress = 0) Then
         RemoveStub index
      Else 'replace the address of a member specified by index
         If (index > UBound(VFTable)) Then
           'resize the array to save original addresses
            ReDim Preserve VFTable(index)
            VFTable(index) = 0
            'resize the array to save changes in the VFT
            ReDim Preserve VFArray(index)
            VFArray(index) = 0
        End If
        'check if a stub exists for this member and needs to be
        'removed first
        RemoveStub index
        Dim oldAddress As Long
        'get original member address
        CopyMemory oldAddress, ByVal pVFT + OffsetToVFT + index * 4, 4
        VFTable(index) = oldAddress
        Dim hexCode As String
        hexCode = hexStub4ProcedureCall
        Dim nBytes As Long     'number of code bytes to allocate
        nBytes = Len(hexCode) \ 2
        Dim Bytes() As Byte    'array of code bytes converted from hex
        ReDim Preserve Bytes(1 To nBytes)
        Dim idx As Long        'loop counter
        'convert each pair of hex chars to a byte code
        For idx = 1 To nBytes
           Bytes(idx) = Val("&H" & Mid$(hexCode, idx * 2 - 1, 2))
        Next idx
        'replace the forwarding address in the native code
        CopyMemory Bytes(5), fnAddress, 4
        'address where the code bytes will be copied
        Dim addrStub As Long
        'allocate memory to store the code bytes
        addrStub = GlobalAlloc(GMEM_FIXED, nBytes)
        'copy given code bytes
        CopyMemory ByVal addrStub, Bytes(1), nBytes
        CopyMemory ByVal pVFT + OffsetToVFT + index * 4, _
           addrStub, 4    'replace member address with stub address
        'save the handle to the stub for cleanup
        VFArray(index) = addrStub
      End If
   End If
End Sub
'Restores the original addresses in the VFT and discards the
'allocated memory for stub implementations
Private Sub Class_Terminate()
   ReplaceMemberSubWithStub 0, 0
End Sub
'Member procedure can be replaced by a stub by calling
'ReplaceMemberSubWithStub(1,address)
Public Sub PrintMessage(ByVal msg As String)
   Debug.Print "PrintMessage says: " & msg
End Sub
'Procedure called through embedded stub
Private Sub PrintFirstParameter(ByVal msg As String)
  Debug.Print "PrintFirstParameter says: " & msg
End Sub
Sub Main()
  'Phase 3: Embedding native code
  Dim fwdSub As New StubCallToSub
  fwdSub.PrintMessage "Hello!"
  fwdSub.ReplaceMemberSubWithStub 1, AddressOf PrintFirstParameter
  fwdSub.PrintMessage "A stub called me instead!"
  fwdSub.ReplaceMemberSubWithStub 1, 0
  fwdSub.PrintMessage "My address has been restored in VFT!"
End Sub

Please observe in the output which code prints the messages, after each call to the ReplaceMemberSubWithStub method:

PrintMessage says: Hello!
PrintFirstParameter says: A stub called me instead!
PrintMessage says: My address has been restored in VFT!

Additional member procedures (not functions) with different prototypes may be declared in this class and by calling the ReplaceMemberSubWithStub method, you can change their destination. The class is self-cleaning in Class_Terminate, so you only have to exercise caution when choosing the index for the method you're setting the pointer. For larger projects, I would recommend using an Enum that somehow correlates the function pointer names with their prototypes:

Public Enum FwdSubIdx
   idxPrintMessage = 1    'index of second public sub
   idxOtherSub            '(ReplaceMemberSubWithStub is not to be
                          ' replaced)
   idxAnotherSub
End Enum

Compilers Demystified: Function Pointers in Visual Basic 6.0

Returning Values

This is where things get complicated. If you feel like taking a break, this might be a good time to reflect on what has been said and assimilate the ideas presented here. It may look like you already have found a general method to handle function pointers, but the truth is that much more investigation is required. Simply changing the member procedure from the previous example into a member function will not work as required. The return value will be lost, the stack will not be adjusted correctly, and the control flow after the function call will be undefined. The reason behind it is that a member function is called in a different way than a global function. Because global functions can be used as callbacks to API calls, their behaviour is quite well known. For example, if it is supposed to return a Long, you will use the eax register as shown below:

Private Function GlobalFunction(ByVal param As Long) As Long
   GlobalFunction = param
End Function

Becomes:

00402AA0   mov     eax,dword ptr [esp+4]    // GlobalFunction = param
00402AA4   ret     4                        // removes param from
                                            // stack on return

On the other hand, member functions have a different mechanism. They are told by the caller where to copy the return value, and I will show you how. Once again, the delightful/painful process of finding, reading, and understanding the disassembly of native code generated by the VB6 compiler is required:

Public Function MemberFunction(ByVal param As Long) As Long
   MemberFunction = param
End Function

Becomes:

00404230  push     ebp        // 'ebp' register is saved on the
                              // stack below the return address
00404231  mov      ebp,esp    // stack frame established
                              // (new ebp points to old ebp)
00404233  sub      esp,0Ch
00404236  push     4011E6h    // exception handler address
0040423B  mov      eax,fs:[00000000]
00404241  push     eax
00404242  mov      dword ptr fs:[0],esp    // register exception
                                           // handler frame
00404249  sub      esp,0Ch
0040424C  push     ebx
0040424D  push     esi                     // 'esi' register saved
0040424E  push     edi
0040424F  mov      dword ptr [ebp-0Ch],esp
00404252  mov      dword ptr [ebp-8],4011D0h
00404259  xor      esi,esi                 // esi set to zero
0040425B  mov      dword ptr [ebp-4],esi   // local temp0 gets zero value
0040425E  mov      eax,dword ptr [ebp+8]   // gets pointer to object
00404261  push     eax
00404262  mov      ecx,dword ptr [eax]
00404264  call     dword ptr [ecx+4]       // the address called here
                                           // belongs to 'msvbvm60.dll'
00404267  mov      edx,dword ptr [ebp+0Ch] // 'edx' register gets the
                                           // value of param
0040426A  mov      dword ptr [ebp-18h],esi
0040426D  mov      dword ptr [ebp-18h],edx // local temp2 stores
                                           // value of param
00404270  mov      eax,dword ptr [ebp+8]   // gets pointer to object
00404273  push     eax
00404274  mov      ecx,dword ptr [eax]
00404276  call     dword ptr [ecx+8]       // the address called here
                                           // belongs to 'msvbvm60.dll'
00404279  mov      edx,dword ptr [ebp+10h] // given 'return value'
                                           // address is copied in 'edx'!!!
0040427C  mov      eax,dword ptr [ebp-18h] // 'eax' register gets
                                           // value of param from local
                                           // temp2
// param value is copied at given 'return value' address!!!
0040427F  mov      dword ptr [edx],eax
// 'eax' register gets zero value from local temp0
00404281  mov      eax,dword ptr [ebp-4]
00404284  mov      ecx,dword ptr [ebp-14h]
00404287  pop      edi
00404288  pop      esi                     // restores 'esi' register
00404289  mov      dword ptr fs:[0],ecx    // restores previous
                                           // exception handler frame
00404290  pop      ebx
00404291  mov      esp,ebp                 // removes stack frame
00404293  pop      ebp                     // restores 'ebp' register
00404294  ret      0Ch                     // removes 12 bytes from
                                           // the stack on return!!!

You've learned that a member procedure is being passed and extra parameter representing the pointer to the object for which the method is called. Now, you find that a member function is given one more parameter representing the address where the return value needs to be stored. Unfortunately, it is given as the last parameter (first pushed onto the stack, before your defined parameters), This complicates the situation greatly. How can such an implementation of a member function be replaced while handling the return value accurately? See for yourself:

VERSION 1.0 CLASS
Attribute VB_Name = "StubFctCall"
'Replaces address of MemberFunction with given address at which
'should reside a function using
'__stdcall calling convention and accepts 3 parameters of type Long;
'restores the original address
'of MemberFunction when given address is 0
Private Sub ReplaceMemberWithGlobal(ByVal fnAddress As Long)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
   Static oldAddress As Long               'static variable which
   If (oldAddress = 0) Then                'stores the original
                                           'MemberFunction address
      'get MemberFunction address
      CopyMemory oldAddress, ByVal pVFT + OffsetToVFT, 4
   End If
   If (fnAddress = 0) Then
      'restores original MemberFunction address
      CopyMemory ByVal pVFT + OffsetToVFT, oldAddress, 4
   Else
      CopyMemory ByVal pVFT + OffsetToVFT, _
         'replace MemberFunction address with given global address
         fnAddress, 4
   End If
End Sub
'Its address in the VFT will be replaced with given fnAddress after
'the first call, thus becoming inaccessible after its first return
Public Function MemberFunction(ByVal fnAddress As Long) As Long
   Debug.Print Hex$(ObjPtr(Me)) & _
      ".MemberFunction(0x" & Hex$(fnAddress) & ")"
   ReplaceMemberWithGlobal fnAddress
   MemberFunction = fnAddress
End Function
'Function for replacing a member function
Private Function GlobalFunction(ByVal objInstance As Long, _
   ByVal parameter As Long, ByRef retVal As Long) As Long
   Debug.Print Hex$(objInstance) & _
      ".GlobalFunction(0x" & Hex$(parameter) & ")"
   retVal = parameter    'copy return value at given location
   GlobalFunction = 0    'return success
End Function
Sub Main()
   'Phase 4: Returning values
   Dim FwdFct As New StubFctCall
   Dim retVal As Long
   retVal = FwdFct.MemberFunction(AddressOf GlobalFunction)
   Debug.Print "StubFctCall.MemberFunction() returned value: " _
      & Hex$(retVal)
   retVal = FwdFct.MemberFunction(&HB0AB0A)
   Debug.Print "GlobalFunction() returned value: " & Hex$(retVal)
End Sub

The output of the test above shows the correct behaviour on returning the same Long parameter:

1729D0.MemberFunction(0xAB1BD4)
StubFctCall.MemberFunction() returned value: AB1BD4
1729D0.GlobalFunction(0xB0AB0A)
GlobalFunction() returned value: B0AB0A

But, why do you set the eax register to zero (GlobalFunction = 0) before returning? Because there's some sort of validation mechanism after a member function is being called:

   Dim retVal As Long
   retVal = FwdFct.MemberFunction(&HB0AB0A)

Becomes:

// 'edx' register gets address of return value
00402E52  lea    edx,[ebp-0C8h]
00402E58  push   edx         // push address of return value
00402E59  push   0B0AB0Ah    // push parameter
00402E5E  push   eax         // push pointer to object 'FwdFct'
00402E5F  mov    ebx,eax     // save pointer to object
// call first public member of the class
00402E61  call   dword ptr [ecx+1Ch]
// here, 'esi' register is zero!!! success is returned as 0!!!
00402E64  cmp    eax,esi
00402E66  fnclex
// if success returned, jump after next call
00402E68  jge    00402E79
00402E6A  push   1Ch
00402E6C  push   4022A8h
00402E71  push   ebx
00402E72  push   eax
00402E73  call   dword ptr ds:[401024h]
// 'eax' register gets return value
00402E79  mov    eax,dword ptr [ebp-0C8h]
00402E7F  lea    edx,[ebp-94h]
00402E85  lea    ecx,[ebp-24h]
00402E88  push   edx
// retVal variable gets return value
00402E89  mov    dword ptr [ebp-24h],eax

Compilers Demystified: Function Pointers in Visual Basic 6.0

Beyond Essentials

The same behaviour can be extended for return types such as Double, Date, Currency, and so on. An API returning a Currency type uses eax and edx registers; you'll have to copy them at the given address that points to the location where the return value is expected by the caller. Double and Date types are returned through the floating point register. It is important to understand that you need to write different stubs depending on the return type of a function pointer. My plan was to have a generic solution for procedures, functions that return 32-bit & 64-bit types, and functions that return quad-words through the floating point register. Strings are returned by reference; this means they can be handled as a 32-bit pointer, returned in eax register. For procedures that do not return anything, the forwarding stub already presented can be used safely, but for functions you need to write different stubs that copy one or two register values, or the floating point register, at the given location where the caller expects the return value.

Moreover, because an extra, last parameter is given by the caller, your stubs also should remove the pointer to the return value, to properly adjust the stack pointer on return to the caller. It is obvious that such a stub implementation cannot simply jump to the forwarding address as you were doing for procedure calls forwarding. Instead, the forwarding address must be called, to return back to your stub and not to the original caller. When the forwarding call is made, the return address to your stub is pushed on the stack, and that must happen immediately after the parameters expected by the function you're calling. That means that you have to remove the address of the VB caller from the stack and save it at some safe location where it will be available after the forwarding call. One solution I found was to save it at the location given by the caller for the return value. That's feasible, but it involves changing the stub implementation for each prototype of the functions you're forwarding calls to. That means that you'll have to provide the size in bytes of all parameters that the function takes to the piece of code that replaces the address of a method in the VFT. Hmm, not an easy task to do...

Luckily, there is another way! If you're not already acquainted with it, let me introduce you to the 'Thread Information Block' (TIB). The format of the TIB structure can be found in many online resources (for example, "Under The Hood - MSJ, May 1996" by Matt Pietrek), and won't be described here. For your benefit, the TIB structure holds an entry, pvArbitrary, that is available for application use. Very few applications make use of this location, thus not affecting other components by overwriting their data. Because the TIB structure is available on a per-thread basis, your implementation will be thread safe. Is it enough to store the return address in pvArbitrary of the TIB? I'm afraid the answer is no. It will fail in case of re-entrant calls, or when any stub replaces the return address of another stub. A common scenario is that an API is called through your forwarding technique, being passed a callback that also calls an API through a stub. How do you make two imbricated calls on the same thread not overwrite their return address? You create a linked list that acts as a LIFO (last in first out) stack. The pvArbitrary will always point to the head of the list, which stores the return address of the last call. Such a linked list requires allocation and deletion, for which I have chosen the GlobalAlloc and GlobalFree APIs from kernel32.dll because they are always available. The resulting assembly can be used to form stubs handling Double/Date (64-bit floating point register), Currency (eax and edx registers) and Long/Integer/Boolean/ByVal String (eax register) returns is explained below:

   push        8      // we need 8 bytes allocated
   push        0      // we need fixed memory (GMEM_FIXED)
// replace here XXXXXXXX with the address of GlobalAlloc
   mov         eax,XXXXXXXXh
   call        eax    // allocate new list node
   pop         ecx    // remove return address from stack
// store return address in list node
   mov         dword ptr [eax],ecx
   pop         ecx    // remove pointer to object from stack
// get pointer to TIB structure
   mov         ecx,dword ptr fs:[18h]
// get pointer to previous list node from pvArbitrary
   mov         edx,dword ptr [ecx+14h]
   mov         dword ptr [eax+4],edx     // link list nodes
// store new head of list at pvArbitrary
   mov         dword ptr [ecx+14h],eax
// XXXXXXXX can be replaced here with any forwarding address
   mov         eax,XXXXXXXXh
   call        eax    // call the forwarding address
// get the location where return value is expected by VB caller
   pop         ecx
#ifdef _RETURN64_DOUBLE_          // return 64-bit floating point value
   fstp        qword ptr [ecx]    // return value gets double result
#else
// copy first 32-bit of the return value
   mov         dword ptr [ecx],eax
#ifdef _RETURN64_    // return 64-bit value
// copy second 32-bit of the return value
   mov         dword ptr [ecx+4],edx
#endif
#endif
// get pointer to TIB structure
   mov         ecx,dword ptr fs:[18h]
// get pointer to head of list from pvArbitrary
   mov         eax,dword ptr [ecx+14h]
// get return address from list node
   mov         edx,dword ptr [eax]
   push        edx    // restore return address onto stack
// get pointer to previous list node
   mov         edx,dword ptr [eax+4]
// store pointer to previous node at pvArbitrary
   mov         dword ptr [ecx+14h],edx
   push        eax    // we need to free the list node
// replace here XXXXXXXX with the address of GlobalFree
   mov         eax,XXXXXXXXh
   call        eax    // free list node
   ret                // return to VB caller

What happened with the validation mechanism after a member function is being called? Before returning to the VB caller, the eax register needs to be set to zero. The documentation of the GlobalFree API says that it returns zero when the function succeeds. I am counting on the successful discard of the allocated node, as I'm counting on the successful allocation of it. If you prefer, insert a 'xor eax,eax' (native code is: "33C0") above the ret instruction. Any other improvements to this stub are left as an exercise to the reader. Stub implementations can be written to handle even __stdcall to __cdecl forwarding. The world of possibilities is endless. The native code produced from the above assembly can be split into three parts, to ease the formation of a stub handling the required return type just by concatenating strings:

VERSION 1.0 CLASS
Attribute VB_Name = "StubFwdWithStackOnTIB"
Private Const hexStub4FunctionProlog = "6A08" & "6A00" & _
   "B8XXXXXXXX" & "FFD0" & "59" & "8908" & _
   "59" & "648B0D18000000" & "8B5114" & "895004" &_
   "894114" & "B8XXXXXXXX" & "FFD0" & "59"
'The hex string bellow represents the code bytes compiled from a
'short assembly described as follows:
'   fstp        qword ptr [ecx]    // return value gets double result
Private Const hexStub4ReturnDbl = "DD19"
'The hex string bellow represents the code bytes compiled from a
'short assembly described as follows:
// copy first 32-bit of the return value
'   mov         dword ptr [ecx],eax
Private Const hexStub4Return32bit = "8901"
'The hex string bellow represents the code bytes compiled from a
'short assembly described as follows:
// copy first 32-bit of the return value
'   mov         dword ptr [ecx],eax
// copy second 32-bit of the return value
'   mov         dword ptr [ecx+4],edx
Private Const hexStub4Return64bit = "8901" & "895104"
Private Const hexStub4FunctionEpilog = "648B0D18000000" & _
   "8B4114" & "8B10" & "52" & "8B5004" & _
   "895114" & "50" & "B8XXXXXXXX" & "FFD0" & "C3"
Private Enum StubTypes    'supported stub types
   ret0bit    'method is a procedure and does not return a value
'method is a function that returns 32-bit type
'(String included, since returned ByRef)
   ret32bit
'method is a function that returns 64-bit type (ex. Currency)
   ret64bit
'method is a function that returns 64-bit float type (ex. Double, Date)
   retDbl
End Enum
'An array that saves the original addresses to the member procedures
Private VFTable() As Long
'An array that saves addresses of allocations made for stubs,
'in order to be freed
Private VFArray() As Long
'address of GlobalAlloc from kernel32.dll
Private pGlobalAlloc As Long
'address of GlobalFree from kernel32.dll
Private pGlobalFree As Long
'Sets initial state of the private arrays, thus UBound will not
'fail on first call;
'also, obtains the addresses of GlobalAlloc and GlobalFree used for
'the linked list stored at pvArbitrary entry in the TIB
Private Sub Class_Initialize()
   ReDim VFTable(0)
   VFTable(0) = 0
   ReDim VFArray(0)
   VFArray(0) = 0
   Dim hKernel32 As Long 'handle to kernel32.dll
   hKernel32 = LoadLibrary("kernel32.dll")
   pGlobalAlloc = GetProcAddress(hKernel32, "GlobalAlloc")
   pGlobalFree = GetProcAddress(hKernel32, "GlobalFree")
End Sub
'Restores the original addresses in the VFT and discards the
'allocated memory for stub implementations
Private Sub Class_Terminate()
   ReplaceMethodWithStub 0, 0, 0
End Sub
'Removes only existing stub for method specified by index
Private Sub RemoveStub(ByVal index As VFTidxs)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
   If (index < 1) Then Exit Sub
   If (index > UBound(VFTable)) Then Exit Sub
   If (VFTable(index) <> 0) Then    'stub exists for this member
      Dim oldAddress As Long
      oldAddress = VFTable(index)
'restore original member address
      CopyMemory ByVal pVFT + OffsetToVFT + index * 4, oldAddress, 4
      VFTable(index) = 0
'discard the allocated memory for stub implementation
      GlobalFree VFArray(index)
      VFArray(index) = 0
   End If
End Sub
'Replaces/restores the address of a method of this class
'If given index is 0 then all original addresses are restored in
'the VFT
Private Sub ReplaceMethodWithStub(ByVal index As VFTidxs, _
   ByVal fnType As StubTypes, ByVal fnAddress As Long)
   Dim pVFT As Long
   CopyMemory pVFT, ByVal ObjPtr(Me), 4    'get the VFT address
'restore all the original addresses
   If (index < 1) Then
      For index = 1 To UBound(VFTable)
         RemoveStub index
      Next index
   Else
'restore only the address for the index specified
      If (fnAddress = 0) Then
         RemoveStub index
      Else    'replace the address of a member specified by index
         If (index > UBound(VFTable)) Then
'resize the array to save original addresses
            ReDim Preserve VFTable(index)
            VFTable(index) = 0
'resize the array to save changes in the VFT
            ReDim Preserve VFArray(index)
            VFArray(index) = 0
         End If
         'check if a stub exists for this member and needs to be
         'removed first
         RemoveStub index
         Dim oldAddress As Long
         'get original member address
         CopyMemory oldAddress, ByVal pVFT + OffsetToVFT + _
            index * 4, 4
         VFTable(index) = oldAddress
         Dim hexCode As String
         Select Case fnType
         'method is a function that returns 64-bit float type
         Case StubTypes.retDbl
            hexCode = hexStub4FunctionProlog & hexStub4ReturnDbl & _
            hexStub4FunctionEpilog
         'method is a function that returns 64-bit type
         Case StubTypes.ret64bit
            hexCode = hexStub4FunctionProlog & hexStub4Return64bit & _
            hexStub4FunctionEpilog
         'method is a function that returns 32-bit type
         Case StubTypes.ret32bit
            hexCode = hexStub4FunctionProlog & hexStub4Return32bit & _
            hexStub4FunctionEpilog
         'method is a procedure and does not return a value
         '(default:StubTypes.ret0bit)
         Case Else
            hexCode = hexStub4ProcedureCall
         End Select
         Dim nBytes As Long     'number of code bytes to allocate
         nBytes = Len(hexCode) \ 2
         Dim Bytes() As Byte    'array of code bytes converted from hex
         ReDim Preserve Bytes(1 To nBytes)
         Dim idx As Long        'loop counter
         'convert each pair of hex chars to a byte code
         For idx = 1 To nBytes
            Bytes(idx) = Val("&H" & Mid$(hexCode, idx * 2 - 1, 2))
         Next idx
         'method is a procedure and does not return a value
         If (fnType = ret0bit) Then
            'replace the forwarding address in the native code
            CopyMemory Bytes(5), fnAddress, 4
         'method is a function returning a 32-bit, 64-bit or
         '64-bit float type
         Else
            'replace the address of GlobalAlloc
            CopyMemory Bytes(6), pGlobalAlloc, 4
            'replace the forwarding address in the native code
            CopyMemory Bytes(33), fnAddress, 4
            'replace the address of GlobalFree
            CopyMemory Bytes(nBytes - 6), pGlobalFree, 4
         End If
         'address where the code bytes will be copied
         Dim addrStub As Long
         'allocate memory to store the code bytes
         addrStub = GlobalAlloc(GMEM_FIXED, nBytes)
         'copy given code bytes
         CopyMemory ByVal addrStub, Bytes(1), nBytes
         CopyMemory ByVal pVFT + OffsetToVFT + index * 4, _
            addrStub, 4    'replace member address with stub address
         'save the handle to the stub for cleanup
         VFArray(index) = addrStub
      End If
   End If
End Sub

The sample project provided shows a class implementation that tests all the discussed return types, as well as a callback that uses call forwarding, to ensure that your return addresses are not being replaced when saved as a linked list stored in the pvArbitrary entry of the TIB.

Compilers Demystified: Function Pointers in Visual Basic 6.0

Spreading Thin with Typeless Forwarding

Although C++ has 'typedef' for prototype definition of function pointers, '__asm' blocks, intrinsic '__asm __emit' and '__declspec(naked)' function declaration specification, the best I could write in VB6 is the class described in this last section, which I believe can be used to achieve about everything you could with C++. For those who are too confident when writing code, type checking of parameters passed to a function pointer can be utterly removed. In most situations, a developer will know at compile time a prototype of a function for which it has a pointer. Very few applications would need to construct a call at runtime, with any number of parameters, of any known types. Some years ago, I was looking for an application that can read some sort of script where I can define how to test some DLLs I have developed. It would've helped regression testing, and could've been easily maintained by a non-developer tester.

Such an application can be written as only one VB6 module, with the help of the TypelessFwd class provided in the sample project. I do not recommend using typeless forwarding when you know the prototype of the function at compile time. This technique merely helps you to understand function calls and parameter passing. They should be rarely used and with advised caution, as when writing assembly. Because it splits apart the parameters pushing and the call itself, there'll be no correlation that the compiler can test or understand. If you can live with that and assume the full responsibility of checking the types of parameters expected by a function called through a pointer, then its maximum flexibility will be the reward:

'Phase 6: Spreading thin with typeless forwarding
Dim pFn As New TypelessFwd
Dim sqValue As Double
sqValue = 5
'Function SquareRoot(ByVal value As Double) As Double
Dim hVBVM As Long, pSquareRoot As Long
'already loaded msvbvm60
pSquareRoot = pFn.AddressOfHelper(hVBVM, "msvbvm60.dll", "rtcSqr")
pFn.ByRefPush VarPtr(sqValue) + 4
pFn.ByRefPush VarPtr(sqValue)
Debug.Print "Sqr(" & sqValue & ") = " & pFn.CallDblReturn(pSquareRoot)
'unload runtime module, if required
pFn.AddressOfHelper hVBVM, vbNullString, vbNullString
Dim interval As String, number As Double, later As Date
interval = "h"
number = 10
'Function DateFromNow(ByVal interval As String,
'                     ByRef number As Double) As Date
pFn.ByValPush VarPtr(number)
pFn.ByValPush StrPtr(interval)
later = pFn.CallDblReturn(AddressOf DateFromNow)
Debug.Print "In " & number & " " & interval & " _
   will be: " & later
Dim sSentence As String
sSentence = "The third character in this sentence is:"
'Function SubString(ByRef sFrom As String, ByVal start As Long,
'                   ByVal length As Long) As String
pFn.ByValPush 1
pFn.ByValPush 3
pFn.ByValPush VarPtr(sSentence)
Dim retVal As String
Debug.Print sSentence & " '" & _
   pFn.CallStrReturn(AddressOf SubString) & "'."
Dim sCpuVendorID As String
'pre-allocate 12 bytes (don't use String * 12)
sCpuVendorID = Space$(12)
'Sub Get_CPU_VendorID(ByVal sCpuVendorID As String)
Dim pGet_CPU_VendorID As Long
'allocate memory storing native code
pFn.NativeCodeHelper pGet_CPU_VendorID, hexGet_CPU_VendorID
pFn.ByValPush StrPtr(sCpuVendorID)
pFn.Call0bitReturn pGet_CPU_VendorID
'free memory storing native code
pFn.NativeCodeHelper pGet_CPU_VendorID, vbNullString
Debug.Print "CPU Vendor ID: " & sCpuVendorID & "."

The class provides two helper methods for obtaining function pointers. AddressOfHelper can be used to load and unload DLLs at runtime and retrieve pointers to exported functions by name or ordinal, as with the GetProcAddress API. NativeCodeHelper will allocate and free memory storing native code given as hex strings. Whenever you discover a bottleneck in your VB code, write some assembly that does it faster, and embed the native code through a hex string. One remark should be mentioned about embedded native code: The edi, esi and ebx registers values should be preserved because it seems that VB makes use of one of them for validating the return value. The sample project shows how to retrieve some CPU information (using cpuid instruction) by calling embedded native code.

Two other methods of this class will help you push parameters onto the stack, before calling a pointer to a function. ByValPush takes a Long value (32-bit), and leaves it onto the stack as it is given. When a parameter to a function is passed ByRef, the pointer to the value should be pushed onto the stack. For example, 'ByRef param As Double' means the address of the double variable should be pushed onto the stack before the call; this can be achieved by using 'ByValPush VarPtr(param)'. It is like saying "push the 32-bit value of the address to double parameter". How do you pass a Double type ByVal instead of ByRef? Use the ByRefPush method to dereference any address, by pushing the 32-bit value it points to onto the stack. Because Double is a 64-bit type, you must call ByRefPush twice. First, you'll push the value of the last 32-bit of the Double and then push the first 32-bit, because parameters and their content get pushed from right to left. The reason, for those who didn't understand already, is that the esp register (stack pointer) decreases 4-bytes after a push instruction (stack grows downwards).

Don't be confused by the naming of these methods because they shouldn't be associated with the ByVal/ByRef declaration of parameters in a function prototype. Calling 'ByRefPush VarPtr(param) + 4' is like saying "add 4 bytes to the address of double parameter, and use the resulting address to read a 32-bit value that must be pushed onto stack". Usually, custom types are passed by reference, but even when passed by value, the same policy can be applied, while considering their size and byte-alignment. Pushing each 32-bit value of a structure, giving its address to ByRefPush method, will achieve a ByVal parameter passing of a custom type. The String type directly maps a BSTR (an Automation type representing a basic string commonly used through COM) and you can get both the pointer to the encapsulated buffer, as well as the pointer to the object. When a parameter of type String is defined ByVal, it means the function needs the pointer to the actual buffer, which can be retrieved with StrPtr. When the String parameter is defined ByRef, it means the function needs the pointer to the object (double pointer to the buffer), which can be retrieved with VarPtr. In both cases, use ByValPush with the appropriate address. You can find in my tests calls that receive strings and doubles passed both as ByVal and ByRef.

The other five methods of the class, being replaced with stubs as the previous two, have the purpuse of making a call to a given address, while handling different types of return values: none (procedure with no return value), 32-bit value, pointer to string (32-bit address), 64-bit value (for example, Currency) and double (64-bit floating point value). Please note here that the pointer to a string return is specifically declared (Long cannot be used instead), because VB must see the return as 'ByVal String'. The assignment operator copying a Long into a String would result in a textual number representing the address of the buffer, and not the content of the buffer. Looking at the assembly of the stubs used for these methods, you'll observe that your list kept at pvArbitrary in the TIB structure stores also the location of the return value, because in the moment of the call, it is pushed onto stack after the parameters for the forwarding call, thus must be removed and saved, as the return address to the VB caller.

Conclusions

Before going through the sample code and trying to adapt it to your needs, let me summarize the topics discussed:

  • Public methods of a class are called through jump tables, and the corresponding addresses are stored in a 'Virtual Function Table' (VFT). The pointer to the vftable is stored as first 4 bytes (hidden member data) within each instance of the class.
  • The compiler will generate accessors and modifiers for public variables (member data) of a class and their addresses will be inserted in the VFT. For classes having no public member data, the VFT will contain addresses of our defined methods starting with offset &H1C (28 bytes), in the order of their declaration.
  • Replacing the addresses in the VFT will redirect calls to whatever code you need, and, as long as the prototype and calling convention aren't changed, the call will return successfully.
  • Each vftable is shared by all instances of the same class; therefore, changing it for one object means it will affect even objects created later during the lifetime of that code section (until the corresponding VB DLL is unloaded, respectively the entire process that resides in the main executable).
  • Member procedures are given an extra parameter (pushed last onto the stack) representing the pointer to the object for which you make the call.
  • Member functions are given two extra parameters: the object pointer (as first parameter, pushed last onto stack) and the location where the return value is expected by the caller (as last parameter, pushed first onto the stack).
  • There'll be no string conversion or other type conversion for parameters because it is done for calls to functions defined using the Declare statement. The same can be assumed for return types. Strings passed or returned by value can be handled as a 32-bit pointer to the encapsulated buffer, as known from BSTR type description.
  • Native code easily can be embedded by converting an equivalent hex string into a fixed memory buffer dynamically allocated with GlobalAlloc. Embedding native code, stub functions can replace methods of a class, and these can forward your calls while adjusting the stack as expected and handling the return value, depending on its type. Not shown here, but it is even possible to convert calls made with then__stdcall calling convention and received by functions written as __cdecl calling convention.
  • When these stubs need control after the forwarding call returns, they must temporarily store the return address of the VB caller. Different designs also may need to store the pointer to the return value or, not shown here, even some context registers. To be thread-safe and re-entrant capable, these can be saved in a linked list dynamically managed as a LIFO stack, and the pointer to its head node can be copied into the pvArbitrary member of the 'Thread Information Block' (TIB) structure.
  • VB generates some validation code testing eax register against one of these registers: edi, esi, ebx. If the embedded native code is using these registers, they must be saved and eax set to zero before returning.

I hope that you didn't find this too obscure, Thank you very much for reading and I hope that it will prove useful in your future implementations. Any kind of feedback would be greatly appreciated.

Revision History

  • 06-06-2007: Original version.
    Known issue: Due to the lack of time available, an unresolved issue noticed in the sample code was left for further investigation. I can describe the behaviour and the reason, such that you'll be aware of the problem. Running the code in VB's IDE will not report any problem because first-chance exceptions are not displayed to the user. The heap manager reports that the same invalid address was given to RtlSizeHeap and RtlFreeHeap. It seems that it happens when a method returning a String type has been replaced by a function that does not return a discardable BSTR. It all works perfectly if the method is replaced by another VB implementation, but by testing GetCommandLine API that returns a const buffer and not a discardable BSTR, VB has tried to delete the returned memory; thus, the heap manager complains about being an invalid address. A workaround can be used for APIs returning constant string buffers. Declare them as returning a Long, and copy the memory from the returned pointer until the null-terminating character.


About the Author

Bogdan Apostol

Though he did not own a computer until 2002, Bogdan's passion about software development sparked for the first time in 1990, when he understood that computer science can help in saving lives, as well as improve quality everywhere. Thus, he keeps in mind that creating software is for people, not computers. He finds programming as the best mixture of art with science and his favorite instrument for such "music" remains unmanaged C++. He studied computer science and applied mathematics in Bucharest, where he also worked for about 6 years. Since 2001 he left his hometown, hoping to learn from experiencing one of the most developing countries in the world, UAE. He's always eager to help and share, having the most satisfaction when feeling himself useful. He has many other hobbies which, some get him inspired in his profession and some, at least, balance and diversify his experience.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • Mobile is introducing sweeping changes throughout your workplace. As a senior stakeholder driving mobile initiatives in your organization, you may be lost in a sea of technologies and claims from vendors promising rapid delivery of applications to your employees, customers, and partners. To help explain some of the topics you will need to be aware of, and to separate the must-haves from the nice-to-haves, this reference guide can help you with applying a mobile strategy in the context of application …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds