'Transferring string arrays from VBA7 to C++

I'm currently working on a project where I want to be able to manipulate and graph data with relative ease in Excel. However, a lot of the data involved exceeds Excels row limits considerably and it needs a degree of preprocessing before I can work with it.

To solve this I decided to write a backend in C++ to handle the preprocessing so that it's acceptable for Excel. The aim is to be able to pick several files within excel which are then sent to the .dll to be parsed, preprocessed, and have some averaging applied to make it more easily handled in excel. The data is then passed back to Excel to be graphed, etc.

I've already worked out how to send arrays of data from the .dll to Excel reliably. However, sending data from Excel to the .dll, usually an array of BSTR which hold file paths, has proven more difficult.

After reading through a few questions:

I thought the following code should work:

// lib.cpp

/*
* clear_log() & write_log(...) both write to a specified log file
*/

inline std::string WINAPI
bstr_string_convert( const BSTR bstr ) {
    const auto bstrlen = SysStringLen(bstr);
    const auto buffer = new char[bstrlen + 1];
    size_t n_char_converted{ 0 };
    if ( bstrlen > 0 ) {
        const auto err =
            wcstombs_s( &n_char_converted,
                        buffer, bstrlen + 1,
                        bstr, bstrlen );
    }
    else {
        buffer[0] = '\0';
    }
    return std::string{ buffer };
}

const std::string log_location{
    "C:\\\\path\\to\\log\\location\\"
};

void WINAPI
clear_log( const std::string_view filename ) {
    std::fstream log_file( log_location + filename,
                     std::ios_base::out | std::ios_base::trunc );
    log_file.close();
}

void WINAPI
write_log( const std::string_view filename, const std::string_view str ) {
    std::fstream log_file( log_location + filename,
                            std::ios_base::out | std::ios_base::app );
    log_file << str << "\n";
    log_file.close();
}

// This works
__declspec(dllexport) LPSAFEARRAY WINAPI
get_double_array( _In_ const LPSAFEARRAY* ppsa ) {
    CComSafeArray<double> csa(*ppsa);

    clear_log("double_log.txt");
    write_log( "double_log.txt",
               std::format("size: {}", csa.GetCount()) );
    for ( LONG i{ csa.GetLowerBound() }; i < csa.GetUpperBound(); ++i ) {
        write_log( "double_log.txt",
                   std::format("{}: {}", i, csa.GetAt(i)) );
    }

    return csa.Detach();
}

// This doesn't
__declspec(dllexport) LPSAFEARRAY WINAPI
get_str_array( _In_ const LPSAFEARRAY* ppsa ) {
    CComSafeArray<BSTR> csa(*ppsa);

    clear_log("string_log.txt");
    write_log( "string_log.txt",
               std::format("size: {}", csa.GetCount()));
    for (LONG i{ csa.GetLowerBound() }; i < csa.GetUpperBound(); ++i ) {
        write_log( "string_log.txt",
                   std::format( "{}: {}",
                                i,
                                bstr_string_convert(csa.GetAt(i))
                              ) );
    }

    return csa.Detach();
}
Declare PtrSafe Function send_string_array _
    Lib "path\to\dll" _
    Alias "_get_str_array@4" _
    (ByRef first_element() As String) As String()
    
Declare PtrSafe Function send_double_array _
    Lib "path\to\dll" _
    Alias "_get_double_array@4" _
    (ByRef ptr() As Double) As Double()

(Not sure what's making the syntax highlighting weird here)

Sub test()
    Dim doubles(3) As Double
    doubles(0) = 3.141592
    doubles(1) = 1235.12617
    doubles(2) = -1266.2346
    
    Dim d_result() As Double
    d_result = send_double_array(doubles)
    
    Dim n As Long
    For n = 0 To 2
        Debug.Print d_result(n)
    Next n
    
    Dim strings(3) As String
    strings(0) = "This"
    strings(1) = "is a "
    strings(2) = "test."
    
    Dim result() As String
    result = send_string_array(strings)
    
    For n = 0 To 2
        Debug.Print result(n)
    Next n

End Sub

Immediate Window:

 3.141592 
 1235.12617 
-1266.2346 
??
??
??

double_log.txt:

size: 4
0: 3.141592
1: 1235.12617
2: -1266.2346

string_log.txt:

size: 4
0: 
1: 
2: 

So my question is what am I missing that causes get_double_array(...) to work, but get_str_array(...) doesn't?

As an aside, why does CComSafeArray::GetCount() return 4 for an array declared with 3 elements? Seems like something weird is going on there.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source