Microsoft KB Archive/205277

= 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 ID: 205277

Article Last Modified on 5/31/2007

-

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

-



This article was previously published under Q205277



IN THIS TASK
 SUMMARY  Requirements Cross-language function calls Create a DLL project Create a Visual Basic project Pass arrays without using type libraries  SAFEARRAYs</li> Pass an array to a C function or to a C++ function that expects a pointer</li> Pass an array to a C function or to a C++ function that expects a SAFEARRAY</li></ul> </li></ul>

<ul> Pass strings without using type libraries <ul> Strings</li> BSTRs</li> Pass a string to a C function or to a C++ function that expects a pointer to an ANSI character</li> Pass a string by value</li> Pass a string by reference</li></ul> </li></ul>

<ul> Pass strings without Unicode-to-ANSI conversion <ul> Helper functions</li> Pass a string to a C function or to a C++ function that expects a pointer to a Unicode character</li> Pass a string to a C function or to a C++ function that expects a BSTR</li> Pass a string to a C function or to a C++ function that expects a pointer to a BSTR</li> Pass an array of strings to a C function or to a C++ function that expects a SAFEARRAY</li> 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</li> 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</li></ul> </li></ul>

<ul> <li>Create an ATL DLL <ul> <li>Pass an array by using type libraries</li> <li>Pass a string by value by using type libraries</li> <li>Pass a string by reference by using type libraries</li> <li>Pass an array of strings by using type libraries</li></ul> </li> <li>Verify that your project works</li> <li>Download the sample code</li> <li>Troubleshooting</li></ul> </li> <li>REFERENCES</li></ul>

<div class="summary_section">

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. back to the top

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

back to the top

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.

back to the top

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 <ol> <li>Start Microsoft Visual C++.</li> <li>On the File menu, click New.</li> <li>Under Projects, click Win32 Dynamic-Link Library.</li> <li>In the Project name box, type StdDLL .</li> <li>In the Location box, type C:\, and then click OK.</li> <li>Click A simple DLL project, and then click Finish.</li> <li>In the New Project Information dialog box, click OK.</li> <li>Include OLE data types in your project. To do this, follow these steps: <ol style="list-style-type: lower-alpha;"> <li>Expand StdDLL files, and then expand Header Files.</li> <li>Right-click the StdAfx.h file, and then click Open.</li> <li> In the StdAfx.h file, locate the following line of code: <li> Paste the following code before the code that you located in step c: <li>On the File menu, click New.</li> <li>Under Files, click Text File.</li> <li>In the File name box, type StdDLL.def, and then click OK.</li> <li> Add the following text to the StdDLL.def file: LIBRARY StdDLL EXPORTS </li></ol> </li></ol>
 * 1) include <windows.h> </li>
 * 1) define INC_OLE2 </li>

back to the top

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: <ol> <li>In Visual Basic 6.0, create a new Standard EXE project. By default, a form that is named Form1 is created.</li> <li>Add a CommandButton object to the Form1 form. By default, the Command1 object is created.</li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Add the following code to Form1: Option Base 0 Option Explicit </li> <li>In the Project Explorer, right-click Project1, point to Add, and then click Module. The Add Module dialog box appears.</li> <li>Under New, click Module, and then click Open. By default, the Module1 module is created.</li></ol>

back to the top

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.

back to the top

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.

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, expand Source Files.</li> <li>Right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the DllMain function: long __declspec (dllexport) __stdcall Func1(long *pnFirstElement, long lSize) { return 1; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func1 </li> <li>Switch to Visual Basic 6.0.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Object.</li> <li>Double-click Command1 to view the Command1_Click event procedure.</li> <li> 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) </li></ol>

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.

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func1 function: long __declspec (dllexport) __stdcall Func2(SAFEARRAY **ppsaMyArray) { return 2; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func2 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = Func2(MyArrayOfLongs) MsgBox ("Func2 returned" & Result) </li></ol>

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.

back to the top

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.

back to the top

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.

back to the top

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  characters occupies 2*  + 6 bytes of storage:
 * 4 bytes for the value that stores the length of the string
 * 2* bytes for the string
 * 2 bytes for the delimiter

Also, the 4-byte value that stores the length of the string is 2*.

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.

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func2 function: long __declspec (dllexport) __stdcall Func3(char *pcharFirstCharacter, long lSize){ return 3; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func3 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> 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) </li></ol>

back to the top

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.

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func3 function: long __declspec (dllexport) __stdcall Func4(char **ppcharFirstCharacter, long lSize){ return 4; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func4 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = Func4(MyString, 5) MsgBox ("Func4 returned" & Result) </li></ol>

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.

back to the top

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.

back to the top

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: <ol> <li> In a text editor such as Notepad, paste the following code: [ 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); } } </li> <li>On the File menu, click Save. The Save As dialog box appears.</li> <li>In the File name box, type VBptrlib.odl .</li> <li>In the Save as type list, select All Files, and then click Save.</li> <li>Open Command Prompt window.</li> <li>Change the directory path to the location where you saved the VBptrlib.odl file in step 4.</li> <li>Create a type library. To do this, type the following command at the command prompt, and then press ENTER:
 * 1) define RTCALL _stdcall

MIDL VBptrlib.odl

</li> <li>Switch to Visual Basic 6.0.</li> <li>On the Project menu, click References.</li> <li>Locate and then click VarPtrStringArray Support for Visual Basic.</li> <li>Click OK to add a reference the type library that you created in step 7.</li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func4 function: long __declspec (dllexport) __stdcall Func5(wchar_t *pwcharFirstCharacter, long lSize){ return 5; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> On a new line, append the following text to the existing text in the StdDLL.def file: Func5 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = Func5(StrPtr(MyString), 5) MsgBox ("Func5 returned" & Result) </li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func5 function: long __declspec (dllexport) __stdcall Func6(BSTR bstrMyString){ return 6; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func6 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = Func6(StrPtr(MyString)) MsgBox ("Func6 returned" & Result) </li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func6 function: long __declspec (dllexport) __stdcall Func7(BSTR *bstrMyString){ return 7; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> On a new line, append the following text to the existing text in the StdDLL.def file: Func7 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = Func7(VarPtr(MyString)) MsgBox ("Func7 returned" & Result) </li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func7 function: long __declspec (dllexport) __stdcall Func8(SAFEARRAY **ppsaMyArray){ return 8; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func8 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> 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) </li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> 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; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> On a new line, append the following text to the existing text in the StdDLL.def file: Func9 </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> 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) </li></ol>

back to the top

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: <ol> <li>Switch to Visual C++ 6.0.</li> <li>In the File View, right-click StdDLL.cpp, and then click Open.</li> <li> Add the following code after the Func9 function: long __declspec (dllexport) __stdcall Func10(MYSTRUCTURE *pstructMyStruct, long lSize){ return 10; } </li> <li>In the File View, right-click StdDLL.def, and then click Open.</li> <li> Append the following text to the existing text in the StdDLL.def file: Func10 </li> <li>On the Build menu, click Build StdDLL.dll.</li> <li>On the File menu, click Close Workspace.

Note If you receive a message to save files or to close document windows, click Yes.</li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Module1, and then click View Code.</li> <li> 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 </li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> 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) </li></ol>

back to the top

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.

back to the top

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. <ol> <li>In the Class View, expand ATLDLL classes, right-click IObj1, and then click Add Method.</li> <li>In the Method Name box, type ATL_Func1 .</li> <li>In the Parameters box, type the following, and then click OK:

[in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult

</li> <li>In the Class View, right-click IObj1, and then click Go to Definition.</li> <li> In the definition, locate the following code: [id(1), helpstring("method ATL_Func1")] HRESULT ATL_Func1([in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult); </li> <li> 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. </li> <li>In the Class View, expand IObj1, right-click ATL_Func1, and then click Go to Definition. The contents of the Obj1.cpp file appears.</li> <li> Locate the following code: // TODO: Add your implementation code here </li> <li> Paste the following code after the code that you located in step 8: *plResult = 11; </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> 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) </li></ol>

Note Because Visual Basic passes a SAFEARRAY as a pointer to a pointer, use SAFEARRAY ** in the prototype of the Func2 function.

back to the top

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: <ol> <li>Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.</li> <li>In the Method Name box, type ATL_Func2 .</li> <li>In the Parameters box, type the following, and then click OK:

[in] BSTR bstrMyString, [out, retval] long *plResult

</li> <li> In the implementation of the ATL_Func2 function in the Obj1.cpp file, locate the following code: // TODO: Add your implementation code here </li> <li> Paste the following code after the code that you located in step 4: *plResult = 12; </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = MyObj.ATL_Func2(MyString) MsgBox ("ATL_Func2 returned " & Result) </li></ol>

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.

back to the top

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: <ol> <li>Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.</li> <li>In the Method Name box, type ATL_Func3 .</li> <li>In the Parameters box, type the following, and then click OK:

[in, out] BSTR *pbstrMyString, [out, retval] long *plResult

</li> <li> In the implementation of the ATL_Func3 function in the Obj1.cpp file, locate the following code: // TODO: Add your implementation code here </li> <li> Paste the following code after the code that you located in step 4: *plResult = 13; </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = MyObj.ATL_Func3(MyString) MsgBox ("ATL_Func3 returned " & Result) </li></ol>

back to the top

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: <ol> <li>Under ATLDLL classes in the Class View, right-click IObj1, and then click Add Method.</li> <li>In the Method Name box, type ATL_Func4 .</li> <li>In the Parameters box, type the following, and then click OK:

[in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult

</li> <li> In the implementation of the ATL_Func4 function in the Obj1.cpp file, locate the following code: // TODO: Add your implementation code here </li> <li> Paste the following code after the code that you located in step 4: *plResult = 14; </li> <li>Under ATLDLL classes in the Class View, right-click IObj1, and then click Go to Definition.</li> <li> Locate the following code: [id(4), helpstring("method ATL_Func4")] HRESULT ATL_Func4([in, out] SAFEARRAY **ppsaMyArray, [out, retval] long *plResult); </li> <li> 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. </li> <li>Switch to Visual Basic 6.0.</li> <li>In the Project Explorer, right-click Form1, and then click View Code.</li> <li> Append the following code to the existing code in the Command1_Click event procedure: Result = MyObj.ATL_Func4(MyArrayOfStrings) MsgBox ("ATL_Func4 returned " & Result) </li></ol>

back to the top

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.

back to the top

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:

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.

back to the top

Troubleshooting
The following list contains problems that you may experience when you create the sample project that this article uses: <ul> <li> 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: Paste the following code before the code that you located: <li> Symptom
 * 1) include <windows.h>
 * 1) define INC_OLE2 </li>

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); </li> <li>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.</li></ul>

back to the top

<div class="references_section">