user2530062
user2530062

Reputation: 517

Replace to_utf8string to ensure proper string encoding on Windows 7 in a C++ DLL

I have a legacy C++ code that was written as a dll that can be called on Windows 10. A new requirement is to allow this dll to function properly on earlier Windows (7 Embedded, optionally XP as well). In short, the code is supposed to display body of a REST response.

The problem is, when called on Windows 7 without any modifications, there are additional (sometimes invisible) characters, especially at the beginning of the displayed output.

The code below is an abbreviated version and I believe to_utf8string is the root of the problem. What other function should I use in order for this dll to work on Windows 7 properly?

const char* foo(const char* param)
{
    web::http::http_response resp = other_foo(param).get();
    std::string utf8Response;

    web::json::value json = resp.extract_json().get();
    utf8Response = utility::conversions::to_utf8string(json.serialize());

    return utf8Response.c_str();
}

Two example outputs on Windows 7:

  1. Should be: {"statusCode": 400, example 1

  2. Should be: {"status": example2

Upvotes: 0

Views: 255

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596672

The problem has nothing to do with character encoding, or OS used. The function is returning a dangling pointer to invalid memory!

The function is declaring a local std::string variable that goes out of scope when the function exits. It is returning a pointer to the std::string's internal character data, which is freed when the std::string is destroyed.

To solve this, the function will have to return a pointer to dynamically allocated memory that the caller can then free (or pass back to the DLL to free) at a later time when it is done using the data. For example:

const char* foo(const char* param)
{
    web::http::http_response resp = other_foo(param).get();
    web::json::value json = resp.extract_json().get();
    std::string utf8Response = utility::conversions::to_utf8string(json.serialize());

    // the caller can use CoTaskMemFree() to free this returned memory...
    size_t size = utf8Response.size() + 1; // +1 for null terminator
    char *ptr = static_cast<char*>(CoTaskMemAlloc(size));
    if (ptr)
        std::memcpy(ptr, utf8Response.c_str(), size);
    return ptr;
}
const char* response = foo(param);
if (response) {
    ...
    CoTaskMemFree(response);
}

Or:

const char* foo(const char* param)
{
    web::http::http_response resp = other_foo(param).get();
    web::json::value json = resp.extract_json().get();
    std::string utf8Response = utility::conversions::to_utf8string(json.serialize());

    // the caller must call free_foo_result() to free this memory...
    size_t size = utf8Response.size() + 1; // +1 for null terminator
    char *ptr = new(std::nothrow) char[size];
    if (ptr)
        std::memcpy(ptr, utf8Response.c_str(), size);
    return ptr;
}

void free_foo_result(const char *data)
{
    delete[] data;
}
const char* response = foo(param);
if (response) {
    ...
    free_foo_result(response);
}

If re-writing the EXE to make these extra function calls is not an option, then the DLL will have to cache the JSON data so it can survive past the return, eg:

const char* foo(const char* param)
{
    static thread_local std::string utf8Response;

    web::http::http_response resp = other_foo(param).get();
    web::json::value json = resp.extract_json().get();
    utf8Response = utility::conversions::to_utf8string(json.serialize());

    return utf8Response.c_str();
}

Upvotes: 3

Related Questions