You are currently offline, waiting for your internet to reconnect

How to Pass a String or String Arrays Between VB and a C DLL

Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer offers support. Therefore, this article is offered "as is" and will no longer be updated.
Summary
This article demonstrates techniques to perform the following tasks betweenVisual Basic and a C DLL:
  • Pass a string to a C DLL.
  • Pass (and modify) an array of strings to a C DLL.
  • Return a string from a function within a C DLL.
Each of these techniques demonstrates slightly different way of handlingstrings within a DLL, and the last two require the Control Development Kit,which comes with Visual Basic version 3.0, Professional Edition.
More information
There are two parts to each technique demonstrated: the declaration withinyour Visual Basic code for the C function, and the internals of the Cfunction that process the data being passed, modified, or sent back.

Pass a String to a C DLL

The declaration for the C function must use ByVal for C to receive astring. C functions typically expect strings to end in a "null character"(binary zero). When you declare the function argument using ByVal, thistells Visual Basic to pass the string as a null-terminated string. Thus,the declaration looks like this:
   Declare Sub passOneString Lib "mydll.dll" (ByVal lpszBuf As String)
The actual C function looks like this:
   void __far __pascal __export passOneString(char __far *lpszBuf)
The actual internals of the C function are straightforward while itreceives the argument as a standard C string.

NOTE: Both "far" and "pascal" are compiler-specific keywords that areallowed by the ANSI standard. They should have the appropriate number ofunderscores to put them in the appropriate name space. The latest compilersstrictly follow the standard and say that you need two underscores (butwill allow fewer for backward compatibility). Some previous Microsoftcompilers only allowed 0 or 1 underscores.

Pass (and Modify) an Array of Strings to a C DLL

The declaration for the function demonstrating this technique does not useByVal. Because an array is being passed, the declaration needs to pass thearray of strings to the function using the internal Visual Basic arraydescriptor (HAD). Thus, the declaration looks like this:
   Declare Sub passStrings Lib "mydll.dll" (theArray() As String,                   ByVal nIndex% )
The actual C function looks like this:
   void __far __pascal __export passStrings(HAD had, int nIndex)
The function VBArrayElement() is used to extract a Visual Basic stringhandle (HLSTR) from the array (using the index into the array specified bynIndex). Then VBGetHlstr() converts that string handle into a standard Cstring. If you want, and if the buffer used to receive the string is largeenough for the changes, then you can modify that C string as you like.However, the modified string needs to be stored in the original VisualBasic string handle. This is done through the VBSetHlstr() function.

Return a String from a Function Within a C DLL

For a Visual Basic routine to receive a string from a function, thatfunction must be declared to return a string and, within the C side, toreturn a variable of type HLSTR. Thus, the declaration looks like this:
   Declare Function returnAString Lib "mydll.dll" () As String
The actual C function looks like this:
   hlstr __far __pascal __export returnAString()
This time, the VBCreateTempHlstr is used to transform a given standard Cstring into a Visual Basic string handle. Then the function simply returnsthat handle, which Visual Basic receives as a string.

Additionally, the VBRuntimeError function is used to generate a VisualBasic run-time error. If an error condition exists after callingVBCreateTempHlstr, VBRuntimeError sets a Visual Basic error code and thenexits the function. For this reason, this function should only be used in aroutine that is called directly by Visual Basic, not in the controlprocedure of a custom control or other location where Windows itselfinvokes the code.

Step-by-Step Example to Create the DLL

Here are the steps necessary to build a DLL using Visual C++:
  1. Start Visual C++.
  2. Create a new project by choosing New from the Project menu. Select the following options:

    1. Set the Project Type to "Windows dynamic-link library (.DLL)".
    2. Clear the "Use Microsoft Foundation Classes" check box.
  3. Create a new file, add the following text and save it as "MYDLL.DEF":
       LIBRARY        MYDLL   DESCRIPTION    "String Manipulation DLL"   EXETYPE        WINDOWS 3.1   CODE           PRELOAD MOVEABLE DISCARDABLE   DATA           PRELOAD MOVEABLE SINGLE   HEAPSIZE       4096   EXPORTS            passOneString  @1            passStrings    @2            returnAString  @3
  4. Create a new file, add the following code, and save it as "MYDLL.C":
       #include <windows.h>   #include <string.h>   #include <stdio.h>   #include "vbapi.h"   #define USHORT unsigned short   //-------------------------------------------------------------------   // Global Variables   //-------------------------------------------------------------------   HANDLE hmodDLL;   //-------------------------------------------------------------------   // Receive a single string in functions argument list:   // display string to user   //-------------------------------------------------------------------   void __far __pascal __export passOneString(LPSTR lpszBuf)   {      // Display old string to user.      MessageBox( NULL, lpszBuf, "Inside DLL...  (unmodified)", 0 );   }   //-------------------------------------------------------------------   // Receive/modify array of strings in functions argument list:   // - Retrieve a specified element in an array of strings.   // - Transform that element into a standard C string.   // - Modify that standard C string.   // - Save the modified string back into the original element of the   //   array.   //-------------------------------------------------------------------   void __far __pascal __export passStrings(HAD had, int nIndex)   {      HLSTR       hlstr;               // Reference to VB string      int         idx[1];              // Array in one dimension      char        lpszBuf[255];        // "Std C" string buffer      USHORT      cbCount;             // # of characters in string      // Retrieve a specified element (nIndex) in an array of strings.      idx[0] = nIndex;      hlstr = VBArrayElement(had, 1, (LPINT)idx);      // Transform that element into a standard C string.      cbCount = VBGetHlstr( hlstr, lpszBuf, sizeof( lpszBuf ) - 1 );      // Display old string to user.      MessageBox( NULL, lpszBuf, "Inside DLL...  (unmodified)", 0 );      // Modify that standard C string.      wsprintf(lpszBuf, "%s <-- size = %d", lpszBuf, lstrlen(lpszBuf));      // Save the modified string in the original element of the array.      VBSetHlstr( &hlstr, lpszBuf, lstrlen( lpszBuf ) );   }   //-------------------------------------------------------------------   // Return a string from a function back to a Visual Basic program:   // - Create Hlstr from standard C string.   // - Report error (if any).   // - Return Hlstr to calling VB function.   //-------------------------------------------------------------------   HLSTR __far __pascal __export returnAString()   {       HLSTR temp;       char *buff = {"This function returns a string from a DLL."};      // Creates Hlstr from standard C string.       temp = VBCreateTempHlstr(buff, lstrlen(buff));      // Report error (if any).      if (HIWORD(temp) == -1)         VBRuntimeError(LOWORD(temp));      // Return Hlstr to calling VB function.      return temp;   }   //-------------------------------------------------------------------   // Initialize library.   // This routine is called from the DLL entry point in LIBINIT.ASM,   // which is called when the first client loads the DLL.   //-------------------------------------------------------------------   BOOL FAR PASCAL LibMain(HANDLE hmod, HANDLE segDS, USHORT cbHeapSize)   {       // Avoid warnings on unused (but required) formal parameters.       cbHeapSize = cbHeapSize;       segDS = segDS;       hmodDLL = hmod;       // Leave the DS unlocked when not running.       // Required only under Windows version 3.1       // Win32 does not require or support UnlockData()       UnlockData( 0 );       return TRUE;   }   //-------------------------------------------------------------------   // Handle exit notification from Windows.   // This routine is called by Windows when the library is freed   // by its last client.   //-------------------------------------------------------------------   VOID __far __pascal __export WEP (BOOL fSystemExit)   {       // Avoid warnings on unused (but required) formal parameters.       fSystemExit = fSystemExit;   }
  5. From the Project menu, choose the Build MYDLL.DLL option.
  6. Add the MYDLL.DEF, MYDLL.C, and \VB\CDK\VBAPI.LIB files to the project. This assumes you have installed Visual Basic in the \VB directory. Otherwise, modify this last entry to the correct path.

Step-by-Step Example to Create Visual Basic Application to Use DLL

  1. Start a new project in Visual Basic. Form1 is created by default.
  2. Place three command buttons (Command1, Command2, and Command3) on Form1.
  3. Using the following table as a guide, set the properties of the controls added in step 2.
       Control name   Property   New value   ---------------------------------------------   Command1       Caption    &Pass a String   Command2       Caption    Pass &And Modify Array   Command3       Caption    &Receive a String
  4. Add the following code to the (general) (declarations) section of Form1:
       ' Enter each of the following Declare statements as one, single line:   Declare Sub passOneString Lib "mydll.dll" (ByVal lpszBuf As String)   Declare Sub passStrings Lib "mydll.dll" (theArray() As String,      ByVal nIndex%)   Declare Function returnAString Lib "mydll.dll" () As String
  5. Place the following code in the Command1 Click event procedure:

       Sub Command1_Click ()      Dim MyStr As String * 80      Dim My2ndStr$      MyStr = "Hello World (First String)"      My2ndStr$ = "Hello World Again (Second String)"      Call passOneString(MyStr)      Call passOneString(My2ndStr$)   End Sub
    NOTE: This demonstrates that either method of defining a string works equally well.
  6. Place the following code in the Command2 Click event procedure:
       Sub Command2_Click ()      Dim i%      ReDim array(0 To 2) As String      array(0) = "Short"      array(1) = "Medium Medium"      array(2) = "Long Longer Longest"      For i% = 0 To 2         Call passStrings(array(), i%)         MsgBox array(i%), 0, "Inside VB...  (modified)"      Next i%   End Sub
  7. Place the following code in the Command3 Click event procedure:
       Sub Command3_Click ()      Dim MyStr$      MyStr$ = returnAString()      MsgBox MyStr$, 0, "Returned from DLL..."   End Sub
  8. Choose Start from the Run menu to run the program.
References
  • Visual Basic for Windows, version 3.0 "Programmer's Guide," pp. 563-566.
  • Visual Basic for Windows, version 3.0 "Professional Features Book 1: Control Development Guide," pp. 69-71.
  • Knowledge Base article 106553, "How to Write C DLLs and Call Them from Visual Basic."
3.00 DLL Strings HLSTR
Properties

Article ID: 118643 - Last Review: 06/17/2014 21:47:00 - Revision: 2.0

  • KB118643
Feedback