Using Strings

When Visual Basic passes a string by reference to a C-language DLL, it uses a special OLE 2 data type called a BSTR. OLE Automation allows BSTR strings to be allocated and freed by any component that supports this data type. In Excel 97, BSTRs now contain UNICODE strings. This is a change from Excel 95 and earlier, so be careful when porting your code.

In most cases, a BSTR can be treated like a pointer to a null-terminated string. In general, it's best if your C-language code does not directly manipulate the string data. You can de-reference the pointer to copy data from the BSTR, however.

Instead of directly manipulating BSTR data, OLE provides several functions that should be used to allocate, free, and reallocate BSTR values. These functions are listed in Chapter 6 in Volume 2 of the OLE 2 Programmer's Reference. Microsoft Excel still passes all strings as ANSI, even though string APIs under Win32 are typically UNICODE. This can cause problems when dealing with the BSTRs that Microsoft Excel passes to your DLL. The OLE functions for dealing with BSTR values under Win32 assume the BSTRs contain UNICODE strings. There are a few functions that will deal with byte-oriented strings, and you should make sure to use these functions. For example, instead of using SysStringLen() on a string from Excel, you should use SysStringByteLen().

When you pass a string by reference, your C-language function should declare the argument as a pointer to a BSTR. The pointer will never be NULL, but if the Visual Basic string is unassigned (that is, created with the Dim statement but not assigned a value), the BSTR pointed to will be NULL. If the string is assigned but empty, the first character will be a null character and the string length will be zero.

The pointer may also reference a NULL pointer if the original variable was created as a variant but not assigned. Visual Basic would coerce the variant to a string when it called the DLL; but because the variant is empty, it behaves like a declared — but unassigned — string.

The following code example tests for these conditions:

short WINAPI SType(BSTR *pbstr)
{
    if (pbstr == NULL)     // pointer is null; will never happen
        return 1;
    if (*pbstr == NULL)    // string (or variant) is alloc by VB 
                        // with Dim statement,
                        // but not yet assigned
        return 2;
    if (*pbstr[0] == 0)    // string is allocated 
                        // and assigned to empty string ("")
        return 3;
    // string has a value; this value can be accessed at *pbstr
    return 4;
}

This Visual Basic code declares and calls the SType function:

Declare Function SType Lib "debug\ADVDLL.DLL" _
    (s As String) As Integer

Sub STypeTest()
    Dim s As String

    MsgBox SType(s) 'displays 2
    s = ""
    MsgBox SType(s) 'displays 3
    s = "test"
    MsgBox SType(s) 'displays 4
End Sub