Click here to Skip to main content
15,881,248 members
Articles / Desktop Programming / MFC
Article

A way for a MFC DLL to deal, at runtime, with BSTR from Visual Basic and from Visual C++

Rate me:
Please Sign up or sign in to vote.
4.88/5 (20 votes)
30 Jul 20033 min read 103.2K   34   13
Correct input/output of strings to/from a DLL written in VC++, using BSTR type.

Introduction

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

So, the 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:

  1. Visual Basic always creates a new BSTR containing ANSI characters (not UNICODE ones!) when passing a string to a DLL
  2. 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 BSTR:

  1. If called from a Visual Basic application: input BSTR contains ANSI characters
  2. 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 _UNICODE symbol:

void __declspec(dllexport) __stdcall FunctionWithInputBSTR(BSTR BSTR_str)
{
    // Mandatory instruction in a MFC DLL
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // Convertion function: see below
    CString CString_str = BSTR2CString(BSTR_str);

    // Code using input CString_str
}

BSTR __declspec(dllexport) __stdcall FunctionWithOutputBSTR()
{
    // Mandatory instruction in a MFC DLL
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    CString CString_str = _T("");
    
    // Code defining output CString_str

    return CString_str.AllocSysString();
}

This is the corresponding file DLL_example.def.

LIBRARY      "DLL_example"
DESCRIPTION  'DLL_example Windows Dynamic Link Library'

EXPORTS
    ; Explicit exports can go here
    FunctionWithInputBSTR  @1 
    FunctionWithOutputBSTR @2

As already mentioned, how to build the output BSTR from a CString is invariant: return CString_str.AllocSysString();

The function BSTR2CString(BSTR_str) deals with different kinds of input BSTR:

static CString BSTR2CString(BSTR BSTR_str)
{
    CString CString_str = _T("");
    
    if (BSTR_str != NULL)    // To be sure that input string is valid...
    {
        CString s;
        LPSTR   p = s.GetBuffer(::SysStringLen(BSTR_str) + 1);
        BOOL    UsedDefaultChar;
        
        ::WideCharToMultiByte(CP_ACP, 0, BSTR_str, -1, 
                p, ::SysStringLen(BSTR_str)+1, 
                NULL, &UsedDefaultChar);

        if (UsedDefaultChar)
            // BSTR_str contains an ANSI string
            CString_str = (LPCTSTR)BSTR_str;
        else
            // BSTR_str contains an UNICODE string
           CString_str = (LPCWSTR)BSTR_str;
    }

    return CString_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.

In fact, ::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 TRUE.

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 ListBox, List1.

The declaration of the two functions are:

VB
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:

VB
Dim str as String

str = "Input String"

Call FunctionWithInputBSTR(str)
      
str = StrConv(FunctionWithOutputBSTR(), vbFromUnicode)

List1.AddItem (str)

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 ListBox.

Calling the DLL from Visual C++: example of an MFC application

As in the previous example, m_List1 is a ListBox.

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;

HINSTANCE hLib;
    
hLib = LoadLibrary(_T("DLL_example"));

if (hLib == NULL)
{
    MessageBox(_T("Unable to load .dll"), NULL, MB_ICONERROR);
}
else
{
    FunctionWithInputBSTR = (ptr_func1)GetProcAddress(hLib, 
                                 _T("FunctionWithInputBSTR"));
    FunctionWithOutputBSTR = (ptr_func2)GetProcAddress(hLib, 
                                 _T("FunctionWithOutputBSTR"));

    BSTR    bstr;
    CString str;
    
    // CString => BSTR conversion and call to function
    str = _T("Input String");
    bstr = str.AllocSysString();

    FunctionWithInputBSTR(bstr);

    // Call to function and BSTR => CString conversion
    bstr = FunctionWithOutputBSTR();
    str = CString(bstr);

    // Add the CString to the ListBox
    m_List1.AddString((LPCTSTR)str);
    
    FreeLibrary(hLib);
}

... and that's it!

I hope that someone will find this article useful... see you!

History

31/07/03 - First issue.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
I'm a SW engineer and I've been programming since I was a child.
I'm interested in music, science fiction, fantasy.
I play the electric, acoustic and classical guitar: I like rock, blues and (why not?) classical music.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Krishna200022-Aug-11 19:31
Krishna200022-Aug-11 19:31 
QuestionCString Where is it definied in VC++? Pin
Sachs30-Aug-05 3:42
Sachs30-Aug-05 3:42 
AnswerRe: CString Where is it definied in VC++? Pin
Marcello Bonino30-Aug-05 19:36
Marcello Bonino30-Aug-05 19:36 
GeneralBSTR* Pin
Manikandan20-Apr-05 5:18
Manikandan20-Apr-05 5:18 
GeneralRe: BSTR* Pin
Marcello Bonino20-Apr-05 19:27
Marcello Bonino20-Apr-05 19:27 
GeneralSome Additions regarding VB Pin
aule6-Aug-03 1:54
aule6-Aug-03 1:54 
GeneralWhy not... Pin
Brian van der Beek31-Jul-03 8:54
Brian van der Beek31-Jul-03 8:54 
GeneralBSTR2CString(BSTR bstr) Pin
Anonymous31-Jul-03 8:11
Anonymous31-Jul-03 8:11 
GeneralRe: BSTR2CString(BSTR bstr) Pin
Anonymous4-Aug-03 8:25
Anonymous4-Aug-03 8:25 
GeneralRe: BSTR2CString(BSTR bstr) Pin
Anonymous4-Aug-03 8:43
Anonymous4-Aug-03 8:43 
GeneralRe: BSTR2CString(BSTR bstr) Pin
Hugo Hallman4-Aug-03 9:45
Hugo Hallman4-Aug-03 9:45 
GeneralRe: BSTR2CString(BSTR bstr) Pin
Anonymous7-Aug-03 7:47
Anonymous7-Aug-03 7:47 
GeneralRe: BSTR2CString(BSTR bstr) Pin
Hugo Hallman8-Aug-03 5:27
Hugo Hallman8-Aug-03 5:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.