DOCUMENT:Q196976 01-MAR-2002 [vbwin] TITLE :INFO: Updated VB4dll.txt for Visual Basic 4.0 PRODUCT :Microsoft Visual Basic for Windows PROD/VER::4.0 OPER/SYS: KEYWORDS:kbVBp400 kbGrpDSVBDB ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - Microsoft Visual Basic Standard Edition for Windows, version 4.0 - Microsoft Visual Basic Professional Edition for Windows, version 4.0 - Microsoft Visual Basic Enterprise Edition for Windows, version 4.0 ------------------------------------------------------------------------------- SUMMARY ======= The following article contains the updated 'Passing and Returning Strings' section of the updated VB4dll.txt file distributed with Microsoft Visual Basic version 4.0 for Windows. The information contained in that section is correct, but somewhat confusing to someone that is not familiar with the inner workings of Visual Basic. Visual Basic does not pass a BSTR to a UNICODE string to a DLL unless a type library is used. The code in the MORE INFORMATION section demonstrates how to handle this issue in the .dll. MORE INFORMATION ================ Section four (4) correctly describes passing a BSTR to a .dll, but the method that it uses is confusing. Visual Basic 4.0 32-bit will not pass a 'proper' BSTR to the .dll. In Win32 OLE, a BSTR is described as pointing to a UNICODE string. Since most of the world is still ANSI, Visual Basic converts the string to ANSI before passing. It therefore, is not a 'proper' BSTR. It is completely valid to do this, but it is not what one might expect in the .dll. Since a BSTR is defined as ANSI in Win16 OLE, this is not a problem with Visual Basic 4.0 16-bit. When Visual Basic 4.0 32-bit is going to pass a string to a .dll, it allocates some temporary memory, creates a new BSTR with a buffer half as large as the original. It then converts the UNICODE characters in the actual string into ANSI and copies them into the buffer pointed to by the new BSTR. This may sound like an odd thing to do, but is done to solve the following two problems: 1. Most of the world is not yet using UNICODE. Most existing .dll's and API's use ANSI strings. If Visual Basic did not convert the strings to ANSI, the Visual Basic programmer would be locked out of the vast majority of existing .dll functions. 2. The 16-bit version passes the actual BSTR to the .dll. If the 32-bit did not pass a BSTR then 16-bit .dll's would have to be entirely re-written so that they did not use any BSTR functions. This would have run counter to one of the main reasons for offering a 16-bit and 32-bit version [ASCII 150] easier porting. The solution is to pass a BSTR that has an ANSI string stuffed in it. The code example in this section relies on this behavior. As properly documented in section 9 of the VB4dll.txt file, Visual Basic can not pass a BSTR that points to a UNICODE string to a .dll without a type library. It always converts the string to ANSI as described in the first sentence of this paragraph. To pass a BSTR pointing to a .dll, you should use a Type library. It is possible to do this other ways, but all of them rely on some language quirk or loophole that may not be available in future versions. The following section has been updated so that the preceding points are clear. The original version that ships with Visual Basic 4.0 is correct. The modifications below only clarify what is being done. 4: Passing and Returning Strings --------------------------------- Visual Basic maintains variable-length strings internally as BSTRs. BSTRs are defined in the OLE header files as OLECHAR FAR *. An OLECHAR is a UNICODE character in 32-bit OLE and an ANSI character in 16-bit OLE. A BSTR can contain NULL values because a length is also maintained with the BSTR. BSTRs are also NULL terminated so they can be treated as an LPSTR. Currently this length is stored immediately prior to the string. This may change in the future, however, so you should use the OLE APIs to access the string length. You can pass a string from Visual Basic to a DLL in one of two ways. You can pass it "by value" (ByVal) or "by reference". When you pass a string ByVal, Visual Basic passes a pointer to the beginning of the string data (i.e. it passes a BSTR). When a string is passed by reference, Visual Basic passes a pointer to a pointer to the string data (i.e. it passes a BSTR *). The following table lists what Visual Basic will pass to a DLL function when passing a string. Version By Value By Reference ------------------------------------- 3.0 LPSTR HLSTR 4.0 BSTR BSTR * NOTE: Visual Basic 4.0 32-bit BSTR's point to either ANSI or UNICODE strings depending on the method used to call them as illustrated in the following chart. Declaration Method By Value By Reference -------------------------------------------------------------- Declare Statement ANSI ANSI Type Library (defined as BSTR) BSTR BSTR In Visual Basic 3.0, you could use the Visual Basic API routines to access and modify an HLSTR. In Visual Basic 4.0 you should use the OLE APIs to access a BSTR. The following table lists the Visual Basic 3.0 string- handling APIs, and the OLE equivalents. Visual Basic API OLE API -------------------------------------------------------- VBCreateHlstr SysAllocString/SysAllocStringLen VBCreateTempHlstr SysAllocString/SysAllocStringLen VBDerefHlstr* N/A VBDerefHlstrLen* N/A VBDerefZeroTermHlstr N/A VBDestroyHlstr SysFreeString VBGetHlstrLen SysStringLen VBResizeHlstr SysReAllocStringLen VBSetHlstr SysReAllocString NOTE: The BSTR is a pointer to the string, so you do not need to dereference it. 16\32-Bit Example: The first function in this example takes a Visual Basic string by reference, and returns an uppercase copy of the string. The second function takes a Visual Basic string by value and also returns an uppercase copy of the string. This is similar to the UCase function in Visual Basic. In both cases, the .dll function modifies the passed string, which is reflected back in Visual Basic. This happens even when the Visual Basic string is passed ByVal because what is passed to the .dll function is a BSTR which is a char far *, and thus, it is possible to directly access the memory buffer pointed to by the BSTR. Sample Code: #include #include #ifdef _WIN32 #define CCONV _stdcall #define NOMANGLE #else #define CCONV FAR PASCAL _export #define NOMANGLE EXTERN_C #include #include #include #include #include #endif NOMANGLE BSTR CCONV UpperCaseByRef(BSTR *pbstrOriginal) { BSTR bstrUpperCase; int i; int cbOriginalLen; LPSTR strSrcByRef, strDst; //This is code to allocate a BSTR that points to an //ANSI string for 16 or 32 bit. #if !defined(_WIN32) // 16 bit uses ANSI strings to char = byte cbOriginalLen = SysStringLen(*pbstrOriginal); #else //32 bit uses UNICODE strings char = 2 * byte //but Visual Basic passed an ANSI string. cbOriginalLen = SysStringByteLen(*pbstrOriginal); #endif bstrUpperCase = SysAllocStringLen(NULL, cbOriginalLen); strSrcByRef = (LPSTR)*pbstrOriginal; strDst = (LPSTR)bstrUpperCase; //Using runtime function for ANSI strings for(i=0; i<=cbOriginalLen; i++) *strDst++ = toupper(*strSrcByRef++); //Placing ANSI string in for return to Visual Basic SysReAllocString (pbstrOriginal, (BSTR)"Good Bye"); return bstrUpperCase; } NOMANGLE BSTR CCONV UpperCaseByVal(BSTR bstrOriginal) { BSTR bstrUpperCase; int i; int cbOriginalLen; LPSTR strSrcByVal, strDst; // This is code to allocate a BSTR that points to an // ANSI string for 16 or 32 bit. #if !defined(_WIN32) // 16-bit uses ANSI strings to char = byte cbOriginalLen = SysStringLen(bstrOriginal); #else // 32-bit uses UNICODE strings char = 2 * byte // but Visual Basic passed an ANSI string. cbOriginalLen = SysStringByteLen(bstrOriginal); #endif bstrUpperCase = SysAllocStringLen(NULL, cbOriginalLen); strSrcByVal = (LPSTR)bstrOriginal; strDst = (LPSTR)bstrUpperCase; // Using run-time function for ANSI strings for(i=0; i<=cbOriginalLen; i++) *strDst++ = toupper(*strSrcByVal++); // Placing ANSI string in for return to Visual Basic. SysReAllocString (&bstrOriginal, (BSTR)"Good Bye"); return bstrUpperCase; } The following Visual Basic code calls the preceding two UpperCase functions: #If Win32 Then Private Declare Function UpperCaseByRef Lib "vb4dll32.dll" (Str _ As String) As String Private Declare Function UpperCaseByVal Lib "vb4dll32.dll" _ (ByVal Str As String) As String #Else Private Declare Function UpperCaseByRef Lib "vb4dll16.dll" (Str _ As String) As String Private Declare Function UpperCaseByVal Lib "vb4dll16.dll" _ (ByVal Str As String) As String #End If Private Sub StringTest () Dim Str As String, NewStr As String Str = "Hello World!" MsgBox "In VB, Before: " & Str NewStr = UpperCaseByRef(Str) MsgBox "In VB, After: " & Str MsgBox "In VB, CapsStr: " & NewStr Str = "Hello World!" MsgBox "In VB, Before: " & Str NewStr = UpperCaseByVal(Str) MsgBox "In VB, After: " & Str MsgBox "In VB, CapsStr: " & NewStr End Sub REFERENCES ========== (c) Microsoft Corporation 1998. All Rights Reserved. Contributions by Troy Cambra, Microsoft Corporation Additional query words: ====================================================================== Keywords : kbVBp400 kbGrpDSVBDB Technology : kbVBSearch kbAudDeveloper kbZNotKeyword6 kbZNotKeyword2 kbVB400Search kbVB400 Version : :4.0 Issue type : kbinfo ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2002.