If we want to pass strings to a DLL as input or to get strings from a DLL as output and we want to do this in the most general way, we are told from Microsoft to use the
BSTR type. Unfortunately, things are different if we call the DLL from a Visual Basic application or from a Visual C++ one.
In this short article, I'm going to present how to deal with this.
What is a BSTR? (from MSDN documentation)
If we look at the implementation of the
BSTR type, we get the following definition:
typedef wchar_t* BSTR
BSTR type is actually a
typedef definition: a pointer to UNICODE characters.
To understand this, let's look at the following two definitions:
typedef wchar_t* LPWSTR
typedef char* LPSTR
The difference is in the internal representation: a
BSTR contains a
long variable (including the string length) before the start address and an extra null character after the last character of the string.
BSTR to and from a DLL: Visual Basic - Visual C++
Again from the MSDN documentation (in a remote part of it, to be honest !), we read what follows:
- Visual Basic always creates a new
BSTR containing ANSI characters (not UNICODE ones!) when passing a string to a DLL
- Visual Basic always gets a
BSTR containing UNICODE characters when getting a string from a DLL
This can be a problem, from the DLL point of view, as Visual C++ always exports and imports UNICODE strings.
So, the DLL must deal at runtime, with both the cases of input
- If called from a Visual Basic application: input
BSTR contains ANSI characters
- If called from a Visual C++ application: input
BSTR contains UNICODE characters
Luckily enough, the DLL will always export
BSTR with UNICODE characters.
A DLL written in Visual C++ using MFC: the BSTR2CString function
These are two functions exported by the DLL DLL_example.dll, written in Visual C++, using MFC and without defining the
void __declspec(dllexport) __stdcall FunctionWithInputBSTR(BSTR BSTR_str)
CString CString_str = BSTR2CString(BSTR_str);
BSTR __declspec(dllexport) __stdcall FunctionWithOutputBSTR()
CString CString_str = _T("");
This is the corresponding file DLL_example.def.
DESCRIPTION 'DLL_example Windows Dynamic Link Library'
; Explicit exports can go here
As already mentioned, how to build the output
BSTR from a
CString is invariant:
BSTR2CString(BSTR_str) deals with different kinds of input
static CString BSTR2CString(BSTR BSTR_str)
CString CString_str = _T("");
if (BSTR_str != NULL)
LPSTR p = s.GetBuffer(::SysStringLen(BSTR_str) + 1);
::WideCharToMultiByte(CP_ACP, 0, BSTR_str, -1,
CString_str = (LPCTSTR)BSTR_str;
CString_str = (LPCWSTR)BSTR_str;
As it can be seen, the only thing to do is to try to convert the input
BSTR_str from UNICODE to ANSI calling the function
::WideCharToMultiByte and recording in the flag
UsedDefaultChar if some UNICODE character in
BSTR_str cannot be represented in ANSI.
::WideCharToMultiByte supposes that
BSTR_str contains UNICODE characters: if this is not so, a system-defined default character will be used to fill the output string (pointed by
LPSTR p) and
UsedDefaultChar will be set to
So, depending on the value of the flag
UsedDefaultChar, the corresponding conversion is performed.
Calling the DLL from Visual Basic: example
Let's suppose we have a form with a
The declaration of the two functions are:
Private Declare Sub FunctionWithInputBSTR Lib _
"DLL_example" (ByVal str As String)
Private Declare Function FunctionWithOutputBSTR Lib _
"DLL_example" () As String
and this is a sample of how to use them:
Dim str as String
str = "Input String"
str = StrConv(FunctionWithOutputBSTR(), vbFromUnicode)
Please note that, once having gotten the string from the DLL, a conversion from UNICODE to ANSI is needed to correctly show it in the
Calling the DLL from Visual C++: example of an MFC application
As in the previous example,
m_List1 is a
This is an example of how to load the DLL and call the two functions:
typedef void (WINAPI* ptr_func1)(BSTR bstr);
typedef BSTR (WINAPI* ptr_func2)(void);
ptr_func1 FunctionWithInputBSTR = NULL;
ptr_func2 FunctionWithOutputBSTR = NULL;
hLib = LoadLibrary(_T("DLL_example"));
if (hLib == NULL)
MessageBox(_T("Unable to load .dll"), NULL, MB_ICONERROR);
FunctionWithInputBSTR = (ptr_func1)GetProcAddress(hLib,
FunctionWithOutputBSTR = (ptr_func2)GetProcAddress(hLib,
str = _T("Input String");
bstr = str.AllocSysString();
bstr = FunctionWithOutputBSTR();
str = CString(bstr);
... and that's it!
I hope that someone will find this article useful... see you!
31/07/03 - First issue.