Thread: C++ console application works; Excel crashes when using the same code.

  1. #31
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Compilers will decorate function names based on linkage, calling convention, and whatever else (it's all compiler dependent) - Stdcall and DLL tools of MSVC and MinGW

    VB[A] wants stdcall but with no decoration (same as most all Win32 API DLL's). The normal way to achieve that is with a DEF file. "--add-stdcall-alias" allows you to skip using a DEF file.

    >> Now I'm going back to the old BSTR problem
    Does the code from post #11 work?

    gg

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

    Thumbs up

    >> Now I'm going back to the old BSTR problem
    Does the code from post #11 work?
    Yes and no: it does compile well with no errors, but, when I call the function from Excel like this =string_ex("Lucas") I can only see "L" as a result.

    The returned string is being stripped: I do not know if the DLL is sending back only one char or whether VBA is stripping the received string ...

    I'll keep on searching ... thanks!

  3. #33
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Quote Originally Posted by MSDN
    ... Functions should return types that VBA can recognize ByVal. This means that some types are more easily returned by modifying arguments in place: strings, arrays, user-defined types, and objects.
    ...
    To return a byte string to VBA from a DLL, you should modify a byte-string BSTR argument in place. For this to work, you must declare the DLL function as taking a pointer to a pointer to the BSTR and in your C/C++ code, and declare the argument in the VBA code as ‘ByRef varg As String’.

    You should only handle strings that are passed in these ways from VBA using the OLE BSTR string functions to avoid memory-related problems.
    So MSDN prefers that you return string by reference. But I see plenty of examples returning BSTR's to VB[A] (like this:Financial applications using Excel ... - Google Books)

    I may of incorrectly assumed that the given BSTR will be null-terminated. Try out these functions and see what you get:
    Code:
    BSTR WINAPI string_test1()
    {
        const char *s = "Testing 123";
        return SysAllocStringByteLen(s, strlen(s));
    }
    
    BSTR WINAPI string_test2(BSTR stringVar)
    {
        const size_t len = SysStringByteLen(stringVar);
        const char *begin = (const char*)stringVar;
        const char *end = begin + len;
        
        std::string buffer(begin, end);
    	
        return SysAllocStringByteLen(buffer.c_str(), len);
    }
    gg

  4. #34
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Actually, I've tried this as well ...

    Code:
    BSTR string_ex(BSTR stringVar)
    {
        string buffer((char*)stringVar);
        // code. here we work with 'buffer'. once done
        // copy its content to 'out_string'
        string out_string=buffer;
    
        // conversion from std::string to LPCSTR
        int str_len = out_string.length();
        LPCSTR lpcstr_out_string;
        lpcstr_out_string=out_string.c_str();
    
    	return SysAllocStringByteLen(out_string.c_str(),str_len);
    
    }
    It tried to use a LPCSTR string since the first parameter of the function SysAllocStringByteLen is LPCSTR ... but the result is the same: I always get the first char of the string .... :-S

    rgds!

    lucas

  5. #35
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by laimaretto View Post
    Actually, I've tried this as well ...

    Code:
    BSTR string_ex(BSTR stringVar)
    {
        string buffer((char*)stringVar);
        // code. here we work with 'buffer'. once done
        // copy its content to 'out_string'
        string out_string=buffer;
    
        // conversion from std::string to LPCSTR
        int str_len = out_string.length();
        LPCSTR lpcstr_out_string;
        lpcstr_out_string=out_string.c_str();
    
    	return SysAllocStringByteLen(out_string.c_str(),str_len);
    
    }
    It tried to use a LPCSTR string since the first parameter of the function SysAllocStringByteLen is LPCSTR ... but the result is the same: I always get the first char of the string .... :-S

    rgds!

    lucas
    Well, then start with the easy stuff.... is out_string.length() what you expect it to be? For I am suspicious of the (char *) cast at the beginning, as a BSTR starts with the size of the string, right? So you'll probably have one byte of ... something, then some zeros, which would kill your char*-style-string dead. EDIT: No, I'm wrong, the pointer points to the beginning of the data. BUT BSTRs are also Unicode, or at least MSDN says "May contain multiple embedded null characters" which would be bad.
    Last edited by tabstop; 12-16-2010 at 12:01 PM.

  6. #36
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    string_test1() only shows a "T"?
    Are you using .TLB to import the functions on the VBA side?
    Are you using "Variant" as the string type on the VBA side?

    >> string buffer((char*)stringVar);
    This assumes that stringVar is a NULL terminated C-string. I do not know if that is a correct assumption.

    gg

  7. #37
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    How to: Convert Between Various String Types

    If you have access to CComBSTR I would use that. And read about the conversions
    Woop?

  8. #38
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Quote Originally Posted by Codeplug View Post
    string_test1() only shows a "T"?
    No, it does not. This is the output:

    =string_test1(): 敔瑳湩⁧㈱3
    =string_test2("Lucas"): Lucas

    string_test1 returns a string longer than 1 char, but not "Testing123" as you have put in the code of the function.

    string_test2 is working fine ... I need to study your code to fully understand what is it that you've done ...

    Thanks in advance ... I've learnt many things throughout this post ...

  9. #39
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    I don't what it looks like to you, but your results from string_test1 have (for me, in Firefox) one unprintable character, which Firefox shows in a box: 2067. 0x20 is ' ', and 0x67 is 'g', which is very interesting indeed. SysAllocWhateverWhatever takes a LPC pointer, so you may need to change things to wide pointers or whatever MS calls them.

  10. #40
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Excel is not treating your returned BSTR as an ANSI string. The UTF16-LE form of "敔瑳湩⁧㈱3" comes out as "Testing 123" when you just look at the bytes.

    string_test2() is basically doing a "memcpy" of the original string. So Ansi or Unicode, you get the same result.
    Quote Originally Posted by Codeplug View Post
    Are you using .TLB to import the functions on the VBA side?
    Are you using "Variant" as the string type on the VBA side?
    If the answer is yes to either, then you will get Unicode BSTR's - not Ansi.

    It almost seems like the BSTR's you're receiving are Unicode BSTR's. And the BSTR you return is interpreted as a Unicode BSTR. Can you explain exactly how you are calling and and displaying the results in Excel? And what version of Excel? And answer the two quoted questions.

    gg

  11. #41
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I took a look at a project like this that I've done myself a year ago.
    I got it working by passing a string to VBA via a callback function.
    How did I do it?

    I had a prototype that looked like:
    typedef void* (__stdcall StrSetRangeFncPtr)(Worksheet Sheet, VBString Addr, VBString What);
    Which in VBA was:
    Sub StrSetRange(ByVal Sheet As Worksheet, ByVal Addr As String, ByVal What As String)

    Where VBString = BSTR.
    It was called like so:

    g_StrSetRange(Sheet, CComVBString(Cell/*.c_str()*/), str);
    Where Cell is a string (type: const char*).

    Hope that helps in some way.
    Oh, and CComVBString = CComBSTR.

    I also checked what ATL did, and it basically converted the ANSI string to Unicode with

    nResult = MultiByteToWideChar(_acp_ex, 0, lp, nLen, str, nConvertedLen);

    where str is of type BSTR and

    str = ::SysAllocStringLen(NULL, nAllocLen);

    _acp_ex is some weird stuff I don't know.
    Last edited by Elysia; 12-16-2010 at 04:18 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #42
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I think I found the "problem" - once I had time to play with Excel myself.
    Code:
    Public Declare Function lstrlenA Lib "kernel32.dll" (ByVal s As String) As Long
    
    Function lstrlenA_wrap(s As String) As Long
        lstrlenA_wrap = lstrlenA(s)
    End Function
    Quote Originally Posted by Excel 2007 Worksheet
    =lstrlenA("ABC") evaluates to 1
    =lstrlenA_wrap("ABC") evaluates to 3
    So the lesson learned is: Calling directly from the worksheet always uses Unicode BSTR's. I haven't see that documented anywhere...

    >> I got it working by passing a string to VBA via a callback function.
    If it's marshaled vial COM, then BSTR's are always Unicode.

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Not sure how this simple code works
    By metros in forum C Programming
    Replies: 8
    Last Post: 02-09-2010, 06:22 PM
  2. Subject:How to create a C# console application?
    By Adock in forum C# Programming
    Replies: 5
    Last Post: 09-03-2008, 05:58 PM
  3. Why my code crashes
    By ICool in forum C Programming
    Replies: 19
    Last Post: 09-08-2007, 09:03 AM
  4. Problem w/ console application compadibility
    By DarkMortar in forum C Programming
    Replies: 2
    Last Post: 05-16-2006, 07:13 PM
  5. making a stealthy win32 console application?
    By killdragon in forum C++ Programming
    Replies: 3
    Last Post: 09-08-2004, 02:50 PM

Tags for this Thread