Thread: Cannot convert two digit-long integer to std::string

  1. #1
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27

    Question Cannot convert two digit-long integer to std::string

    Hi everyone:

    I'm dealing with some DLLs that I'm trying to call from Excel.

    The issue is that, depending on how I call from VBA the DLL then it works or it does not:

    Here's the c++ code:

    Code:
    BSTR test_int2str(double x)
    {
        string mystring;
        ostringstream s;
        s << x;
        mystring=s.str();
        return string2bstr(mystring);
    }
    
    BSTR string2bstr(string input_string)
    {
        const size_t len = input_string.length();
        return SysAllocStringByteLen(input_string.c_str(), len);
    }
    And here's the VBA code:

    Code:
    Declare Function test_int2str Lib _
        "d:\datos\doc\tech\c++\excel\ipfnc_dll\ipfnc.dll" _
        (ByVal x As Double) As String
    
    Function test_int2str_wrap(x As Double) As String
        
        Dim test_string As String
        test_string = test_int2str(x)
        test_int2str_wrap = test_string
    
    End Function
    The thing is:

    If I do in Excel ...

    =test_int2str(1) => result="1"
    =test_int2str(9) => result="9"
    =test_int2str(10) => result="〱"
    =test_int2str(11) => result "ㄱ"
    =test_int2str(109) => result="〱9"
    =test_int2str(111) => result="ㄱ1"

    So, you see, it's a curious behavior. It does work well with one-digit-long numbers, but it does not with longer ones ...

    However, if I do call the 2nd function in VBA (test_int2str_wrap) everything works just perfect ...

    =test_int2str(111) => result="111" ...

    I know the 2nd way works, but I'd like to skip that way: I'd like to use VBA as less as possible...

    Any ideas?

    Thanks!

    Lucas

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    This whole 16/32 ANSI/Unicode thing seems to be a recurring problem.

    1) Have you tried using SysAllocString Function instead?

    2) Have you tried using std::wstring instead of std::string?

    EDIT: You know, those are really bad suggestions, now that I've tried them. When I run this code:
    Code:
    #include <string>
    #include <sstream>
    #include <oleauto.h>
    #include <iostream>
    
    using namespace std;
    
    BSTR string2bstr(wstring input_string)
    {
        const size_t len = input_string.length();
        return SysAllocStringByteLen(input_string.c_str(), len);
    }
    
    BSTR test_int2str(double x)
    {
        wstring mystring;
        ostringstream s;
        s << x;
        mystring=s.str();
        return string2bstr(mystring);
    }
    
    int main(void) {
        BSTR foo = test_int2str(27);
        cout << SysStringLen(foo) << endl;
        cout << "In bytes: " << SysStringByteLen(foo) << endl;
        return 0;
    }
    I get the length of foo is 1 (in bytes: gives 2). So there's a conversion that is (or isn't, I suppose, depending on your point of view) happening.
    Last edited by tabstop; 12-22-2010 at 11:13 AM.

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Okay. What happens if you try this?
    Code:
    BSTR string2bstr(string input_string)
    {
        int len2 = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(),
            -1, NULL, 0);
        LPWSTR buffer = (LPWSTR)malloc(len2 * sizeof(*buffer) );
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(), -1, buffer,
            len2);
        return SysAllocString(buffer);
    }

  4. #4
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    You do realise this sort of thing is the bread and butter of VBA?

    Code:
    Function test_int2str_wrap(x As Double) As String
        
        Dim test_string As String
        test_string = CStr(x)
        test_int2str_wrap = test_string
    
    End Function

  5. #5
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Quote Originally Posted by adeyblue View Post
    You do realise this sort of thing is the bread and butter of VBA?

    Code:
    Function test_int2str_wrap(x As Double) As String
        
        Dim test_string As String
        test_string = CStr(x)
        test_int2str_wrap = test_string
    
    End Function
    Hi, thanks ... and yes, I do realize, the thing is I want to do it via a C++ DLL ... :-)

    Cheers!

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> However, if I do call the 2nd function in VBA (test_int2str_wrap) everything works just perfect ...
    Yes - this is what we discovered at the end of your previous thread: http://cboard.cprogramming.com/windo...tml#post990769

    If you want to be able to call your DLL directly from the cell or from VBA, then you have to use Unicode strings in your interface. That means you have to use Variant instead of String.

    gg

  7. #7
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Quote Originally Posted by Codeplug View Post
    >> However, if I do call the 2nd function in VBA (test_int2str_wrap) everything works just perfect ...
    Yes - this is what we discovered at the end of your previous thread: http://cboard.cprogramming.com/windo...tml#post990769

    If you want to be able to call your DLL directly from the cell or from VBA, then you have to use Unicode strings in your interface. That means you have to use Variant instead of String.

    gg
    Ok, just to clarify:

    0) I understand that wide-char=two-byte per char=Unicode while char=one-byte per char=ANSI. Please, correct me if I'm wrong.

    1) A VBA Variant that contains a string is passed as a pointer to a Unicode wide-character string BSTR structure when passed ByVal, and as a pointer to a pointer when passed ByRef. How to: Access DLLs in Excel ( How to: Access DLLs in Excel ). This means that the VBA function needs to be declared as follows:

    Code:
    Public Declare Function string_test2 Lib _
        "d:\datos\doc\tech\c++\excel\ipfnc_dll\ipfnc.dll" _
        (ByVal stringVar As Variant) As Variant
    So, output parameter is Variant (two-byte per char) and the function expects a Variant as return (two-byte per char);

    This makes me think that if I declare the function as follows ...

    Code:
    Public Declare Function string_test2 Lib _
        "d:\datos\doc\tech\c++\excel\ipfnc_dll\ipfnc.dll" _
        (ByVal stringVar As Variant) As Variant
    ... then the function is sending output parameter as string (one-byte per char) and also expects a string (one-byte per char).

    2) In C++, what is the type that matches a Variant in VBA? Is still the BSTR or not? Apparently not, because my previous functions to convert from bstr2string and viceversa are no longer working if I declare the function in VBA to use Variant... These were my functions:

    Code:
    BSTR string2bstr(string input_string)
    {
        const size_t len = input_string.length();
        return SysAllocStringByteLen(input_string.c_str(), len);
        
    }
    
    string bstr2string(BSTR input_string)
    {
        const size_t len = SysStringByteLen(input_string);
        if (len!=0)
        {
            const char *begin = (const char*)input_string;
            const char *end = begin + len;
            std::string buffer(begin, end);
            return buffer;
        }
        else
        {
            std::string buffer ("Error");
            return buffer;
        }
    }
    The previous functions were working wonderfully if I would use this..

    Code:
    BSTR string_test2(BSTR stringVar)
    {
        string buffer;
        buffer=bstr2string(stringVar);
    
        buffer=buffer + buffer;
    
        return string2bstr(buffer);
    }
    That would do the following in Excel ... =string_test2("Lucas")=LucasLucas.

    If using Variant these two functions no longer work ... and I'm loosing focus.

    3) So, what to use in Excel, Variants or Strings? What's better, considering that, the output parameter being sent out from VBA must be used then within the C++ DLL to operate then with it. And, finally, the operations with this string must be sent back to VBA ... ppfff ...

    Ok, very sorry for this long post ...

    thanks!

    Lucas

  8. #8
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Quote Originally Posted by tabstop View Post
    Okay. What happens if you try this?
    Code:
    BSTR string2bstr(string input_string)
    {
        int len2 = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(),
            -1, NULL, 0);
        LPWSTR buffer = (LPWSTR)malloc(len2 * sizeof(*buffer) );
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(), -1, buffer,
            len2);
        return SysAllocString(buffer);
    }
    Hi tabstop, this function works nice. I have some questions though:

    1) How did you know that you had to use the MultiByteToWideChar function? I assume by this then that VBA expects two-byte-per-char strings then...

    2) Why is not working my previous function string2bstr?

    3) I'm getting very confused with the following

    3.1) ANSI/Unicode
    3.2) two-bytes vs one-byte char
    3.3) I do not know who is sending what and who is expecting which kind of type
    3.4) If I'm working within C++, then I should pick up one type, operate inside the DLL and finally convert that to the type that VBA is expecting. Which should be the "interface type", isn't it?

    4) In my project have basically two types of functions that will be called from VBA:
    4.1) function1 (x as double) as string)
    4.2) function2 (y as string) as string)
    4.3) I assume then that, if I'm always expecting back a string, then, within the C++ DLL, the function string2bstr will be the same, isn't it?

    Well, thank you very much. I think I have a big confusion and I need to clear up my ideas ...

    Regards,

    Lucas

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by laimaretto View Post
    Hi tabstop, this function works nice. I have some questions though:

    1) How did you know that you had to use the MultiByteToWideChar function? I assume by this then that VBA expects two-byte-per-char strings then...
    I read the manual: String Manipulation Functions [Automation]

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    0) In Windows, a wchar_t is a UTF16LE encoded Unicode codepoint. It can take one or more codepoints to make a glyph ("character"). "Ansi" in Windows implies a codepage. There are single-byte and multi-byte codepages. Under a multi-byte codepage, it may take one or two bytes for a single glyph.

    1),2) A VBA Variant in C/C++ is a VARIANT. There are COM API's just for dealing with them: http://msdn.microsoft.com/en-us/library/ms221673.aspx
    There are also nice C++ wrapper classes, available only to MSVC (including Express versions): http://msdn.microsoft.com/en-us/library/5yb2sfxk.aspx

    >>That would do the following in Excel ... =string_test2("Lucas")=Lucas.
    When invoked directly from a cell, a String/BSTR is always a Unicode string. The only reason your code seemed to work is because you got the byte length and made a copy of that many bytes. In this case the byte length is 10.

    3) When you pass a String/BSTR directly from a cell, it's always a Unicode string (wchar_t's inside the BSTR).
    When you pass a String/BSTR from VBA, it's always an Ansi string (char's inside the BSTR). So if you want your C/C++ code to work the same regardless of where it's invoked: directly from a cell or from VBA code - then you have no choice but to use Variant/VARIANT.
    (Edit: If you are using COM to invoke your calls, ie. not using a Declare statement, then String/BSTR calls are always Unicode in this case.)

    Sample:
    Code:
    #include <sstream>
    #include <iomanip>
    using namespace std;
    
    #ifdef _MSC_VER
    #   include <comutil.h>
    #   include <comdef.h>
    #   pragma comment(lib, "comsuppw.lib")
    #else
    #   error Sorry, MSVC only
    #endif 
    
    void get_com_error_string(const _com_error &err, _bstr_t &errstr);
    
    DLL_API VARIANT __stdcall my_test(VARIANT s)
    {
        _bstr_t bs;
        try
        {
            bs = L"Hello World: ";
            bs += _bstr_t(_variant_t(s)); 
        }//try
        catch (const _com_error &err)
        {
            get_com_error_string(err, bs);
            // we failed to convert the variant to a string, log the VARTYPE too
            wostringstream serr;
            serr << L", VARTYPE=" << V_VT(&s);
            bs += serr.str().c_str();
        }//catch 
    
        _variant_t ret = bs;
        return ret.Detach();
    }//my_test
    
    void get_com_error_string(const _com_error &err, _bstr_t &errstr)
    {
        wostringstream serr;
        serr << L"COM error : " << hex << setw(8) << err.Error();
        _bstr_t bstrSrc = err.Source();
        const wchar_t *src = (const wchar_t*)bstrSrc;
        if (src)
            serr << L", src=[" << src << L"]";
            
        _bstr_t bstrDesc = err.Description();
        const wchar_t *desc = (const wchar_t*)bstrDesc;
        if (desc)
            serr << L", desc=[" << desc << L"]";
    
        const wchar_t *errmsg = err.ErrorMessage();
        if (errmsg)
            serr << L", msg=[" << errmsg << L"]";
    
        errstr = serr.str().c_str();
    }//get_com_error_string
    The other advantage of passing Variant/VARIANT is that your cell/VBA calls can pass any of the basic data types and COM will automatically convert them to string for you (in the _variant_t -> _bstr_t conversion). Not all Variant types can be converted to a string. That's when the _com_error catch code kicks in.

    gg

  11. #11
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Hi All,

    After searching for info, I came out with two functions to come and go from BSTR to std::string and vice versa...

    The functions work fine for me and I can call them directly from a Cell in Excel, operate then inside the DLL and return the result as BSTR to Excel; Excel will correctly interpret the received string.

    This is how I declare the function under VBA:

    Code:
    Declare Function string_test Lib _
        "d:\datos\doc\tech\c++\excel\ipfnc_dll\ipfnc.dll" _
        (ByVal mystring As String) As String
    This is my function within C++ to convert from BSTR to std::string:

    Code:
    string bstr2string(BSTR input_string)
    {
        DWORD len, bstrLength;
        size_t result;
    
        // +1 for NULL teminator
        // bstrLength is measured in Chars
        bstrLength = SysStringLen(input_string)+1;
        // len is measured in Bytes
        len = WideCharToMultiByte(CP_ACP, 0, input_string, bstrLength, 0, 0, 0, 0);
    
        if (len>0)
        {
            char *buffer = new char[len];
            result = WideCharToMultiByte(CP_ACP, 0, input_string, bstrLength,buffer,len, 0, 0);
            return buffer;
        }
    
    }
    And this is the one to convert from std::string to BSTR:

    Code:
    BSTR string2bstr(string input_string)
    {
        // MultyBytetoWideChar converts ANSI strings to UNICODE
        // int len2 is the required size in wide chars
        // it includes the NULL char at the end. So it's "len"+1
        // The codepage CP_ACP -AnsiCodePage- is used by Windows (Excel)
        //  When using 'As string' in VBA causes autmatically UNICODE->ANSI conversion.
        int len2 = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(),-1, NULL, 0);
    
        // malloc returns a pointer to the begining of the buffer
        // buffer size in memory is (len2*sizeof(*buffer))
        // 5th parameter of MultiByteToWideChar is LPWSTR
        // 1st parameter= CP_ACP = ANSI Code Page
        LPWSTR buffer = (LPWSTR)malloc(len2 * sizeof(*buffer));
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, input_string.c_str(), -1, buffer,len2);
    
        // Allocates a new string and copies the passed string into it.
        // The input_parameter must be a Unicode string in 32-bit applications,
        // and an ANSI string in 16-bit applications. The input parameter may be NULL.
        return SysAllocString(buffer);
    }
    Any improvement is welcome ... but so far they are working ...

    Regards,

    Lucas

    ps: Actually, the function BSTR string2bstr(string input_string) was given to my by tabstop (thanks for this!).

  12. #12
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    string2bstr is leaking memory. You need to free the buffer before returning from the function.
    bit∙hub [bit-huhb] n. A source and destination for information.

  13. #13
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    They both leak, also what happens in bstrtostring if len is less than or equal to 0?

  14. #14
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Generic conversion code I've posted in the past:
    Code:
    std::wstring str_to_wstr(const std::string &str, UINT cp = CP_ACP)
    {
        int len = MultiByteToWideChar(cp, 0, str.c_str(), (int)str.length(), 0, 0);
        if (!len)
            return L"ErrorA2W";
        
        std::vector<wchar_t> wbuff(len + 1);
        // NOTE: this does not NULL terminate the string in wbuff, but this is ok
        //       since it was zero-initialized in the vector constructor
        if (!MultiByteToWideChar(cp, 0, str.c_str(), (int)str.length(), &wbuff[0], len))
            return L"ErrorA2W";
    
        return &wbuff[0];
    }//str_to_wstr
    
    std::string wstr_to_str(const std::wstring &wstr, UINT cp = CP_ACP)
    {
        int len = WideCharToMultiByte(cp, 0, wstr.c_str(), (int)wstr.length(), 
                                      0, 0, 0, 0);
        if (!len)
            return "ErrorW2A";
    
        std::vector<char> abuff(len + 1);
    
        // NOTE: this does not NULL terminate the string in abuff, but this is ok
        //       since it was zero-initialized in the vector constructor
        if (!WideCharToMultiByte(cp, 0, wstr.c_str(), (int)wstr.length(), 
                                 &abuff[0], len, 0, 0))
        {
            return "ErrorW2A";
        }//if
    
        return &abuff[0];
    }//wstr_to_str
    >> The functions work fine for me and I can call them directly from a Cell in Excel...
    And hopefully no one will call it from VBA and break it.

    gg

  15. #15
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    @adeyblue/bithub:

    Sorry if this question is somehow silly or generic but: how do you know or what did you do to see/test/findout that the functions are leaking memory? How do I prevent this memory leak?

    @codeplug:

    I've seen your code before, but since I'm a newbie in C++, I did not find it simple, at least for me (and I just wanted to work out the solution on my own ... je).

    In any case, the thing I like about mine is that there is only one input parameter in each function. Could you elaborate a little bit more on your proposal? Why are you using std:wstring in the function str_to_wstr?

    On the other hand ...

    >> The functions work fine for me and I can call them directly from a Cell in Excel...
    And hopefully no one will call it from VBA and break it.
    ... of course. This functions only will work if you call them directly from a Cell in Excel.

    Thanks and best regards,

    Lucas

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Error in Exceptions.h file during build of QuickFix library
    By arupsarkar in forum C++ Programming
    Replies: 3
    Last Post: 07-16-2010, 10:30 AM
  2. Need help with Realloc()
    By krazyxazn in forum C Programming
    Replies: 10
    Last Post: 01-27-2010, 10:05 PM
  3. Inserting a swf file in a windows application
    By face_master in forum Windows Programming
    Replies: 12
    Last Post: 05-03-2009, 11:29 AM
  4. Link List math
    By t014y in forum C Programming
    Replies: 17
    Last Post: 02-20-2009, 06:55 PM
  5. need help
    By emperor in forum C Programming
    Replies: 1
    Last Post: 03-04-2002, 12:26 PM