BUG: Fixed Length String Array Passed To DLL Is Not Converted
  
PSS ID Number: Q145730
Article last modified on 02-12-1996
 
4.00    | 4.00
 
WINDOWS | WINDOWS NT
 

------------------------------------------------------------------------
The information in this article applies to:
 
 - Standard, Professional, and Enterprise Editions of Microsoft Visual
   Basic, 16-bit and 32-bit, for Windows, version 4.0
------------------------------------------------------------------------
 
SYMPTOMS
========
 
Fixed length strings within arrays are not being converted from UNICODE to
ANSI when passed to dynamic-link libraries (DLLs).
 
STATUS
======
 
Microsoft has confirmed this to be a problem in the Microsoft products
listed at the beginning of this article. We are researching this problem
and will post new information here in the Microsoft Knowledge Base as it
becomes available.
 
WORKAROUND
==========
 
To work around this problem, put a redimensionable array in a user defined
type (UDT) and pass the user defined type to your DLL by reference. Using a
UDT forces Visual Basic to do the required Unicode/ANSI conversion. For
example:
 
   Private Type rec
       x() As String * 5
   End Type
 
   Private Declare Sub bar Lib "MyLib.DLL" (ByRef x As rec)
 
   Private Sub Command1_Click()
 
      Dim r As rec
      ReDim r.x(4)
      r.x(0) = "This"
      r.x(1) = "Is A"
      r.x(2) = "Test"
      r.x(3) = "Of Th"
      r.x(4) = "e DLL"
 
      Call bar
 
   End Sub
 
A sample C DLL routine is shown below:
 
typedef struct rectag
{
   SAFEARRAY *psa;
} rec;
 
void WINAPI bar ( rec *MyUDT )
{   long lUBound, lLBound;
   HRESULT hresult;
   char Buffer[20];
   int i;
   hresult = SafeArrayGetUBound(MyUDT->psa, 1, &lUBound);
   MessageBox(0,_itoa(lUBound,Buffer, 10),
"Upper bound of Array", MB_OK);
   if(FAILED(hresult))
      MessageBox(0,"Failed to find arrays upper bound",
"Upper Bound of Array", MB_OK);
   else
   {
      hresult = SafeArrayGetLBound(MyUDT->psa, 1, &lLBound);
      if(FAILED(hresult))
         MessageBox(0,"Failed to find arrays lower bound",
"Lower Bound of Array", MB_OK);
      else
      {
         MessageBox(0,
_itoa(lLBound,Buffer, 10), "Lower bound of Array", MB_OK);
         MessageBox(0,
_itoa(SafeArrayGetDim(MyUDT->psa),Buffer, 10),
"Dimension of Array", MB_OK);
/* N.B. After conversion, each array element holds
   two ASCII entries. */
         for(i=lLBound;i<=lUBound / 2;i++)
         {
            MessageBox(0,
_itoa(SafeArrayGetElemsize(MyUDT->psa),Buffer, 10),
"Size of Array Element (in Bytes)", MB_OK);
            hresult = SafeArrayGetElement(MyUDT->psa, &i,  Buffer);
            MessageBox(0, Buffer, "SBCS Array Element", MB_OK);
         }
      }
   }
 
}
 
MORE INFORMATION
================
 
Steps to Reproduce Problem
--------------------------
 
1. Start Visual Basic with a New Project.
 
2. Add a command button to a form.
 
3. Add the following code to the form's "General Declarations" section:
 
   Private Declare Sub foo Lib "MyLib.DLL" (x() As Any)
 
4. Add the following code to the Click event of the command button:
 
   Private Sub Command1_Click()
 
      Dim x(4) As String * 5
 
      x(0) = "This"
      x(1) = "Is A"
      x(2) = "Test"
      x(3) = "Of Th"
      x(4) = "e DLL"
 
      Call foo(x())
 
   End Sub
 
5. Create a DLL with an exported procedure that uses the safe array. For
   example:
 
   #include <windows.h>
   void WINAPI foo ( SAFEARRAY **psa )
   {    long lUBound, lLBound;
      HRESULT hresult;
      char Buffer[20];
      int i;
      hresult = SafeArrayGetUBound(*psa, 1, &lUBound);
      if(FAILED(hresult))
         MessageBox(0,"Failed to find array's upper bound",
         "Upper Bound of Array", MB_OK);
      else
      {
         MessageBox(0,_itoa(lUBound,Buffer, 10),
         "Upper bound of Array", MB_OK);
         hresult = SafeArrayGetLBound(*psa, 1, &lLBound);
         if(FAILED(hresult))
            MessageBox(0,"Failed to find array's lower bound",
            "Lower Bound of Array", MB_OK);
         else
         {
            MessageBox(0,_itoa(lLBound,Buffer, 10),
            "Lower bound of Array", MB_OK);
            MessageBox(0,
   _itoa(SafeArrayGetDim(*psa),Buffer, 10),
   "Dimension of Array", MB_OK);
            for(i=lLBound;i<=lUBound;i++)
            {
               MessageBox(0,
   _itoa(SafeArrayGetElemsize(*psa),Buffer, 10),
   "Size of Array Element (in Bytes)", MB_OK);
               hresult = SafeArrayGetElement(*psa, &i,  Buffer);
   /* N.B. We must use a Wide function to display the
    array element's UNICODE contents */
               MessageBoxW(0,(LPCWSTR) Buffer,
               L"Unicode Array Element", MB_OK);
            }
         }
      }
   }
 
6. The .DEF file for the above DLL is shown below:
 
   ; The DEF File
   LIBRARY MyLib
 
   CODE   PRELOAD MOVEABLE DISCARDABLE
   DATA   PRELOAD MOVEABLE
 
   EXPORTS
      foo @1
      bar @2
 
7. Run your Visual Basic program and press the form's command button. You
   will be presented with a sequence of messages boxes which show the Upper
   and lower bounds of the array, and the number of dimensions that the
   array contains. This will be followed by another sequence of message
   boxes which show the size and Unicode contents of the array. Note that
   the Unicode array contents must be displayed using the Wide function
   MessageBoxW.
 
KBCategory: kbusage kbbuglist
KBSubcategory: APrgOther
Additional reference words: 4.00 vb4win vb4all buglist4.00
=============================================================================
Copyright Microsoft Corporation 1996.
