by Jim Karabatsos - Professional Training Institute
When passing string data between a VB program and a dynamic link library (DLL), we need to take into account the fact that most DLLs are written for use by C programs (especially in the case of the various Windows components like Kernel, User and GDI).
Whenever a string needs to be sent to the DLL, the calling program allocates the string in its own memory space and passes the address of the string (in the form of a 32-bit selector/offset pair) to the DLL. The DLL is then able to reference this string data because it is passed the starting address and looks for a terminating binary zero byte (called a null byte) as signifying the end.
When a string needs to be returned from the DLL, however, it is generally not a good idea to have the DLL allocate the string in its data segment and pass that address back. This is because the DLL may be used by more than one task and so the memory might be overwritten if the DLL is called by another task before it is used by the first program. The usual convention, therefore, is to require that the calling program pass the address of the buffer as a parameter and for the DLL to place the returned string in that buffer. In this way, the data buffer belongs to the task and is not shared.
Implicit in this scheme is the anatomy of a C string. In C, strings are very simple structures — essentially, a C string is an array of characters, beginning at a defined address and ending when the first null (or binary zero) byte is encountered. This structure allows for very long strings (theoretically unlimited, although the Intel architecture does impose a practical limit of 64K bytes) with very little overhead. It also means that a C string cannot contain null characters, and that determining the length of the string requires scanning the entire length of the string for a null character. Perhaps not so obviously, it also means that the maximum length of the string cannot be determined by examining the string itself; any program has to know how much room was allocated in the first instance and ensure that no data is written past the end of that space to ensure no data corruption or GPFs occur.
Whenever a string needs to be returned from a DLL to our VB program, we need to allocate a string large enough to hold the return value, pass the string ByVal (causing the VB string to be copies to a C string and passed), then scan through the string to find the null byte and set the string length appropriately. In other word, we create this sort of contorted code :
X$ = String$(256,0) Call SomeSub(ByVal X$) X$ = Left$(X$,Instr(X$,Chr$(0))-1)
What I would really like is to do this :
X$ = SomeFunction$()
where SomeSub and SomeFunction are in a DLL. I think you would agree that the latter syntax is much more in keeping with the style of string handling in Basic.
From the very early days of Basic, string handling has been one of its fortés, and VB carries on with that heritage. (As an aside, I truly believe that the reason most Basic programmers find that C and Pascal require so much work is the fundamental shift from strings being a built-in and natural data type that takes part in expressions to strings being different objects that are manipulated rather awkwardly through functions.) In VB, a string is a more complex data type, where the data is still stored as a sequence of bytes, but where the attributes of that data, such as its address, its current and maximum lengths and some other information, is stored in a separate data structure called an HLSTR. Microsoft has documented that an HLSTR exists but has not documented the internals of that data structure.
What Microsoft has done, however, is provide a set of functions that can be used to manipulate HLSTRs (and therefore VB strings) from a non-VB program. This information and interface library actually ships with VB Professional as an essential component of the Control Development Kit (CDK). While primarily intended for custom control writers, these same functions can be used by DLLs to work with VB strings directly.
We do not have room in this article for a thorough discussion on all the functions provided by the VBAPI (oh no, not another API!). However, I will demonstrate how to return a DLL that returns a VB string directly.
One of the VBAPI functions introduced in version 2.0 of VB is VBCreateTempHLSTR. This function s passed a pointer to a string buffer and a length, and returns a HLSTR which is a 32 bit value. The HLSTR returned by this function is a temporary HLSTR and VB provides support for twenty of these. The HLSTR will be destroyed the first time that it is referenced by the VB program, making it ideal for use as the return value of a string function. Here is what the code might look like :
hlstr = VBCreateTempHLSTR(lpBytes, cBytes)
where lpBytes is a long pointer to a buffer, and cBytes is an unsigned integer count of bytes.
This and other functions are documented in the CDK documentation and in the VB API Help file. Borland Pascal programmers can download from Compuserve a Pascal unit that encapsulates this API and makes it available for use in Pascal programs.
If you would like more information on using these functions,
email me at email@example.com.