How to pass arrays and strings between Visual Basic and C functions or between Visual Basic and C++ functions by using Visual Basic 6.0

Article translations Article translations
Article ID: 205277 - View products that this article applies to.
This article was previously published under Q205277
Expand all | Collapse all

On This Page

SUMMARY

This step-by-step article describes how to pass arrays and strings between Microsoft Visual Basic 6.0 applications and functions that are written in C or in C++. This article discusses the following topics:
  • SAFEARRAY data types
  • C-language arrays
  • BSTR data types
  • C-language strings
The sample code in the "Download the sample code" section uses the concepts that this article discusses. However, the sample code does not describe how to work with SAFEARRAY data types or with BSTR data types in C or in C++.

Microsoft provides programming examples for illustration only, without warranty either expressed or implied. This includes, but is not limited to, the implied warranties of merchantability or fitness for a particular purpose. This article assumes that you are familiar with the programming language that is being demonstrated and with the tools that are used to create and to debug procedures. Microsoft support engineers can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific requirements.

Requirements

This article assumes that you are familiar with the following topics:
  • Microsoft Visual Basic 6.0 programming
  • Microsoft Visual C++ 6.0 programming
  • Microsoft Active Template Library (ATL) programming
The following list outlines the recommended hardware, software, network infrastructure, and service packs that you need:
  • Microsoft Windows 2000 or Microsoft Windows XP
  • Microsoft Visual Basic 6.0
  • Microsoft Visual C++ 6.0

Cross-language function calls

When you make cross-language function calls, you must know how each language stores various types of parameters because different programming languages store some data types differently. These parameter types include non-primitive data types such as arrays, strings, and user-defined data types.

To write functions in C or in C++ that are called from Visual Basic, you must understand how these programming languages store the types of parameters that you are using.

Create a DLL project

To pass arrays or strings between Visual Basic and C or between Visual Basic and C++, you can use C, C++, or ATL to create a DLL. When you use ATL, a type library is created that is used for function calls between Visual Basic and the DLL. To use ATL, see the "Create an ATL DLL" section.

To use C or C++ to create a DLL, follow these steps
  1. Start Microsoft Visual C++.
  2. On the File menu, click New.
  3. Under Projects, click Win32 Dynamic-Link Library.
  4. In the Project name box, type StdDLL.
  5. In the Location box, type C:\, and then click OK.
  6. Click A simple DLL project, and then click Finish.
  7. In the New Project Information dialog box, click OK.
  8. Include OLE data types in your project. To do this, follow these steps:
    1. Expand StdDLL files, and then expand Header Files.
    2. Right-click the StdAfx.h file, and then click Open.
    3. In the StdAfx.h file, locate the following line of code:
      #include <windows.h> 
    4. Paste the following code before the code that you located in step c:
      #define INC_OLE2
    5. On the File menu, click New.
    6. Under Files, click Text File.
    7. In the File name box, type StdDLL.def, and then click OK.
    8. Add the following text to the StdDLL.def file:
      LIBRARY StdDLL
      EXPORTS

Create a Visual Basic project

To call functions in a DLL, you can use a Visual Basic application. To create a Visual Basic application project, follow these steps:
  1. In Visual Basic 6.0, create a new Standard EXE project. By default, a form that is named Form1 is created.
  2. Add a CommandButton object to the Form1 form. By default, the Command1 object is created.
  3. In the Project Explorer, right-click Form1, and then click View Code.
  4. Add the following code to Form1:
    Option Base 0
    Option Explicit
  5. In the Project Explorer, right-click Project1, point to Add, and then click Module. The Add Module dialog box appears.
  6. Under New, click Module, and then click Open. By default, the Module1 module is created.

Pass arrays without using type libraries

In Visual Basic, an array is stored as a SAFEARRAY. A SAFEARRAY is a structure that contains information about an array such as the number of dimensions and the size of each element. In C or in C++, an array name is a pointer to the memory location that contains the first element of the array.

Visual Basic permits only valid access to arrays. However, in C or in C++, you are responsible for permitting only valid access to arrays.

SAFEARRAYs

The definition of a SAFEARRAY varies, depending on the operating system that you are using. The following SAFEARRAY structure is a typical, generic definition of a SAFEARRAY:
typedef struct FARSTRUCT tagSAFEARRAY {
   // Count of dimensions in this array.
   unsigned short cDims;

   // Flags that are used by the SafeArray routines that are documented later in the definition.
   unsigned short fFeatures;

   // Size of an element of the array. Does not include size of the data that is pointed to.
   #if defined(WIN32) unsigned long cbElements;

   // Number of times the array has been locked without corresponding unlock.
   unsigned long cLocks;
   #else unsigned short cbElements;
   unsigned short cLocks;

   // Used on Macintosh only.
   unsigned long handle;

   // Pointer to the data.
   #endif void HUGEP* pvData;

   // One bound for each dimension.
   SAFEARRAYBOUND rgsabound[1];

} SAFEARRAY;
Operating systems such as Microsoft Windows 2000 and Windows XP use the whole Win32 API. Therefore, on a computer that is running Windows 2000 or Windows XP, after you process conditional directives, you may define SAFEARRAY in the Oaidl.h file as in the following sample code:
typedef struct tagSAFEARRAY { 
   USHORT cDims;
   USHORT fFeatures;
   ULONG cbElements;
   ULONG cLocks;
   PVOID pvData;
   SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY;
In the Oaidl.h file, SAFEARRAYBOUND is a structure that is defined as in the following sample code:
typedef struct tagSAFEARRAYBOUND {
   unsigned long cElements;
   long lLbound;
} SAFEARRAYBOUND;
The Oaidl.h file also contains prototypes for functions that you can use to access SAFEARRAYs.

Pass an array to a C function or to a C++ function that expects a pointer

Typically, if a function was written in C or in C++, and the function was not specifically designed to be called from Visual Basic, the function expects a pointer to the first element of the array when you pass an array to the function from Visual Basic.

In Visual Basic, you can call a C function or a C++ function that expects a pointer to the first element of the array by passing the first element of the array by reference. Because the C function or the C++ function cannot determine the size of the array, this type of function typically will accept a second parameter that contains the size of the array.

To call a function that expects a pointer to the first element of the array, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, expand Source Files.
  3. Right-click StdDLL.cpp, and then click Open.
  4. Add the following code after the DllMain function:
    long __declspec (dllexport) __stdcall Func1(long *pnFirstElement, long lSize) {
       return 1; 
    }
  5. In the File View, right-click StdDLL.def, and then click Open.
  6. Append the following text to the existing text in the StdDLL.def file:
    Func1
  7. Switch to Visual Basic 6.0.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func1 Lib"C:\StdDLL\Debug\StdDLL.dll" (FirstElement As Long, ByVal Size As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Object.
  10. Double-click Command1 to view the Command1_Click event procedure.
  11. Append the following code to the existing code in the Command1_Click event procedure:
    Dim Result As Long
    Dim MyArrayOfLongs(3) As Long
    
    MyArrayOfLongs(0) = 0
    MyArrayOfLongs(1) = 1
    MyArrayOfLongs(2) = 2
    MyArrayOfLongs(2) = 3
    
    Result = Func1(MyArrayOfLongs(0), 4)
    MsgBox ("Func1 returned " & Result)
Note You cannot use earlier steps to pass an array of Strings or to pass an array of user-defined data types if one or more of the user-defined data types contains Strings.

Pass an array to a C function or to a C++ function that expects a SAFEARRAY

Some C functions and some C++ functions are written specifically to be called from Visual Basic. If you want to pass an array between Visual Basic and one of these functions, use the SAFEARRAY structure in the prototype of the C function or in the prototype of the C++ function.

The advantages of using the SAFEARRAY structure in the prototype of the C function or in the prototype of the C++ function are the following:
  • Because Visual Basic stores arrays as SAFEARRAYs, your Visual Basic application can call the C function or the C++ function as if the function were written in Visual Basic.
  • Because Visual Basic permits only valid access to arrays, when you use the SAFEARRAY structure in the prototype of the C function or in the prototype of the C++ function, your C function or your C++ function will have valid access to information in the SAFEARRAY structure.
  • Because the SAFEARRAY structure contains the size of the array, you do not have to pass an additional parameter to the C function or to the C++ function.
To call a C++ function that uses the SAFEARRAY structure, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func1 function:
    long __declspec (dllexport) __stdcall Func2(SAFEARRAY **ppsaMyArray) {
       return 2;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func2
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func2 Lib"C:\StdDLL\Debug\StdDLL.dll" (MyArray() As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Result = Func2(MyArrayOfLongs())
    MsgBox ("Func2 returned" & Result)
Note Because Visual Basic passes a SAFEARRAY as a pointer to a pointer, you must use SAFEARRAY ** in the prototype of the Func2 function.

Also, you must lock the array before you access it, and then you must release the array after you are finished using it.

Pass strings without using type libraries

A string is a sequence of continuous characters. A BSTR is also known as a basic string or as a binary string.

Strings

To pass strings between Visual Basic and C or between Visual Basic and C++, it is a good idea to understand the different methods that these programming languages use to represent characters. It is also a good idea to understand how these programming languages store strings.

The American National Standards Institute system (ANSI) and the Unicode system (Unicode) are two systems to represent characters. ANSI uses one byte to store characters. Unicode uses two bytes to store characters.


Although Visual Basic uses Unicode internally, Visual Basic converts Unicode characters to ANSI equivalents while passing strings to C or to C++. Visual Basic converts ANSI characters to Unicode equivalents while accepting strings from C or from C++.

However, if you use type libraries to pass strings between Visual Basic and C or between Visual Basic and C++, Visual Basic does not convert the characters because type libraries are based on OLE. OLE uses only Unicode.

Visual Basic stores a string as a BSTR. Essentially, a BSTR is a pointer to a Unicode string. However, this string is prefixed by the length of the string and is followed by a NULL character.

C and C++ store a string as an array of characters. In C or in C++, the name of the character array that is used to store a string is a pointer to a memory location. This memory location contains the first character of the string. Therefore, you can use a pointer to an ANSI string by using the following declaration:
char *pcharMyANSIString;
You can also use a pointer to point to a Unicode string by using the following declaration:
wchar_t *pwcharMyUNICODEString;
However, in C or in C++, you must know the size of the string or you must use a delimiter to mark the end of the string. C and C++ use the NULL character as a delimiter. The NULL character is the ASCII value of zero. ANSI strings use one zero byte as a delimiter and Unicode strings use two zero bytes as a delimiter.

BSTRs

The following line of code defines a BSTR:
typedef OLECHAR FAR* BSTR;
In this definition, if you replace the intermediate definitions from Windows header files and from OLE header files, BSTR is defined the following:
typedef wchar_t* BSTR;
Therefore, a BSTR appears to be a pointer to a Unicode character.

A variable of type BSTR has three parts:
  • The actual Unicode string
  • A 4-byte value that stores the length of the Unicode string

    Note This value prefixes the Unicode string.
  • A 2-byte NULL character that is a delimiter

    Note The delimiter is stored immediately after the Unicode string. The delimiter is not considered to be part of the string. The delimiter does not contribute to the length of the string.
A BSTR that has n characters occupies 2*n + 6 bytes of storage:
  • 4 bytes for the value that stores the length of the string
  • 2*n bytes for the string
  • 2 bytes for the delimiter
Also, the 4-byte value that stores the length of the string is 2*n.

Some important issues to consider while working with BSTRs include the following:
  • Unlike typical C strings or C++ strings, you may have NULL characters as part of your string. Ultimately, the length of the string is determined by the 4-byte value that is used to store the length of the string. The length of the string is not determined by the occurrence of the first NULL character.
  • You can directly access the string that is stored in a BSTR. You can also directly access the 4-byte value that is used to store the length of the string. However, you must be extremely careful if you try to modify a BSTR. If you modify a BSTR incorrectly, your application may quit unexpectedly (crash).
  • The Oleauto.h file contains prototypes for functions that you can use to work with BSTRs. These functions include the SysAllocString function and the SysFreeString function. You can use the functions in the Oleauto.h file to allocate, to destroy, or to reallocate memory for BSTRs.
  • A NULL BSTR is considered to be the equivalent of an empty BSTR. Therefore, when you write C functions or C++ functions that use BSTRs, it is a good idea to look for NULL pointers before you use BSTRs.

Pass a string to a C function or to a C++ function that expects a pointer to an ANSI character

Frequently, you must pass a string from Visual Basic to a C function or to a C++ function that was not specifically intended to be called from Visual Basic. Because Visual Basic converts Unicode characters to ANSI equivalents while passing strings to C or to C++, a pointer to an ANSI character is equivalent to a pointer to a Unicode character before the character has been converted.

A pointer to a Unicode character is represented as the following:
wchar_t
Essentially, this is a BSTR. Therefore, you can call functions that expect a pointer to an ANSI character from Visual Basic by passing a string by value. However, you must always include a parameter in these functions that contains the size of the string that you are passing.

If the C function or the C++ function is going to modify the string, you must allocate sufficient memory for the modified string before you pass the string to the function. Also, a C function or a C++ function that modifies a string should return a value that indicates the size of the modified string.

To call a function that expects a pointer to an ANSI character, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func2 function:
    long __declspec (dllexport) __stdcall Func3(char *pcharFirstCharacter, long lSize){
       return 3;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func3
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func3 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal MyString As String, ByVal Size As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Dim MyString As String
    MyString = "Hello"
    
    Result = Func3(MyString, 5)
    MsgBox ("Func3 returned " & Result)

Pass a string by value

In Visual Basic, if you pass a parameter to a function and you want to prevent the function from modifying the parameter, pass the parameter by value. In the following Visual Basic code, you declare the MyString BSTR as a string that is passed by value from Visual Basic to a C function or to a C++ function that is named MyFunc:
Declare Function MyFunc Lib"C:\MyDLL\Debug\MyDLL.dll" (ByVal MyString As String, ByVal Size As Long) As Long
Because MyString is a BSTR that is passed by value, the C prototype or the C++ prototype of the MyFunc function is the following:
long MyFunc(BSTR MyString, long lSize);
Because BSTR is a pointer to a wchar_t, you may want to write the prototype of the MyFunc function as the following:
long MyFunc(wchar_t *MyString, long lSize);
However, because Visual Basic converts Unicode characters to ANSI equivalents while passing strings to C or to C++, write the prototype of the MyFunc function as the following:
long MyFunc(char *MyString, long lSize);
The lSize parameter passes the size of the MyString BSTR from Visual Basic to C or to C++. The implementation for the MyFunc function is similar to the implementation of the Func3 function in the "Pass a string to a C functions or to a C++ function that expects a pointer to an ANSI character" section.

Pass a string by reference

In Visual Basic, if you pass a parameter to a function, and you want to permit the function to modify the parameter, pass the parameter by reference. In the following Visual Basic code, you declare the MyString BSTR as a string that is passed by reference from Visual Basic to a C function or to a C++ function that is named Func4:
Declare Function Func4 Lib"C:\StdDLL\Debug\StdDLL.dll" (MyString As String, ByVal Size As Long) As Long
Because MyString is a BSTR that is passed by reference, if you want to modify the passed string, you must make the Func4 function accept a pointer to a BSTR. The prototype of the Func4 is the following:
long Func4(BSTR *MyString, long lSize)
Because BSTR is a pointer to a wchar_t, you may want to write the prototype of the Func4 function as the following:
long Func4(wchar_t **MyString, long lSize)
However, because Visual Basic converts Unicode characters to ANSI equivalents while passing strings to C or to C++, write the prototype of the Func4 function as the following:
long Func4(char **MyString, long lSize)
To call the Func4 function, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func3 function:
    long __declspec (dllexport) __stdcall Func4(char **ppcharFirstCharacter, long lSize){
       return 4;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func4
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func4 Lib"C:\StdDLL\Debug\StdDLL.dll" (MyString As String, ByVal Size As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Result = Func4(MyString, 5)
    MsgBox ("Func4 returned" & Result)
Note This type of function must include a parameter that contains the size of the string that is being passed. This type of function returns a value that indicates the size of the modified string.

Pass strings without Unicode-to-ANSI conversion

In Visual Basic, an array of strings is a SAFEARRAY, and every element is a BSTR. If you do not use type libraries when you pass SAFEARRAYs between Visual Basic and C or between Visual Basic and C++, you must use the following methods to prevent Unicode-to-ANSI conversion.

Helper functions

Typically, you can prevent Unicode-to-ANSI conversion by using built-in helper functions that exist in Visual Basic.

The VarPtr function returns the address of a variable. However, you cannot use the VarPtr function to return the address of an array.

The StrPtr function returns the address of a Unicode string. The StrPtr function is used to distinguish between an empty string ("") and a NULL string (vbNullString). The function StrPtr("") returns the address of the memory location where the empty string is stored. However, the StrPtr(vbNullString) function returns zero.

The difference between the VarPtr function and the StrPtr function is especially important if the variable that is being passed to these functions is a string. If the variable is a string, these functions return the following:
  • The StrPtr function returns the address of the Unicode string that is part of the BSTR variable.
  • The VarPtr function returns the address of the BSTR variable.
Therefore, the StrPtr function returns a pointer to the Unicode string and the VarPtr function returns a pointer to the pointer that the StrPtr function returns.

There is no built-in Visual Basic function that returns the address of an array of strings or the address of an array of user-defined types that contains strings. However, you can do this by using the VarPtr function that is defined in the Msvbvm60.dll file.

To use the VarPtr function that is defined in the Msvbvm60.dll file, create a type library that contains corresponding declarations. If you declare the VarPtr function in a type library, Unicode-to-ANSI conversion does not occur because the function call uses the type library.

To use the VarPtr function to return the address of an array of strings or to return the address of an array of user-defined types that contains strings, follow these steps:
  1. In a text editor such as Notepad, paste the following code:
    #define RTCALL _stdcall
    [
    uuid(C6799410-4431-11d2-A7F1-00A0C91110C3),
    lcid (0), version(6.0), helpstring("VarPtrStringArray Support for Visual Basic")
    ]
    library PtrLib
    {
    importlib ("stdole2.tlb");
    [dllname("msvbvm60.dll")]
    module ArrayPtr
       {
       [entry("VarPtr")]
       long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);
       }
    }
  2. On the File menu, click Save. The Save As dialog box appears.
  3. In the File name box, type VBptrlib.odl.
  4. In the Save as type list, select All Files, and then click Save.
  5. Open Command Prompt window.
  6. Change the directory path to the location where you saved the VBptrlib.odl file in step 4.
  7. Create a type library. To do this, type the following command at the command prompt, and then press ENTER:
    MIDL VBptrlib.odl
  8. Switch to Visual Basic 6.0.
  9. On the Project menu, click References.
  10. Locate and then click VarPtrStringArray Support for Visual Basic.
  11. Click OK to add a reference the type library that you created in step 7.

Pass a string to a C function or to a C++ function that expects a pointer to a Unicode character

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func5(wchar_t *pwcharFirstCharacter, long lSize);
In the equivalent Visual Basic declaration, you must pass the address of a Unicode string by value. You can use the StrPtr function to obtain this address.

To call the Func5 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func4 function:
    long __declspec (dllexport) __stdcall Func5(wchar_t *pwcharFirstCharacter, long lSize){
       return 5;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. On a new line, append the following text to the existing text in the StdDLL.def file:
    Func5
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func5 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfFirstCharacter As Long, ByVal Size As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Result = Func5(StrPtr(MyString), 5)
    MsgBox ("Func5 returned" & Result)

Pass a string to a C function or to a C++ function that expects a BSTR

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func6(BSTR bstrMyString);
A BSTR is essentially a pointer to a wchar_t type. Therefore, in the equivalent Visual Basic declaration, you must pass the address of a Unicode string by value. You can use the StrPtr function to obtain this address.

To call the Func6 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func5 function:
    long __declspec (dllexport) __stdcall Func6(BSTR bstrMyString){
       return 6;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func6
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func6 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfFirstCharacter As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Result = Func6(StrPtr(MyString))
    MsgBox ("Func6 returned" & Result)

Pass a string to a C function or to a C++ function that expects a pointer to a BSTR

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func7(BSTR *bstrMyString);
In the equivalent Visual Basic declaration, you must pass the address of a BSTR variable by value. You can use the VarPtr function to obtain this address.

To call the Func7 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func6 function:
    long __declspec (dllexport) __stdcall Func7(BSTR *bstrMyString){
       return 7;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. On a new line, append the following text to the existing text in the StdDLL.def file:
    Func7
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func7 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfStringVariable As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Result = Func7(VarPtr(MyString))
    MsgBox ("Func7 returned" & Result)

Pass an array of strings to a C function or to a C++ function that expects a SAFEARRAY

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func8(SAFEARRAY **ppsaMyArray);
In the equivalent Visual Basic declaration, you must pass the address of a SAFEARRAY by value. You can use the VarPtrStringArray function to obtain this address.

To call the Func8 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func7 function:
    long __declspec (dllexport) __stdcall Func8(SAFEARRAY **ppsaMyArray){
       return 8;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func8
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Declare Function Func8 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfArrayOfStrings As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Dim MyArrayOfStrings(2) As String
    MyArrayOfStrings(0) = "One"
    MyArrayOfStrings(1) = "Two"
    MyArrayOfStrings(2) = "Three"
    
    Result = Func8(VarPtrStringArray(MyArrayOfStrings()))
    MsgBox ("Func8 returned " & Result)

Pass a user-defined data type that contains strings to a C function or to a C++ function that expects a pointer to a structure

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func9(MYSTRUCTURE *pstructMyStruct)
In this prototype, the MYSTRUCTURE structure is defined as the following:
typedef struct{
   long MyLong;
   wchar_t MyUNICODEString[5];
}MYSTRUCTURE;
In the equivalent Visual Basic declaration, you must pass the address of a user-defined data type variable by value. You can use the VarPtr function to obtain this address.
To call the Func9 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func8 function:
    typedef struct{
       long MyLong;
       wchar_t MyUNICODEString[5];
    }MYSTRUCTURE; 
    
    long __declspec (dllexport) __stdcall Func9(MYSTRUCTURE *pstructMyStruct){
       return 9;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. On a new line, append the following text to the existing text in the StdDLL.def file:
    Func9
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Module1, and then click View Code.
  8. Append the following code to the existing code in the Module1 module:
    Type MYSTRUCTURE
       MyLong As Long
       MyString As String * 5
    End Type
    
    Declare Function Func9 Lib "C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfStruct As Long) As Long
  9. In the Project Explorer, right-click Form1, and then click View Code.
  10. Append the following code to the existing code in the Command1_Click event procedure:
    Dim MyStruct As MYSTRUCTURE
    MyStruct.MyLong = 1
    MyStruct.MyString = "Hi"
    
    Result = Func9(VarPtr(MyStruct))
    MsgBox ("Func9 returned " & Result)

Pass an array of user-defined data types that contains strings to a C function or to a C++ function that expects a pointer to a structure

Consider a C function or a C++ function that has the following prototype:
long __declspec (dllexport) __stdcall Func10(MYSTRUCTURE *pstructMyStruct, long lSize);
When the Visual Basic array contains strings, you cannot directly pass the first user-defined data type element of the array by reference because Unicode-to-ANSI conversion occurs. However, you can use the VarPtr function to pass the address of the first user-defined data type element of the array by value.

To call the Func10 function from Visual Basic, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. In the File View, right-click StdDLL.cpp, and then click Open.
  3. Add the following code after the Func9 function:
    long __declspec (dllexport) __stdcall Func10(MYSTRUCTURE *pstructMyStruct, long lSize){
       return 10;
    }
  4. In the File View, right-click StdDLL.def, and then click Open.
  5. Append the following text to the existing text in the StdDLL.def file:
    Func10
  6. On the Build menu, click Build StdDLL.dll.
  7. On the File menu, click Close Workspace.

    Note If you receive a message to save files or to close document windows, click Yes.
  8. Switch to Visual Basic 6.0.
  9. In the Project Explorer, right-click Module1, and then click View Code.
  10. Append the following code to the existing code in the Module1 module:
    Declare Function Func10 Lib"C:\StdDLL\Debug\StdDLL.dll" (ByVal AddressOfFirstStruct As Long, ByVal Size As Long) As Long
  11. In the Project Explorer, right-click Form1, and then click View Code.
  12. Append the following code to the existing code in the Command1_Click event procedure:
    Dim MyArrayOfStructs(1) As MYSTRUCTURE
    MyArrayOfStructs(0).MyLong = 0
    MyArrayOfStructs(0).MyString = "Zero"
    MyArrayOfStructs(1).MyLong = 1
    MyArrayOfStructs(1).MyString = "One"
    
    Result = Func10(VarPtr(MyArrayOfStructs(0)), 2)
    MsgBox ("Func10 returned " & Result)

Create an ATL DLL

DLLs that are created by using ATL contain a type library. When you use type libraries, you do not have to declare references to C functions or to C++ functions that you must call from Visual Basic. Visual Basic obtains all the information that it requires from the type libraries. Also, when you use type libraries, you can call C functions or C++ functions from Visual Basic as though the functions were written in Visual Basic.

To create an ATL DLL, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. On the File menu, click New.
  3. Under Projects, click ATL COM App Wizard
  4. In the Project name box, type ATLDLL, and then click OK. The ATL COM AppWizard dialog box appears.
  5. Under Server Type, click Dynamic Link Library, and then click Finish.
  6. In the New Project Information dialog box, click OK.
  7. In the Class View, right-click ATLDLL classes, and then click New ATL Object.
  8. Under Category, click Objects.
  9. Under Objects, click Simple Object, and then click Next.
  10. In the Short Name box, type Obj1, and then click OK.

Pass an array by using type libraries

When you write a C function or a C++ function specifically to be called from Visual Basic, and you want to pass an array between Visual Basic and C or between Visual Basic and C++, use a SAFEARRAY in the prototype of the C function or in the prototype of the C++ function.

To add a C function or a C++ function to an ATL DLL so that the function accepts an array of long values and so that the function can be called from Visual Basic as though the function were written in Visual Basic, follow these steps.

Note You can follow these steps to add functions to an ATL DLL if you do not pass an array of strings.
  1. In the Class View, expand ATLDLL classes, right-click IObj1, and then click Add Method.
  2. In the Method Name box, type ATL_Func1.
  3. In the Parameters box, type the following, and then click OK:
    [in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult
  4. In the Class View, right-click IObj1, and then click Go to Definition.
  5. In the definition, locate the following code:
    [id(1), helpstring("method ATL_Func1")] HRESULT ATL_Func1([in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult);
  6. To make the ATL_Func1 function comply with the OLE specifications, replace the code that you located in step 5 with the following code:
    [id(1), helpstring("method ATL_Func1")] HRESULT ATL_Func1([in, out] SAFEARRAY (long)*ppsaMyArray, [out, retval] long *plResult);
    Note In this code, (long) specifies an array of long values.
  7. In the Class View, expand IObj1, right-click ATL_Func1, and then click Go to Definition. The contents of the Obj1.cpp file appears.
  8. Locate the following code:
    // TODO: Add your implementation code here
  9. Paste the following code after the code that you located in step 8:
    *plResult = 11;
  10. Switch to Visual Basic 6.0.
  11. In the Project Explorer, right-click Form1, and then click View Code.
  12. Append the following code to the existing code in the Command1_Click event procedure:
    Dim MyObj As New Obj1
    Result = MyObj.ATL_Func1(MyArrayOfLongs())
    MsgBox ("ATL_Func1 returned " & Result)
Note Because Visual Basic passes a SAFEARRAY as a pointer to a pointer, use SAFEARRAY ** in the prototype of the Func2 function.

Pass a string by value by using type libraries

When you write a C function or a C++ function specifically to be called from Visual Basic, and you want to pass a string between Visual Basic and C or between Visual Basic and C++, and you want to prevent the C function or the C++ function from modifying the string, pass the string by value and use BSTR in the prototype of the C function or in the prototype of the C++ function.

To add a function to an ATL DLL so that the function accepts a BSTR and so that the function can be called from Visual Basic as though the function were written in Visual Basic, follow these steps:
  1. Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.
  2. In the Method Name box, type ATL_Func2.
  3. In the Parameters box, type the following, and then click OK:
    [in] BSTR bstrMyString, [out, retval] long *plResult
  4. In the implementation of the ATL_Func2 function in the Obj1.cpp file, locate the following code:
    // TODO: Add your implementation code here
  5. Paste the following code after the code that you located in step 4:
    *plResult = 12;
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Form1, and then click View Code.
  8. Append the following code to the existing code in the Command1_Click event procedure:
    Result = MyObj.ATL_Func2(MyString)
    MsgBox ("ATL_Func2 returned " & Result)
Note f you add a project reference to the type library for your ATL DLL, the type library is used for all calls from Visual Basic to the functions that are declared in the ATLDLL.dll file. Therefore, no Unicode-to-ANSI conversion occurs.

Pass a string by reference by using type libraries

When you write a C function or a C++ function specifically to be called from Visual Basic, and you want to pass a string between Visual Basic and C or between Visual Basic and C++, and you want to permit the C function or the C++ function to modify the string, pass the string by reference and use BSTR * in the prototype of the C function or in the prototype of the C++ function.

To add a function to an ATL DLL so that the function accepts a pointer to a BSTR and so that the function can be called from Visual Basic as though the function were written in Visual Basic, follow these steps:
  1. Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.
  2. In the Method Name box, type ATL_Func3.
  3. In the Parameters box, type the following, and then click OK:
    [in, out] BSTR *pbstrMyString, [out, retval] long *plResult
  4. In the implementation of the ATL_Func3 function in the Obj1.cpp file, locate the following code:
    // TODO: Add your implementation code here
  5. Paste the following code after the code that you located in step 4:
    *plResult = 13;
  6. Switch to Visual Basic 6.0.
  7. In the Project Explorer, right-click Form1, and then click View Code.
  8. Append the following code to the existing code in the Command1_Click event procedure:
    Result = MyObj.ATL_Func3(MyString)
    MsgBox ("ATL_Func3 returned " & Result)

Pass an array of strings by using type libraries

When you write a C function or a C++ function specifically to be called from Visual Basic, and you want to pass an array of strings between Visual Basic and C or between Visual Basic and C++, use a SAFEARRAY in the prototype of the C function or in the prototype of the C++ function.

To add a function to an ATL DLL so that the function accepts an array of strings and so that the function can be called from Visual Basic as though the function were written in Visual Basic, follow these steps:
  1. Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.
  2. In the Method Name box, type ATL_Func4.
  3. In the Parameters box, type the following, and then click OK:
    [in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult
  4. In the implementation of the ATL_Func4 function in the Obj1.cpp file, locate the following code:
    // TODO: Add your implementation code here
  5. Paste the following code after the code that you located in step 4:
    *plResult = 14;
  6. Under ATLDLL classes in the Class View, right-click IObj1, and then click Go to Definition.
  7. Locate the following code:
    [id(4), helpstring("method ATL_Func4")] HRESULT ATL_Func4([in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult);
  8. To make the ATL_Func4 function comply with the OLE specifications, replace the code that you located in step 7 with the following code:
    [id(4), helpstring("method ATL_Func4")] HRESULT ATL_Func4([in, out] SAFEARRAY (BSTR)*ppsaMyArray, [out, retval] long *plResult);
    Note In this code, (BSTR) specifies an array of strings.
  9. Switch to Visual Basic 6.0.
  10. In the Project Explorer, right-click Form1, and then click View Code.
  11. Append the following code to the existing code in the Command1_Click event procedure:
    Result = MyObj.ATL_Func4(MyArrayOfStrings())
    MsgBox ("ATL_Func4 returned " & Result)

Verify that your project works

To verify that your Visual Basic project works, follow these steps:
  1. Switch to Visual C++ 6.0.
  2. On the Build menu, click Build ATLDLL.dll.
  3. Switch to Visual Basic 6.0.
  4. On the Project menu, click References.
  5. Locate and then click ATLDLL 1.0 Type Library, and then click OK.
  6. On the Run menu, click Start. By default, a form that is named Form1 is created.
  7. In the Form1 form, click Command1. You may notice a series of messages that contain the return values of the C functions or of the C++ functions.
  8. Click OK to close each message box. When you close each message box, the next message box appears.

Download the sample code

Sample code that uses the concepts that are mentioned in this article is available in the Microsoft Download Center.

The following file is available for download from the Microsoft Download Center:
Collapse this imageExpand this image
Download
Download the CallCDllinVB.zip package now.
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.

Download this file and unzip all the files. This .zip file contains the following three folders:
  • StdDLL - This folder contains a Visual C project to create a standard C DLL or a standard C++ DLL.
  • ATLDLL - This folder contains a Visual C project to create an ATL DLL.
  • VB - This folder contains a Visual Basic project to call functions in the StdDLL.dll file and in the ATLDLL.dll file. This project file is named Project1.vbp.
To use these projects, follow these steps:
  1. Build the StdDLL.dll file and the ATLDLL.dll file.
  2. Change the declarations in the Module1.bas file of the Project1.vbp project file, to point to the location of the StdDLL.dll file.
  3. In the Project1.vbp project file, add a reference to the ATLDLL 1.0 type library that you built in step 1.
You can now run the Visual Basic application.

Troubleshooting

The following list contains problems that you may experience when you create the sample project that this article uses:
  • Symptom
    When you try to build the StdDLL.dll file, you may receive an error message that is similar to the following:
    error C2065: 'SAFEARRAY' : undeclared identifier
    Cause
    This problem may occur if your project has not included the Ole2.h header file.

    Resolution
    To resolve this problem, locate the following code in the StdAfx.h file:
    #include <windows.h> 
    Paste the following code before the code that you located:
    #define INC_OLE2
  • Symptom
    When you try to build the ATLDLL.dll file, you may receive error messages that are similar to the following:
    error MIDL2139 : type of the parameter cannot derive from void or void * : [ Type 'PVOID' ( Parameter 'ppsaMyArray' ) ]
    error MIDL2105 : pointee / array does not derive any size : [ Field 'rgsabound' of Struct 'tagSAFEARRAY' ( Parameter 'ppsaMyArray' ) ]
    Cause
    This problem may occur if your function does not comply with the OLE specifications.

    Resolution
    To resolve this problem, locate the code that corresponds to the following code for the function in the ATLDLL.dll file:
    [id(1), helpstring("method ATL_Func1")] HRESULT ATL_Func1([in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult);
    Replace the code that you located with code that corresponds to the following code:
    [id(1), helpstring("method ATL_Func1")] HRESULT ATL_Func1([in, out] SAFEARRAY (long)*ppsaMyArray, [out, retval] long *plResult);
  • Symptom
    When you click Command1, you may receive an error message that is similar to the following:
    Run-time error '453':
    Can't find DLL entry point Func1 in C:\StdDLL\Debug\StdDLL.dll
    Cause
    This problem may occur if you have not added a function name to the StdDLL.def file.

    Resolution
    To resolve this problem, add the missing function name to the StdDLL.def file.

REFERENCES

For additional information, click the following article number to view the article in the Microsoft Knowledge Base:
199824 HOWTO: Get the address of variables in Visual Basic
For more information about arrays, visit the following Microsoft Developer Network (MSDN) Web sites:
http://msdn2.microsoft.com/en-us/library/aa261364.aspx

http://msdn2.microsoft.com/en-us/library/7wkxxx2e(vs.71).aspx
For more information about data types, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/aa716185.aspx
For more information about conversion functions and about manipulation functions, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/ms221236.aspx
For more information about BSTRs, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/zthfhkd6(vs.71).aspx
For more information about DLLs, visit the following MSDN Web sites:
http://msdn2.microsoft.com/en-us/library/1ez7dh12(vs.71).aspx

http://msdn2.microsoft.com/en-us/library/z4zxe9k8(vs.71).aspx

http://msdn2.microsoft.com/en-us/library/dt232c9t(vs.71).aspx
For more information about Unicode, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/dtxesf6k(vs.71).aspx
For more information about OLE programming, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/df267wkc(vs.71).aspx
For more information about ATL, visit the following MSDN Web site:
http://msdn2.microsoft.com/en-us/library/3ax346b7(vs.71).aspx
For more information about distributed COM, about type libraries, and about interface definitions, visit the following MSDN Web sites:
http://msdn2.microsoft.com/en-us/library/ms809974.aspx

http://msdn2.microsoft.com/en-us/library/aa367061.aspx

Properties

Article ID: 205277 - Last Review: May 31, 2007 - Revision: 4.3
APPLIES TO
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual C++ 6.0 Service Pack 5
  • Microsoft ActiveX Template Library 3.0
Keywords: 
kbdownload kbappwizard kbwizard kbsdk kbolearc kboleapp kbmidl kbidl kbdll kbcomservices kbcommandline kbatlserver kbapi kb32bitonly kbatlwc kblangcpp kblangc kbdocs kbautomation kbprogramming kbhowtomaster kbhowto KB205277

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com