July 6th, 2022

The empty Windows Runtime string is not just a pretty face

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.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

8 comments

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

Newest
Newest
Popular
Oldest
  • dcookMicrosoft employee

    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.

    • Raymond ChenMicrosoft employee Author

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

    • 紅樓鍮 · Edited

      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.

    • Tobias Käs · Edited

      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.

  • Brian Ross

    > 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 · Edited

      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

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

      • Me Gusta

        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.

Feedback