Visual Basic

Yield Real Pointers or Addresses

Visual Basic 6 (VBA) is also able to yield real pointers, or addresses. Three undocumented VBA methods-VarPtr, ObjPtr, and StrPtr (which are just three different VBA type library aliases pointing to the same entry point in the run-time DLL)-are used to create these pointers. You can turn an object into a pointer value using l = ObjPtr(o), where o is the object whose address you want and l is a long integer in which the address of the object is put. Just resolving an object's address doesn't AddRef the object, however. You can pass this value around and get back to the object by memory copying l into a dummy object variable and then setting another object variable to this dummy (thus adding a reference to the underlying object).

Call CopyMemory(oDummy, l, 4)
  Set oThing = oDummy

CopyMemory should be defined like this:

Private Declare Sub CopyMemory Lib "kernel32" _
      Alias "RtlMoveMemory" (pDest As Any, pSource As Any, _
      ByVal ByteLen As Long)

The really neat thing here is that setting l doesn't add a reference to the object referenced by the argument of ObjPtr. Normally, when you set an object variable to point to an object, the object to which you point it (attach it, really) has its reference count incremented, meaning that the object can't be destroyed, because there are now two references to it. (This incrementing also happens if you pass the object as a parameter to a routine.) For an example of how this can hinder your cleanup of objects, see the discussion of the linked list example.

By using VarPtr (which yields the address of variables and UDTs), StrPtr (which yields the address of strings), and ObjPtr, you can create very real and very powerful and complex data structures.

Here's the short piece of code I used to discover that VarPtr, ObjPtr, and StrPtr are all pretty much the same thing (that is, the same function in a DLL):

Private Sub Form_Load()
      ' VB code to dump or match an external
      ' server method with a DLL entry point. Here it's
      ' used to dump the methods of the "_HiddenModule".
      ' Add a reference to 'TypeLib Information' (TLBINF32.DLL),
      ' which gives you TLI before running this code.
      Dim tTLInfo  As TypeLibInfo
      Dim tMemInfo As MemberInfo
      Dim sDLL     As String
      Dim sOrdinal As Integer
      Set tTLInfo = _
          TLI.TLIApplication.TypeLibInfoFromFile("MSVBVM50.DLL")
      For Each tMemInfo In _
          tTLInfo.TypeInfos.NamedItem("_HiddenModule").Members
          With tMemInfo
              tMemInfo.GetDllEntry sDLL, "", sOrdinal
              ' labDump is the label on the form where the
              ' output will be printed.
              labDump.Caption = labDump.Caption & _
                      .Name & _
                      " is in " & _
                      sDLL & _
                      " at ordinal reference " & sOrdinal & _
                      vbCrLf
          End With
      Next
  End Sub

The code uses TLBINF32.DLL, which can interrogate type libraries (very handy). Here I'm dumping some information on all the methods of a module (in type library parlance) named _HiddenModule. You'll see that this is the module that contains VarPtr, ObjPtr, and StrPtr, which you can discover using OLEVIEW.EXE to view MSVBVM60.DLL:

module _HiddenModule {
          [entry(0x60000000), vararg, helpcontext(0x000f6c9d)]
          VARIANT _stdcall Array([in] SAFEARRAY(VARIANT)* ArgList);
          [entry(0x60000001), helpcontext(0x000f735f)]
          BSTR _stdcall _B_str_InputB(
                          [in] long Number,
                          [in] short FileNumber);
          [entry(0x60000002), helpcontext(0x000f735f)]
          VARIANT _stdcall _B_var_InputB(
                          [in] long Number,
                          [in] short FileNumber);
          [entry(0x60000003), helpcontext(0x000f735f)]
          BSTR _stdcall _B_str_Input(
                          [in] long Number,
                          [in] short FileNumber);
          [entry(0x60000004), helpcontext(0x000f735f)]
          VARIANT _stdcall _B_var_Input(
                          [in] long Number,
                          [in] short FileNumber);
          [entry(0x60000005), helpcontext(0x000f65a4)]
          void _stdcall Width(
                          [in] short FileNumber,
                          [in] short Width);
          [entry(0x60000006), hidden]
          long _stdcall VarPtr([in] void* Ptr);
          [entry(0x60000007), hidden]
          long _stdcall StrPtr([in] BSTR Ptr);
          [entry(0x60000008), hidden]
          long _stdcall ObjPtr([in] IUnknown* Ptr);
      };

When you run the Visual Basic code, you'll see this output:

Label1Array   is in VBA5.DLL at ordinal reference 601
  _B_str_InputB is in VBA5.DLL at ordinal reference 566
  _B_var_InputB is in VBA5.DLL at ordinal reference 567
  _B_str_Input  is in VBA5.DLL at ordinal reference 620
  _B_var_Input  is in VBA5.DLL at ordinal reference 621
  Width         is in VBA5.DLL at ordinal reference 565
  VarPtr        is in VBA5.DLL at ordinal reference 644
  StrPtr        is in VBA5.DLL at ordinal reference 644
  ObjPtr        is in VBA5.DLL at ordinal reference 644

This output shows the method name together with the DLL and ordinal reference (into the DLL) that implements its functionality. If you use DUMPBIN /EXPORTS on MSVBVM60.DLL like this:

dumpbin /exports msvbvm60.dll > dump

and then examine the dump file, you'll see that the routine at ordinal 644 is in fact VarPtr. In other words, VarPtr, ObjPtr, and StrPtr all do their stuff in the MSVBVM60.DLL routine VarPtr!

Matching the code output to the dump, we see this:

Method Name     DLL Routine Name
  Label1Array     rtcArray
  _B_str_InputB   rtcInputCount
  _B_var_InputB   rtcInputCountVar
  _B_str_Input    rtcInputCharCount
  _B_var_Input    rtcInputCharCountVar
  Width           rtcFileWidth
  VarPtr          VarPtr
  StrPtr          VarPtr
  ObjPtr          VarPtr

I haven't explained what the other routines do-you can discover that for yourself.

Stuff About Type Libraries

In this section, we'll take a quick look at type libraries-not those created by Visual Basic (because they're free) but those created by hand. You'll see how to use these handmade type libraries as development tools that will help you ensure that your coding standards are correctly applied.

A type library is where Visual Basic records the description of your ActiveX server's interfaces. Put another way, a type library is a file, or perhaps part of a file, that describes the type of one or more objects. (These objects don't have to be ActiveX servers.) Type libraries do not, however, store the actual objects described-they store only information about objects. (They might also contain immediate data such as constant values.) By accessing the type library, applications can check the characteristics of an object-that is, the object's exported and named interfaces.

When ActiveX objects are exported and made public in your applications, Visual Basic creates a type library for you to describe the object's interfaces. You can also create type libraries separately using the tools found on the Visual Basic 6 CD in \TOOLS\VB\UNSUPPRT\TYPLIB.

Type libraries are usually written using a language called Object Description Language (ODL) and are compiled using MKTYPLIB.EXE. A good way to learn a little more about ODL is to study existing type libraries. You can use the OLEVIEW.EXE tool mentioned earlier to disassemble type libraries from existing DLLs, ActiveX servers, and ActiveX controls for further study.

As I just said, the information described by a type library doesn't necessarily have anything to do with ActiveX. Here are a couple of handy examples to show how you might use type libraries.