The empty Windows Runtime string is not just a pretty face

Raymond Chen

As I noted some time ago, the empty Windows Runtime string is represented by a null pointer. This has natural but perhaps surprising consequences: Even though it is a null pointer, the empty Windows Runtime string is a real string, with hopes and dreams. Or at least a length and data.

At the ABI level, Windows­Get­String­Len reports that a null pointer string has a length of zero, and Windows­Get­String­Raw­Buffer gives you a buffer that consists of a single null terminator.

Since an empty string and a null pointer are indistinguishable at the ABI layer, if you operate at the ABI layer (using raw HSTRINGs) or at a thin projection layer (such as C++/CX and C++/WinRT), you can take advantage of this equivalence.

For starters, you don’t need to check for a null pointer before trying to use the string, because a null pointer is a perfectly valid HSTRING.

ABI if (s != nullptr &&
    WindowsGetStringLen(s) == 1)
if (s != nullptr &&
    s == HStringReference(L"hi").Get())
C++/CX if (s != nullptr &&
    s->Length() == 1)
if (s != nullptr &&
    s == L"hi")
C++/WinRT if (s != hstring{} &&
    s.size() == 1)
if (s != hstring{} &&
    s == L"hi"sv)

If you are checking for a nonempty string, you can just check for null. C++/WinRT and C++/CX even have special methods that tell you directly.

  Slower way Quicker way
ABI if (WindowsGetStringLen(s) != 0) if (s != nullptr)
C++/CX if (s->Length() != 0) if (!s->IsEmpty())
C++/WinRT if (s.size() != 0) if (!s.empty())

Related: The C++/CX String^ is not an object, even though it wears a hat.

8 comments

Discussion is closed. Login to edit/delete existing comments.

  • Brian Ross 0

    > If you are checking for a nonempty string, you can just check for null.

    What if someone passes in an allocated string buffer that happens to point to an empty string (ie. just null terminator).

    If you just check the pointer value – won’t you miss this scenario?

    • Brian Ross 0

      Checking further.. I guess this is why it is safe:

      > One of the special rules for HSTRING is similar to the corresponding rule for BSTR, namely that a null pointer is equivalent to a zero-length string. But HSTRING takes it further: Not only is a null pointer equivalent to a zero-length string, but in fact a null pointer is the representation of a zero-length string. In other words, if you call WindowsCreateString and specify that the string has length zero, then out will come a null pointer.

      You can’t get a HSTRING that is non-null for the case above – WindowsCreateString would have returned nullptr.

      • Dmitry Vozzhaev 0

        But this still looks dangerous. In C++ calls to a null object are UB.

        • Me Gusta 0

          This is one of those “it depends” situations. For example, the winrt::hstring class is a container, much like std::string.
          So while the C API/ABI will use null to represent an empty HSTRING, I would be very surprised if any of the C++ projections would use null to indicate an empty HSTRING. But I only really know C++/WinRT, and I know that this projection uses references to pass parameters.

  • dcookMicrosoft employee 0

    I’m pretty sure that the C++/CX syntax `s->Length()` is undefined behavior. C++ explicitly disallows this == nullptr, and compilers like Clang will optimize such paths as “unreachable”. Admittedly, Clang doesn’t support C++/CX, but similar C++-only wrappers could exist on Clang and syntax like the above could lead to confusing optimizations.

    With that said, the WindowsGetStringLength and s.size() checks look good.

    • Tobias Käs 0

      C++/CX will most likely be using a userdefined type, so ‘s’ won’t be nullptr … it will be a value of the userdefined type containing a field with the nullptr and defining the methods “properly” by calling the ABI. This needs the arrow operator to return another userdefined type and not “just” dereference the pointer, as the pointer points to nothing that has a length method. So given how you typically implement API shims I don’t see how this would invoke undefined behavior.

    • 紅樓鍮 0

      Actually, you can realistically create your own smart pointer type that semantically equates nullptr to "", whose definitions of operator*, operator[], operator-> etc handle nullptr specially by returning a pointer/reference to a literal "", whose operator== equates nullptr to "", etc.

    • Raymond ChenMicrosoft employee 0

      See the linked article. s is not a pointer. It’s a custom type that looks like a pointer (String^).

Feedback usabilla icon