I am almost embarassed to mention it, but in a past life I developed several major corporate applications using VB6. The applications are remarkably stable and have only been recompiled three or four times since their original deployment for various fixes or changes. This particular application has been in use for 10 years (since 1997) and uses Crystal Reports 5.0 for printing. Back in the day, I read Visual Basic Programmer's Journal (VBPJ) quite a bit and saw an article about using the Win32 API for Crystal Reports in VB. Since I was more of a C\C++ programmer thrust into the VB programmer role, I instantly liked this article and used these methods in my application. At the time I was not familiar with Hardcode Visual Basic (or here) by Bruce McKinney so this code used a common hack when copying memory into VB:

' Slightly modified code. Original code from VBPJ.
' ... Removed extra code for sample ...

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)

Private Type DevModeBuffer ' Holds DevMode Data.
    sData As String * 4096
End Type

Private m_HoldDevMode   As DevMode
Private m_DModeBuffer   As DevModeBuffer
Private m_lDModeSize    As Long

Public Function SetupPrinter(lFlags As Long) As Boolean
    ' ... Printer setup ...

    ' Get size of DevMode & DevNames from PrintDlg return
    m_lDModeSize = GlobalSize(PDlg.hDevMode)

    ' Fill Buffers with Space to Perpare for copy to.
    m_DModeBuffer.sData = String$(4095, 32)

    ' Lock memory & copy the standard size DevMode.
    lDModePointer = GlobalLock(PDlg.hDevMode)  ' Locks the memory
    Call CopyMemory(m_HoldDevMode, ByVal lDModePointer, Len(m_HoldDevMode))

    ' Copy entire DevMode Structure for later use.
    Call CopyMemory(m_DModeBuffer, ByVal lDModePointer, m_lDModeSize) ' Copies the memory

    ' ... More printer setup ...
End Function

The key to this code is the String * 4096 and the String$(4095, 32) which initializes the buffer. Prior to Hardcode VB, it wasn't really possible to have arbitrary sized buffers because VB passed the String parameter (BSTR) differently to API functions. So people used a fixed buffer size (in this case 4096) in a Type (structure) and VB passed the parameter as a pointer-to-memory, not a pointer-to-a-pointer-to-memory.

After a little bit of debugging, it quickly becomes apparent that this new printer returns a much larger memory block (6952 bytes) and the CopyMemory statement happily copies the entire block regardless of the allocated size. Fortunately, the change was fairly simple.

With the advent of Hardcore VB and the Win.TLB, it was possible to reference DLL functions in a manner that wasn't previously possible by altering the data type and ByVal/ByRef settings. The Win.TLB has a function CopyMemoryLpToStr() so the code essentially becomes:

' Remove the * 4096 from DevModeBuffer.sData

Public Function SetupPrinter(lFlags As Long) As Boolean
    ' ...
    ' Fill Buffers with Space to Perpare for copy to.
    m_DModeBuffer.sData = String$(m_lDModeSize + 1, 32)

    ' Copy entire DevMode Structure for later use.
    Call CopyMemoryLpToStr(m_DModeBuffer.sData, ByVal lDModePointer, m_lDModeSize) ' Copies the memory
    ' ...
End Function

Well I hope this is the last time I have to touch this application. It has been remarkably stable even though I have moved on to different departments several times and no one is actively maintaining it.

Technorati tags: