c++cwinapicom

BSTR Differences between SysAllocString() and BSTR()?


I'm writing a program that takes input from the command-line and uses that input to connect to a remote WMI server and execute methods. My code is as follows:

#define _WIN32_DCOM
#include <comdef.h>
#include <WbemIdl.h>
#include <stdio.h>
#pragma comment(lib, "wbemuuid.lib")

int wmain(int argc, wchar_t* argv[]) {
    // <SNIP>

    /* Build dynamic string containing the hostname of the target and namespace.
       Example: \\DSKT-390047\ROOT\CIMV2 */
    wchar_t cimv2[256] = { 0 };
    swprintf_s(cimv2, 256, L"\\\\%ls\\ROOT\\CIMV2", argv[1]);

    IWbemServices* pSvc = NULL;
    hr = pLoc->ConnectServer(BSTR(cimv2), BSTR(argv[2]), BSTR(argv[3]), NULL,
    0, NULL, NULL, &pSvc);
    if (FAILED(hr)) {
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // <SNIP>
}

The ConnectServer() method requires 8 arguments, 5 of which are of type BSTR. Microsoft details that to create BSTR types I must do so like the following:

BSTR MyBstr = SysAllocString(L"foo");

However, I've seen some developers use BSTR(variable) too, where the variable is of type wchar_t*, so I'm wondering what are the differences between both? I cannot find much on the latter.

Am I okay doing so as I am now? Or should I allocate BSTR types first, use them, then free them?

I'm thinking that the way I'm doing now avoids the fact of having to free these allocated objects, but something is not right.


Solution

  • (BSTR) cast of a wchar_t* type is a serious programming error which, while it will compile and may work, will result in failures when a function that would take a BSTR tries to operate on it because the BSTR type has the size carried before the pointer and therefore, may contain null characters. Read more here. It was created that way to be compatible with wchar_t* where wchar_t* is needed, i.e. you can pass a BSTR to a function that expects a wchar_t*. But not the other way around.

    BSTR is actually this structure:

    struct BSTR
    {
         unsigned int sz = <the_size>;
         wchar_t str[the_size];
         wchar_t nullchar = 0;
    };
    

    where you actually get a pointer to str, not to the start of the structure, to make it compatible with wchar_t* so the cast to wchar_t* will work in functions like wcslen if the BSTR doesn't contain null characters. This might effectively prevent the programmer from detecting their errors.

    Just try, for example, to use SysStringLen on a wchar_t*. Functions that operate on BSTRs will subtract 4 from the passed value and expect a size to be there.

    The proper way is to use _bstr_t to wrap, which will internally create SysAllocString on construction and SysFreeString on destruction.

    _bstr_t f(L"Hello");
    // pass f to a function that expects a BSTR.