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

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

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

    Hi all,

    Greetings! this is my first post to the forum.

    My question is the following. I have a c++ code which, after being compiled as a console application (I'm using DEV-C++ 4.9.9.2), the code does what I expect it to do.

    However, if I compile the same code as a DLL and after that I call it from within Excel (using Excel 2007), then it will crash.

    I have no problem in using DLLs from within Excel; I've done that before and the work as a charm. The problem arises with this code and I cannot figure out what is wrong.

    I want to bring up this: if compiled as a console application it works; if compiled as a DLL and called from Excel, Excel crashes.

    Here's the code

    Code:
    #include <windows.h>
    #include <string>
    
    using namespace std;
    
    extern "C" __declspec( dllexport ) __stdcall string str_separator(string in_string, string sep_char, int id);
    
    string str_separator(string in_string, string sep_char, int id)
    {
         int str_len=0,pos_sep_char=0,q=0,k=0;
         // temporal strings declared as 'string' types.
         string temp_str ("test_str");
         string out_str ("out_str");
         string temp_char ("0");
         
         temp_str = in_string;
         str_len=temp_str.length();
         
         for(k=0;k<=str_len-1;k++)
         {
               // here we get the kth char from the string to compare it after
               temp_char = temp_str.substr(k,1);
               
               if (temp_char==sep_char) // is the gotten char the one I'm expecting?
               {
                    // I got sep_char within the string.
                    q=q+1;
                    
                    if (id==q)
                    {k=str_len;}
                    else 
                    {
                         temp_str = temp_str.substr(k+1,temp_str.length()-k);
                         str_len=temp_str.length();
                         k=-1;
                    }
               }
               else if (temp_char != sep_char) // gotten char is not the one
               {
                    if (k==0)
                    {out_str = temp_char;}
                    else
                    {out_str += temp_char;}
               }
         }
         
         return out_str;
    }
    Any hint is highly appreciated.

    Best regards,

    Lucas

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I don't believe Excel uses std::string, but rather some nuisance BSTR or something. You have to change the parameters you accept and return.
    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.

  3. #3
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Quote Originally Posted by Elysia View Post
    I don't believe Excel uses std::string, but rather some nuisance BSTR or something. You have to change the parameters you accept and return.
    Hi Elysa, thanks for replying.

    I'm no c++ expert, so I don't think I've completely understood what you've said.

    Are you saying that Excel does not like the "string" type for input parameters? If so, would I have to use the "char" type for working with strings?

    If that's true, oh well, I'll have to do a lot more work. I really liked the string library in order to work in a really easy way with strings ... :-(

    Isn't there any other way of using "strings" as input parameters with Excel?

    Cheers!

    Lucas

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by laimaretto View Post
    Are you saying that Excel does not like the "string" type for input parameters?
    Exactly. A C++ string is not the same thing as an Excel string. When you call the function from Excel, it believes the function is accepting a Excel string and not a C++ string.

    If so, would I have to use the "char" type for working with strings?
    No, you need to use a specific Excel-type string. What that is exactly, I forgot, though I believe it's called BSTR. But perhaps I can look it up tonight if you don't find any info using Google first.

    If that's true, oh well, I'll have to do a lot more work. I really liked the string library in order to work in a really easy way with strings ... :-(
    Not really. You can convert it to a C++ string and vice versa. If you have ATL, I even believe there is a nice BSTR wrapper to work with BSTR strings.

    Isn't there any other way of using "strings" as input parameters with Excel?
    Likely, but I can't think of any. Or at least how to make them work.
    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.

  5. #5

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

    Thanks everyone for the replies.

    I've been reading some information on the web but I'm somehow confused (actually: a lot).

    As far as I've read, the following occurs:

    VBA, when calling the DLL, will sendout as input parameters string variables of 'string' type which has the following characteristics (please, correct me if I'm wrong in any of the following statements):

    VBA string:

    1) is UNICODE (meaning 2 bytes per char)
    2) length of the string is located at the start of the variable
    3) it ends with zero character

    C++ string:

    1) is ANSI (meaning 1 byte per char)
    2) has no length information (length comes with the information)
    3) ends with NULL char

    On the other hand, the VBA string type is compatible with the C++ BSTR type. I understand by this that the BSTR type in C++ has the same structure as the VBA type.

    So, my question now, if I'm expecting parameters coming from VBA, then the function must return a BSTR. Also, input parameters should be BSTR. So, it makes sense to declare the function as follows:

    Code:
    extern "C" __declspec( dllexport ) __stdcall BSTR str_separator(BSTR in_string, BSTR sep_char, int id);
    The problem now is that, within the function, I must (and I prefer to) deal with c++ strings (I'm using #include <string>).

    How do I convert then a BSTR variable (which comes in as an input parameter) into a c++ string (so I can work with strings within the function) and finally convert it back into BSTR so VBA will interpret it correctly?

    I'll keep on reading but any hint is highly appreciated...

    Many thanks,

    Lucas

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    OK, well here's the deal. VBA is written in C/C++, and its strings are implemented using BSTR. That is why it works.
    We cannot assume much about c++ strings. The implementation is well... implementation defined. All we know is that we do know the size, and they can be unicode (std::wstring on Windows).

    As for BSTRs, if you have ATL, then CComBSTR will be your friend: CComBSTR Class
    If not, I suggest this guide: The Complete Guide to C++ Strings, Part II - String Wrapper Classes - CodeProject
    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.

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

    Smile

    Hi!

    So, some new and fresh information (at least for me).

    Just for information, I've found this link very useful:

    Passing Strings to a DLL Procedure

    Now, going to the matter. I've managed myself to pass the DLL from VBA a string, and echo it (meaning, I got the original string back). I've done that by using both string types in C++: BSTR and LPSTR. Why is that working, not a clue, since, as per the link above, there is a little difference between them. However the two codes that follow have worked.

    Code:
    BSTR string_ex(BSTR stringVar)
    {
    	BSTR buffer;
    	buffer = stringVar;
    	return buffer;
    }
    and ...

    Code:
    LPSTR string_ex(LPSTR stringVar)
    {
    	LPSTR buffer;
    	buffer = stringVar;
    	return buffer;
    }
    In both cases, I'm getting the input string back in Excel. My problem now is the following: I want to use the library <string> in order to easily manipulate strings withing C++.

    How do I convert (BSTR or LPSTR) into a string-type object (in order to use the string class).

    Any hint regarding this last question?

    Many thanks,

    Lucas

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You realize that those conversion functions do absolutely nothing, yes? Furthermore, it is trivial to convert a BSTR to a LPSTR (char*). You simply cast the BSTR to char*. The other way around is not trivial.
    But reading that article, it is quite clear that we don't need to mess with BSTRs at all.

    Simply do this:
    When calling your function, make the function accept a const char* if you will not modify the string, or char* otherwise. In VBA, declare that argument with ByVal (not ByRef).
    Use std::string's constructor to make that C-style string into a C++-style string. For example:

    Code:
    const char* my_c_str = "Hahaha";
    std::string s(my_c_str);
    Finally, if you want to pass back a string, you have to do it the old fashioned way.
    In VBA, declare a fixed-length string (that is, Dim MyVar as String * Size).
    Make your function accept a char* and size (ie char* str, size_t size).

    When you want to return your C++-style string to VBA, you must copy it into the buffer, and make sure you don't overflow it. Check that size() < size_of_buffer. Then use strcpy to copy the C++-style strings (use the c_str() member function) to copy it into the char* argument.
    It should work.
    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.

  10. #10
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    When calling your function, make the function accept a const char* if you will not modify the string, or char* otherwise. In VBA, declare that argument with ByVal (not ByRef).
    Use std::string's constructor to make that C-style string into a C++-style string. For example:
    Yes, I've seen this. If declare my C++ function with input parameter of type LPSTR then I can send BvVal a string from VBA and that will work (which is what I want).

    Then, since I have a char* (because of LPSTR) I can assign it to a string object. In fact, look at my function now...

    VBA:
    Code:
    Declare Function string_ex Lib "d:/datos/doc/tech/c++/excel/ipfnc/ipfnc.dll" _
        (ByVal stringVar As String) As String
    C++:
    Code:
    LPSTR string_ex(LPSTR stringVar)
    {
        string buffer(stringVar);
        int str_len;
        str_len=buffer.size();
    	
        char *c_buffer = new char[str_len+1];
        strcpy(c_buffer,buffer.c_str());
        return c_buffer;
    }
    Now, the previous code is compiling, but excel crashes any way. My guess is that Excel does not like the string (or whatever) I'm sending back to it...

    I tried to follow your advise, by building up a buffer of the size of the string and strcpy'ing the string into it, but I don't think that had worked ... :-(

    On the other hand, I do not like your following statement ...

    Finally, if you want to pass back a string, you have to do it the old fashioned way.
    In VBA, declare a fixed-length string (that is, Dim MyVar as String * Size).
    Make your function accept a char* and size (ie char* str, size_t size).
    Why do I have to declare a fixed-length string in VBA? My function may return a variable length string ... So, this is the problem: which type of data must my function return? I think this is the question to answer.

    I'll keep on looking for that solution. It must be possible somehow ... I know I lack of good c/c++ knowledge but I'll try to get this to work ... :-)

    Many thanks anyway,

    Lucas

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Quote Originally Posted by Codeplug View Post
    Quote Originally Posted by MSDN
    A VBA String is passed as a pointer to a byte-string BSTR structure when passed ByVal, ...
    Code:
    BSTR WINAPI string_ex(BSTR stringVar)
    {
        std::string buffer((const char*)stringVar);
        size_t str_len = buffer.length();
    	
        return SysAllocStringByteLen(buffer.c_str(), str_len);
    }
    gg

  12. #12
    Registered User
    Join Date
    Dec 2010
    Location
    Cordoba, Argentina
    Posts
    27
    Hi Codeplug,

    Thanks for the reply and for your suggestion.

    I've tried before what you suggest but I cannot compile your code. This is the compiler's output:

    Code:
    Compiler: Default compiler
    Building Makefile: "d:\datos\doc\tech\c++\excel\ipfnc\Makefile.win"
    Executing  make clean
    rm -f ipfnc.o  ipfnc.dll
    g++.exe -D__DEBUG__ -c ipfnc.cpp -o ipfnc.o  -DBUILDING_DLL=1   -g3
    dllwrap.exe --output-def libipfnc.def --driver-name c++ --implib libipfnc.a ipfnc.o  --no-export-all-symbols --add-stdcall-alias  -g3  -o ipfnc.dll
    ipfnc.o: In function `string_ex@4':
    d:\datos\doc\tech\c++\excel\ipfnc/ipfnc.cpp:45: undefined reference to `SysAllocStringByteLen@8'
    collect2: ld returned 1 exit status
    
    c:\GNU\MinGW\bin\dllwrap.exe: no export definition file provided.
    Creating one, but that may not be what you want
    c:\GNU\MinGW\bin\dllwrap.exe: c++ exited with status 1
    
    make.exe: *** [ipfnc.dll] Error 1
    Execution terminated
    Remeber I'm using Dev-C++ 4.9.9.2.

    I've read that, in order to use the 'SysAllocStringByteLen' function I need to include the header 'oleauto.h' but I do not have it ... :-( ... may be if using VC++, but I'd rather go with Dev-C++

    Rgds,

    Lucas

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by laimaretto View Post
    Yes, I've seen this. If declare my C++ function with input parameter of type LPSTR then I can send BvVal a string from VBA and that will work (which is what I want).

    Then, since I have a char* (because of LPSTR) I can assign it to a string object. In fact, look at my function now...

    VBA:
    Code:
    Declare Function string_ex Lib "d:/datos/doc/tech/c++/excel/ipfnc/ipfnc.dll" _
        (ByVal stringVar As String) As String
    C++:
    Code:
    LPSTR string_ex(LPSTR stringVar)
    {
        string buffer(stringVar);
        int str_len;
        str_len=buffer.size();
    	
        char *c_buffer = new char[str_len+1];
        strcpy(c_buffer,buffer.c_str());
        return c_buffer;
    }
    Now, the previous code is compiling, but excel crashes any way. My guess is that Excel does not like the string (or whatever) I'm sending back to it...

    I tried to follow your advise, by building up a buffer of the size of the string and strcpy'ing the string into it, but I don't think that had worked ... :-(
    Of course it doesn't work. VBA expects a BSTR, not char*. The link you wrote clearly says that you cannot return a string this way.

    On the other hand, I do not like your following statement ...
    This isn't about what you like. This is about what must be done.

    Why do I have to declare a fixed-length string in VBA? My function may return a variable length string ... So, this is the problem: which type of data must my function return? I think this is the question to answer.
    Because you need a buffer to copy data into. You're doing this the C way, which means you need a buffer of sufficient length to copy data into. The usual way to solve this is for a function to return the length that is necessary. The link you gave provides an example. And clearly describes what you need to do.

    Of course, if you just use BSTR, you won't need to worry about all of this! I know I gave a little guide of BSTRs.
    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.

  14. #14
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> I'm using Dev-C++ 4.9.9.2
    Time to move on to something that's actively maintained:
    http://www.codeblocks.org/
    http://wxdsgn.sourceforge.net/

    gg

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

    Thanks! let me have a look a those ... any preference?

    Cheers!

    Lucas

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