- Good morning everyone,
thank you for coming. Especially at this hour at the morning and given the topic at hand I am very impressed by the turnout. My name is Victor and we'll be
spending an hour this morning talking about a very sexy
subject like COM objects. And back in July, this happened, my friend Simon tweeted this
and I immediately identified with the problem, but I
said I'm gonna roll with it. And my daughter keeps telling me that I do look like Gru. And just in case you're disoriented, this is the roadmap for
Victor Ciura for the week. You're in the third talk,
hope you're in the right one and you won't regret it. And let's move on with it. This is part one of N for this topic that I'm trying to tackle. It's an ambitious endeavor, given the interest in the
community around this topic. And I'm hoping to get a lot of feedback, especially at places like this with such a diverse pool of programmers with such a diverse background experience in actually using and
deploying applications on top of COM components. And depending on how well it goes and the feedback I
receive, I plan to refine and improve this and take it even further. So I do really need your
help, and your feedback, and ideas, and let's see how it goes. We even have some chairs here left over from a different panel, so we can do an impromptu COM panel if you like. So audience participation is encouraged, and feel free to say
whatever that's on your mind, hope it's COM related though. So why COM, why are we talking about this? Have we really exhausted
all the cool template topics that we can cover at CPPCon? Well for me at least, COM
has been an important part of my professional
career, and this is mainly because of the constraints
of the application I've been working on. I've been working on this
application, Advanced Installer, for over 13 years now. And Advanced Installer,
I just need to give you a little bit of a background so that you can better
relate to my COM story. Advanced Installer is
an IDE and a tool chain that helps software developers to package and deploy their applications on Windows. And at the same time it helps IT pros repackage and provision operating systems and deploy line-of-business apps in enterprises at scale. So as you might imagine,
Advanced Installer is a very wide, it's an umbrella project, it's a very wide application with very different components underneath. And given the task at hand in provisioning the operating system and deploying various kinds of applications, you can imagine it has a
very wide API surface area, and it actually interacts
with many components from the Windows subsystem,
whether older or newer features of the operating system. So basically what we're trying is to help clients migrate
their Win32 applications to the modern application model like MSIX and maybe some of them will
bring their applications in the Windows Store, or
Windows Store for Business. So as you might imagine
this is a big endeavor. We interact with so many components in the operating system and as
we all know and love Windows, you know that's all
built on COM components. And just before we get started, I suspect I know the answer, but I wanna get the feel for the room. So how many of you have
been doing COM programming in the last 20 years? Okay, this is the right
crowd. (lightly laughs) Again I'm guessing, but how many of you are still doing this? Since you're here I'm guessing it's relevant to you at
least, if not anecdotical. So let's dive into it. Another important takeaway I'm trying to push with this talk, and I wanna encourage a
discussion around this, is that even if COM is
not something of interest to you right now, or
maybe it was in the past, there are important lessons to be learned about good API design. And examining COM as such
an impactful infrastructure in the operating system, and as a library that has
been around for 25 years now, it's very telling to examine how well some of the design choices
stood the test of time, and to really see the
seams in the grand design of the framework and see which parts have played well, and have aged well, and which parts really
gave us so much friction around these years, and
what can we learn from it. What does the modern
operating system has to offer in this regard, have we learned
something over these years? Is it easier now to interact
with the operating system through its COM components or not? Are we in the same mess
that we've been years ago? So I'm hoping to discover
this together with you. So like any good COM programmer
we have to initialize. And if you think about CoInitializeEx, it basically initializes the COM library on the calling thread, very
important detail nowadays. And it sets the thread concurrency model to the one requested, nowadays
a multi-threaded model. It creates a new apartment for the thread if that's what you need. And you really need to
initialize COM on each thread that it actually needs to
invoke COM related function, otherwise it won't work. So this aspect is really important, so. And ideally you would want
to initialize COM only once per thread, at least once I mean, I guess. So multiple calls to
CoInitializeEx on the same thread are allowed, but subsequent
calls issue an S_FALSE H result. And this is relevant
because COM error handling is cumbersome, to say the least. And you need to be careful in
how you manage expectations and check the success of
the different API calls. And in the case of CoInitialize, you wanna be a good
citizen and uninitialize the COM on the calling thread when you're done using COM functionality. And if you're not properly
matching those up, and not doing proper error checking to see if you're the one that
actually initialized the COM on that thread, you might
end up uninitializing COM on the thread, and somebody else in some other part of the
functionality really needs it. So you can really shoot
yourself in the foot by trying to be a good citizen. So you need to be graceful
in uninitializing COM, and do it exactly when
it needs to be done. So do or not do, there is no try. By the way, any Trekkies in the room? Yeah, some of this material
may be unpleasant to you. Okay, so how do we fix CoInitializeEx? Well RAII is something that
we're accustomed to nowadays. So we can build a simple
helper like this one, where we can make sure that
we acquire the COM dependency and release that dependencies
when we're done with it. And this is where the proper
error checking comes into play. As I said earlier, we need to remember if we were the ones that
actually were the first to initialize COM on the calling thread. So that's why we need to save
that mClose Boolean state to make sure that we're the ones that need to CoUninitialize
it on this thread. So if you're doing this
you can use it locally. For example you have a function scope. You have some function
requiring COM functionality and use some API, you
can initialize it locally and when it's done with it, with the current function context, you're done, uninitialized, it's okay. Or you can use it in with a class scope. For example, if you have
a more complex object that has several methods
requiring COM access, you can hold on to that dependency as long as you're done and your object is basically tied, as a
dependency, to the COM itself. So that's pretty straightforward, has worked out very well for us. We actually use it heavily, I couldn't imagine living
without such a functionality. - I have a question.
- Yeah, sure. - [Audience Member] You
showed this error RAII plus? - I can go back. - [Audience Member]
Don't you need to handle a model change error code? Because you can actually change-- - The threaded model?
- Yes. - Yeah. We don't do it, we don't do it. Generally speaking it's not a good idea. Technically you're right, you
can actually initialize COM with a different threading model. For example using multi-threaded, or switch to apartment threaded,
or something like that. Technically you can do it, but it's never a good
idea to mix those up. So we generally try to stay
away from mixing these modes up. We've been burned before by
accidental initialization with different threading models, and there were components that needed to talk with each other and
there are very careful rules of isolation about COM
object instances in memory with regards to the apartment model. So we've been burned with this before and we try to stay away
of mixing those up. - [Audience Member]
Yeah, I'm just wondering if you wanna add some kind
of check for that error code, made by a server end load-- - Maybe we should, at least an assertion would be a good idea because
we've been burned by this. And by the way this is
classes a little simplified, it's not exactly, it
has to fit on a slide. But you're very right, this is a concern. And we've actually encountered, I think last year we've
had a problem with this. We had a component that
was not under our control, and it actually did a
different threading model. - I had another comment.
- Yep, sure. - [Male Audience Member]
I thought this was correct but I wanted to look it
up before I said anything. Doth quoth the MSDN
documentation, let me find it. To close the COM library
gracefully out of thread, each successful call to
CoInitialize or CoInitialize, including those that return S_FALSE, must be balanced by a corresponding
call to CoUninitialize. Your check I think is. Your enclose checks your
result out of the page, should also check S_FALSE
because you're required to call CoUninitialize
even if it returns S_FALSE. - [Audience Member] Yeah, you
can use succeeded COM calls. - I'll have to look at that, okay. I'll get back to you on that,
I'll have to look it up. - [Audience Member] That's
very typical for COM. You need resume every function-- - Yeah, it's telling, it's telling. I think this is telling. But thank you for pointing out, I should really look into that. - [Male Audience Member]
I thought I was correct, but I wanted to look it up
before I actually said-- - Okay, okay, I have to look it up. Cannot confirm it, okay. So COM initializer and RAII. You don't care about the thread you're on as long as you encapsulate
this dependency. You don't care if it's
already initialized or not on this thread, and most important thing, you can nest these objects as needed. You don't care at which point
in the call stack you're in. And you don't need to about
closing and unloading COM when you're done, that's
the whole purpose. So let's talk about strings now. And I had to pick, COM
is such a large topic and it would be a mess to
try to cover too many things. So one of the earliest feedback
I got on this talk idea is that I need to pick something and focus on something COM related, and try to analyze it throughout. And no question, I had to pick strings. For us at least, this has
been the most sensitive issue around COM and I'm trying, at least for this part of the talk, to focus on COM strings a little bit. So what's the COM string type? - [Male Audience Member] BSTR. - BSTR, yeah, our friend. So for those of us who don't remember this ingenious piece of design, the BSTR is a string
with a buffer in memory. It has a length prefix
and is null terminated. - [Male Audience Member] Not always. - Yeah, that's why we have the length. And the most interesting part is that BSTR of course in the type system is a pointer, and it actually points to
the data buffer in memory, not a length prefix, so
there's always a trick of offsetting this pointer. So if you think you can
do a thing like this, which I would expect to work, you can't. This compiles, and links,
and it's incorrect. And you would say that
well, I would never do this. It looks stupid. Yeah, you can by accident
pass a string literal to a COM API function that
has a BSTR form parameter. So I'm honest enough to
admit I've done that before. So you can be easily burned by this. So what you have to do
is allocate a string for this BSTR, and you have
to check the success state. It almost never fails, right? Why check error codes? And use the string, and
then free the string. So lot of hand-waving around
just initializing a string. And somebody has to save our skins here. RAII for the win. Let's see what the Visual
C++ compiler has offered us to mitigate this issue. We have a few RAII COM support classes. I know everybody loves this one, _bstr_t. By the way, these underscore T types, really, really bad in terms
of standard conformance. They should be reserved names. But we're gonna roll with it. So _bstr_t, we have similar
helpers for variants, and for COM pointers, and of course for our HRESULT error handling friend. And because we're focusing on string, let's see what _bstr_t
has to offer for us. It encapsulates nicely the BSTR data type, manages resource allocation
through SysAlloc, SysFree, what we would expect from an RAII wrapper. It uses references counting
to avoid excessive overhead, so this is actually clever and uses Copy on Write underneath, so you can freely copy these things as if they're value types. It provides various
conversion constructors like you would expect to
initialize this data type. So meets the expectation,
it's intuitive, handy. Very handy operators for
lexicographic comparisons and concatenation, and you would use it probably something like this. Pass it around to some COM
API that requires a BSTR, has an explicit method
for getting that pointer to the buffer, and you can convert it to a standard string because
you do need to inter-operate between your COM-related classes and your platform-independent
code at some point, you need to do a two-way handshake in martialing data up between your model and your COM layer. And of course you can leverage the lexicographic compare operator if you need something like this. You can construct a _bstr_t
from a standard string by getting its Cstring non-automated array and you can compare it very easily. So very handy, nice, clean,
no unexpected results here. If we examine the other
helper related to this, a little more convoluted if
we need to deal with pointers. We need to do some magic macro wizardry, and we can actually tag
each interface of interest and construct an ac-hoc
smart point of reference by using this fancy macro,
and we get an instant type, like for example I have
here ITaskbarList3PTR which is a smart pointer. And I can get an instance of this class and use it as if it's a regular pointer. So a little weird using the
macros, but we're used to that. So next bit of history,
who's been using ATL for the past years? Yeah, ATL fans in the house. So just a quick orientation
or a history lesson, this would be a simple ATL structure. The core object in ATL is CComObject, which is a template class. And I'm not gonna cover aggregate objects because it's a much more complicated topic for this discussion. But if we're talking just
about a simple COM object, a COM object inherits
its template parameter, and this would be a very, very
contrived and simple example of actually creating an
instance of this circle object. And invoking just one
method of interest on it, SetRadius in this case. And this is what we would have to do, and all of this is just boilerplate, I don't care about any of this code but I kinda have to write it. And this isn't even the whole class, I couldn't fit it on the slide. This would be the actual
code I care about, in being my constructor
and my methods of interest. So having to write all
this boilerplate code really messes your attention, and it's sometimes error-prone and definitely gets in your way. By the way, do you recognize this pattern? It has a name. - Curiously recurring--
- Yeah, yeah. CRTP, curiously reoccurring
template pattern. So basically it achieves a similar effect to the use of virtual functions, but without the cost of dynamic
polymorphism, or a VTABLE. And it has a binding at
compile time of course, we want this for our COM objects. We don't want virtual
code for each COM method. And the pattern is used extensively in ATL and WTL libraries. We actually use a lot of
ATL in our application, and our whole GUI is built on top of WTL, so I know pain when I see it. But it's a very, very powerful construct. Building up objects this
way is flexible enough, very powerful, not very
friendly for the beginner. So we do have parallels and helpers in ATL for the same constructs. And we have CComPtr and CComBSTR, and of course I'm gonna pick on CComBSTR because we're talking about strings. So this is the ATL wrapper for BSTR type. It manages allocation again the same way, because it's the same type underneath. But this one does not
implement Copy on Write, so this object is different. And in the latest versions of ATL it actually has move semantics, just in case you didn't know. So it provides, again, various
conversion constructors that you would expect to initialize it. Same convenience operators,
usage looks similar with _bstr_t, difference
being that CComBSTR actually has an operator to convert it to a BSTR automatically. This might be helpful, or
might be surprising to you depending on if you wrote the code or not. You can initialize a standard
string from CComBSTR. Of course if we're talking about, I did mention this earlier, if we're talking about COM strings we're always talking about UTF16, so this is the underlying assumption in how the operating system is built. So you're gonna see only
STD wstring in the slides. So you can match your
data up back and forth between your model or
your application logic and the COM components. You can construct the other way around, you can construct CComBSTR
from a standard string, you can compare them, similar
functionality with _bstr_t. So at this point I would
like to run an ad-hoc poll: how many _bstr_t fans
versus CComBSTR fans? So _bstr_t, _bstr_t, oh, not a love, no love for _bstr_t. CComBSTR, ATL, ATL for the win. Okay, just a personal curiosity. - [Audience Member] There's actually, I think it will be easy for people. If there is a piece of
cross-platform code, I understand it's Windows related but sometimes you're have to
work cross-platform library, you will be using post string. I remember the problem is that
wstring will be four bytes on non-Windows platforms, while it will be UTF16 in Windows. So basically if you do have
to write with pattern code you also will have to do
either UDEF base conversions from wstring from Windows
to wstring on non-Windows. - I'm just gonna summarize
what you just said to be on the recording and
other people to follow. So a very good observation. If you're dealing with any
cross-platform development and you need to isolate a
platform abstraction layer that's COM dependent, and you
do need to do this martialing between standard string and COM string, you do need to be careful about
the string representations on other platforms. And it's indeed a very good observation, and I remember a talk a few years back, some people on the Office
team did a presentation that I thought it was fantastic, you should look it up on YouTube
or Channel 9 or something. Don't remember the title, but the whole talk was
around their experience in actually porting Office to iOS, and the challenges they faced
around data type lengths, and NDNS, and character
lengths on different platforms. So that talk goes into many details around data representation
in memory, and adapting, and constructing a proper
platform abstraction layer, and it's actually an
actual real case study from the Office team
that did all this work, and it wasn't easy. (lightly laughs) You should definitely,
if you're interested in stuff like that you
should definitely look it up. I'm sorry I don't remember the title, but it was an excellent talk. - [Male Audience Member] Can
I make just a quick comment? Maybe two comments, 'cause
I kinda raised my hand for both BSTR and ComBSTR,
I use both of them. - Myself included. - [Male Audience Member] One
advantage that I've found with CComBSTR is it does overload
the address of operators. So if you're passing it
to accommodate the item that takes a ret val, you can use CComBSTR to capture the ret val where
you can't do that cleanly with _BSTR construct. The other note that I would
have is in your construct where you're constructing wstring by passing the direct
reference to the BSTR, the third line, I would be very careful about doing that in production code. The reason being, some languages return non-null terminated BSTRs through COM interfaces, which is allowed. And the wrapper classes, while providing convenient overloads for the effective null
terminated strings in C++, don't check that necessarily. And if you use that construct with BSTR that came out of VisualBasic, for example, it will likely crash. - Yeah, very good two observations, and I'm gonna repeat them to be on record. The first one is that
with regards to the poll I made earlier, _bstr_t
versus the ATL CComBSTR, we actually use both, like you said. Different components, different authors, what are you gonna do, that's life. And the preference here,
and the observation was made that if you have an API that
actually returns a string as an output parameter, the CComBSTR, having a get address of function can actually be used to
capture an output parameter, did I convey that correctly? - Sure.
- Okay. And it has a clear
advantage here over _bstr_t. The other observation,
which is more subtle, is regarding embedded nulls, or if the string isn't null terminated. COM of course has projections
in different languages, and if you're talking about COM, the whole idea is about inter-op and being language agnostic
around a common ADI. And if you happen to get a hold of a string reference that
is not null terminated, actually constructing a
standard string this way like line three here is
potentially dangerous, so you better watch out
for the string length because it's not null terminated, did I convey this correctly? - [Male Audience Member] I
was just gonna add to that. There's a way to
construct a stood strings, passing the pointer and the length, and you can actually macro out of that which is what we've done in
our code to help conversion. - Ah, I would rather be
explicit than put a macro there, but the observation is valid. - [Male Audience Member] Well
ATL has conversation back rows like CW to CT, so you can have
a CSTR to BSTR, BSTR to CSTR. - Yeah, I'm not a fan of those but we do have them in the
code base, so, question here? - [Audience Member] Yeah, so
with the BSTR 32-bit problem, there are types in standard for incar 16T, and so you can lock yourself in-- - Yeah, and the basic
string class is templatized. So wstring is just a
template instance for WCarT, so you can actually use the other chart types in the standard. Good observation there, good observation. So again, this is a contrived example and it actually assumes a null
terminated string, so yeah. Very good observations. I'm definitely in the right room. No seriously, actual audience
participation in this talk highly improves the content. So there's a bigger fish here, there's always a bigger fish here. And this would be ATL
CString, one of my favorites. So again, this is a templatized class, has support for char,
wchar_t and StringStraits. It manages allocation,
deallocation of course, uses reference counting to
avoid excessive copy overhead. Again, this is a Copy on Write
implementation, very handy. I see so many people trying
to avoid CString copies and passing references around, and I always find that a futile effort. And it actually has a string
ref internal representation and it correctly checks
that it actually needs to duplicate that string
when modifying it. Has lot more methods and operations than _bstr_t or CComBSTR. Has all the conversion
constructors you would expect, including VARIANT, which is nice. All the operators you would
expect for convenience. Lots of algorithms and utilities, as opposed to the other
ones that are bare wrappers around the data type, this one actually has string algorithms like Find, FindOf, formatting strings, changing the case, the
upper case, prefix, suffix, substrings find, reverse
find, tokenize, what have you, all the goodies you would
expect from a string class. So definitely my favorite. In terms of simple usage, again, you can imagine calling some API that requires a BSTR and
you have to be explicit about requesting an OLE
BSTR from the SysString. And the other way around, if you're trying to construct, this is the most frequent
offender in our code base. Actually data martialing buffers between standard string and CString, I think is the most frequent operation we do in our application because we have all the application logic and
the model of the application built on top of standard string, and each time we need to
interact with the GUI, the GUI again is built
on top of ATL and WTL, so everywhere is CString
and interacting with COM, and we copy things around, yeah? - [Male Audience Member]
You know that leaks, right? - Sorry, the line two?
- Yeah. - Line two yeah, yeah, no, I'm just trying to show the conversion, the conversion function. Actually I've never used
the AllocSysString function from CString, I never needed to, so. But the third line is
the thing that we have, I think that's the most, if I do a search, maybe CString GetString is the
most frequently used function in our code base, so that's unfortunate. - [Audience Member] You haven't
clarified what is the leak? - Yeah, the AllocSysString
method allocates a copy, a BSTR copy of the
underlying CString buffer. - [Male Audience Member]
Well and specifically BSTRs are allocated in global memory in COM, there's a Comalic call
and they have to be freed by CO3 I believe, and
AllocSysString calls Comalic and returns the raw BSTR
pointer, so don't do this. Write a macro like CSTR to BSTR that returns one of the
COM object for _bstr_t, then you can call this
directly just using your macro around the STR.
- It's the same problem you have with SysAllocString
and SysFreeString, you have to manage those manually. So I would never advise
using AllocSysString. There are some convenience
macros like he mentioned. And it's rare that you actually need to do this back and forth, but in case you do you need to be aware that you get new BSTR instances. That's why I put the
allocates comment there. And what I'm trying to do in this slide is with the type system itself,
not the memory management, so the conversions that happen. So again, and the other way around. Again, this is a very frequent
operation in our code base. Converting from a standard
string to a CString to give it some GUI function or working with some component there. So these are a pest for us and
they always give me headaches and I'm trying to get rid of those, so. But what about this modern
COM I keep hearing about? Surely things have improved
in the last 25 years. Enter Windows Runtime. Starting with Windows 8 and Windows 10 we have a new kid on the block. This is a new day, a new beginning. So the Windows Runtimes, for
the ones that are still suck on Windows 7, yeah, we still need to deploy applications there, right? Is a modern, class-based,
object-oriented Windows API. It has reach metadata about the classes and of members, properties. Has language projections
for natural and familiar use in your preferred language. Okay, not Ruby but it could be done. And how do I get access to
the Windows Runtime from C++? Well let's start with the beginning. We have WRL, yeah, that's
how we start it, right? Before there were any real projections we have the Windows Runtime
C++ Template Library, that's a mouthful. It enables you to more easily implement and consume COM components. It's a very thin abstraction over the ABI. It's template-based of course, it gives the ability to
control the underlying code, it's very low-level, so
it's not always pretty. Error handling is still HRESULT-based because it doesn't abstract
away anything on this front. But its design is heavily inspired by ATL, so one huge benefit of this approach is that you can actually
mix in the old COM code with new COM code, and
this is the main reason that the Windows Runtime effort was bootstrapped to this way. I always consider the WRL implementation as a transitional period
so that you can bootstrap and start adopting new APIs in your existing, old application, and to have a seamless interaction there. And of course it was easier
for them to implement this, until we got an actual language projection in the form of C++/CX later. So it actually uses standard C++, and it's template-based like I said. Uses smart pointers, RAII over the place, it actually helps with activation, so it was a clear improvement over the pure ATL-based
approach, rather verbose though. And of course it allowed us to add support for UWP applications and
leverage new APIs there. Immediately after that, I'm doing, I think you got the gist of it, I'm doing a history pass here. So quickly we got a language
projection that was C++/CX. If anybody remembers
that pain with the hat. It uses a nonstandard
C++ language extension, the Visual C++ compiler was enhanced to support these language extensions. The syntax is terse, clean,
much less boilerplate, all the things the compiler does, all the messy work behind the scenes, we just write simple code,
but there's a learning curve. This being a nonstandard
language extension you need to get accustomed
to the hats and how it works, and know the reference
counting that happens behind it and understand it a little bit,
so there's a learning curve. Definitely high-level abstraction here. Something nice about it,
I'm a bit fan of exceptions, I'll admit it, not the
right conference to do it but I'll admit it. So it actually encapsulates
HRESULTs as exceptions, automates various housekeeping tasks, so if you need optimization,
but it's discontinued. So let's start to see
how it would look like, and it would be some
includes and namespaces, nothing special there. RAII for the win, again, this time this helper is
actually provided for us, it's not something I implemented. So very similar to what we had
earlier, RoInitialize helper. Simple usage would require
to get an activation factory, to get an instance of full. For example IUriRuntimeClass interface, so looks like ATL, little bit
different, still very verbose. Not my cup of tea. But what's the Windows
Runtime string type? Again, I'm going back to
this theme of strings. So Windows Runtime string type? Hint, it's not BSTR, sorry? - [Audience Member] HSTRING. - HSTRING, yeah, that's always
a new an improved model. It represents an immutable string, careful here, an immutable
string in the Windows Runtime, it's basically a handle. And in order to get a
hold of these HSTRINGs you have to use functions like
this and man, they're ugly. That's a very sad API surface. But that's what we have to deal with. And we have class representation, for example a very simple odd thing, you cannot initialize it by construction with a string literal, you have to actually code a set method, I thought that was sad. So a very simple example
of initialization there. Yeah, platform string is the
language projection for C++/CX. So if you were using C++/CX that would be the language
projection for it. This time around with the
C++/CX projection it looks nice, I can see a clean initialization as opposed to this garbage. Has the expected operators,
checking for equality, concatenating stuff, looks good. But you cannot stop change, and people don't like
nonstandard language extensions and I consider the evolution a step in the right direction here, and going back to standard C++ is what you want to strive for, and you don't have to rely
on a specific compiler for some language extension. So C++/WinRT comes into play. It's an ISO standard
C++17 language projection for the Windows Runtime,
it's a header-only library, it has C++ class wrappers
for the WinRT APIs. You can author and consume
Windows Runtime APIs with it, and officially supersedes
the WRL and C++/CX, so definitely this is the present. How many of you are using C++/WinRT? Well that's sad, I
guess we haven't got rid of Windows 7 yet, that's clear. So this is an open source project, it was a long time on GitHub, it actually moved to the
Windows SDK a few months back. And I think this is the best
documentation to start with if you're trying to learn about it. And we have to thank that man for this, and let's give him a
round of applause please. (light crowd applause) Kenny did a very big
effort in actually striving for bringing uniformity,
and using a standard C++, and surfacing all the regions
of the APIs in C++/WinRT, and I'm so happy that it's
finally part of the Windows SDK, and it actually feels like
it's a first-class citizen, and using C++ to program
with Windows Runtime is actually legit now. I feel welcomed in the platform finally. So I highly recommend
that you see this talk if you haven't seen it already,
from last year's CPPCon. It's a talk all about WinRT. And from this year, a more
recent version of using C++/WinRT to author UWP applications from the Build conference in spring. So how do I get it, well fortunately it does come with Visual Studio 2017. You just have to select the C++ workload and you're up and running,
it's as simple as that. I would also add, I would like to see, I understand the reason
of rolling out extensions in Visual Studio, it actually
gives you the flexibility of updating on a different cycle and pushing things a little off base or off band than the
regular product of it, but I would like to see more
support right in the books, and this extension helps you. I highly recommend that you check it out. It actually helps with debug visualization for C++/WinRT projects and provides you with project templates to get started with building C++/WinRT applications, and has support for MSBuild, and generating projection
headers and components. So this is a separate
extension you can install. So let's start with the
Windows Runtime again. Much more simple this time around. We have to actually give the
linker some instructions there. Include either WinRT base or foundation, which automatically includes
base, depending on your need. And you can just basically start going. Notice there it does
require C++17, so I'm there, how many of you are able to
leverage C++17 in your work? I say 1/5 of you? So it actually performs better
and produces smaller binaries than any other language projection. It outperforms handwritten code using the ABI interface
directly, like WRL did. The underlying abstraction
uses modern C++ idioms that the Visual C++ compiler
is designed to optimize for, like magic statics, empty base classes, string length solutions and so forth. So leveraging nonstandard
stuff actually helps, because the optimizer doesn't look for improving nonstandard extensions. What's the CPP WinRT string type? And I'm getting to my
thing of strings again. So what's the string type, anyone? - [Audience Member] Still string? - No, but very close. It's winrt::hstring, yes you guessed it. So yeah, question? - [Man With Glasses]
We use IDL's interface for visual language by
their visual interfaces. Can we have access to going into the IDL? - Maybe Kenny can help
there, I don't know. Regarding ADL, the
question was if he uses ADL to define its interface--
- Not ADL, IDL. - IDL, IDL, sorry. - [Man With Glasses] On IDL
for the visual language, and whenever there's a
new publish on the string we've lose beats there. - [Kenny] Yeah, so if you're offering a Windows Runtime interface
it has to use an HSTRING, not a BSTR, so it's a
classic COM interface it can be a WRL string,
it could be a BSTR, it could be whatever you like. In the Windows Runtime type
system it has to be an HSTRING. So that's the only string
type that is permitted by IDL, by the metadata that it
uses and the way it's at. - [Man With Glasses] That mean the old IDL and new IDL can't work
together, is that correct? - [Kenny] Well they can
work together in the sense that you can have a COM object that implements both kinds of interfaces, the one RT interface
and the COM interface, but the one RT type system
only supports one string type, and IDL is just a way for
you to describe the interface and get some code in the
binary metadata format, and that format only supports HSTRINGs as the single string type. - If I can summarize that for the camera, so maybe I wasn't very clear but Windows Runtime is
not built on top of BSTR. Like Kenny said it's
built on top of HSTRING which is a different representation. So if you're defining a component, it has to be defined in terms
of HSTRING type, not BSTR. And I would also add that if
we're talking about Middle, yeah, it's a different Middle version for authoring Windows
Runtime, that was Middle two and that was a very baroque
and very verbose language of describing interfaces, and it was improved in Middle Three, is a much more lightweight and airy mode of describing type libraries. So I have actually seen
a very nice interview with Larry Osterman on
Channel 9 around this, and the changes that he needed to operate on actually describing
the Windows Runtime. So definitely the Windows Runtime is focused around HSTRING, not BSTR. - [Man With Glasses]
So would it be related to conditions of people who
are using IDL right now, how we can workflow using
the access of the WinRT? - [Kenny] Do you wanna take it offline? - Yeah, I think it's best
'cause I'm out of time and I still have a few slides, yeah. Okay, so winrt::hstring
represents an immutable string consistent with the underlying HSTRING. You can construct it
like you would expect, has various constructors
for your convenience there. Again, also the version with
the length, and the pointer has an explicit constructor
with string views. A few examples, you can
construct it in various ways: from a string view, from a pointer, from a pointer and length, all the usual suspects
there, nothing unexpected. The operator that converts
it to a string view is interesting, in my opinion, and I have a whole talk on string views that happened on Monday, but you can catch it on recording. The string view is
actually meant to be a glue between these string types, and my hope is that string view will solve all our inter-op problems
at API design level, and will help us bridge this gap between different string
types in our type system when we're talking, for
example, of standard string and WinRT HSTRING for example, or other string types for another. And you can see in this example I can initialize a standard string from the return result
of this domain method that returns an HSTRING because
of that explicit operator that converts it to a string view, and of course I can
construct a standard string from the string view, so that's a very nice to compose things. So HSTRING is also range, HSTRING exposes almost the same interface as standard string, and I think you asked a little bit earlier, you suspected that the standard string type for the WinRT's standard string. It's not, but it's something that's almost like a standard string. So it mimics the same API, same interface, although the string is immutable, but it looks and feels familiar and it can inter-operate seamlessly, so it actually feels like you're
using the standard string, you don't have to worry about it too much. And you can use range for, you can treat it like a range, just like with standard string. Although each string,
again, is UTF16 underneath, it does play nice with if you do need to do some data martialing
in terms of UTF-8 text, and it has convenience
methods to transform it to a narrow format, for
example, like UTF-8, if that's needed in your case. We don't have that problem but I guess there are scenarios for that, mostly in terms of if you're
doing cross-platform stuff, so. So we end up with something
like this in our applications, and I love this slide. (light laughter) So the highlighted stuff is basically just your COM pleasure. So yeah, we kinda have to
deal with all of these, so it's tough, so you
have to think about okay, I'm doing my business logic,
my platform independence stuff in standard string and doing all my platform dependence
stuff, my GUI, my COM objects, my platform abstraction layer, whatever, I'm doing that with let's say
XString, whatever that is, COM whatever, and I have
to do this data martialing and inter-op and tie those things together so they make sense. And that's all fine until
your application grows and you start to bundle up
utilities and algorithms around strings and then
pain comes into play, because we have to decide do I implement all my string algorithms
in standard string, do I do overload sets
for my XString types, whatever that is? Do I fall back to a
common denominator thing and make all my algorithms
in terms of pointers to characters and lengths,
that's always ugly. So this is where string_view
hopefully is the answer. Like Billy said it's the
duct tape of string types, I like that type. And it's still an ongoing journey, but we're trying to
discover if string_view is really the answer to
all our string problems, and that's why I have this whole quest of actually trying to figure
out the good practices, and all the gotchas
around using string_view. And of course we have the limitation of actually being able to
use this if you're on C++17. There are similar classes
out there for C++11, like UpsellString_view for example, but again, you might have limitations on what you can use in your code base. So definitely a lightweight
string-like view, over under eight characters, it basically maps this
concept of pointer and length. It was designed as glue code, and to reduce the overload
sets for your classes, that's its main purpose,
but it does not manage the lifetime of the object clearly, it's a projection over
an immutable sequence, so it maps really well to WinRT HSTRING, which again represents
an immutable object. The lifetime issue is a problem,
that's the main concern. It has all the usual suspects
in terms of constructors and conversion operators, not all of them explicit unfortunately. Or fortunately, depending
on how you see it. And because of that it does
come with plenty of gotchas. So my whole talk about hanging
yourself with string_view is around those gotchas,
and trying to determine best practices and properly designing APIs around string_view,
and where is it safe or comprehensible to use
it in formal parameters rather than store it as a
data member or in containers. And we're still figuring this out, string_view is a strange type, it actually deceives you in
that it's actually a value type, but it fails to actually
confirm into being a proper, regular type because of
its assignment operator and how its comparison function works. So it's difficult, it's deceiving. So we're still figuring it out, and it has real potential
to really help us here, especially, my hopes are
especially around COM and string_view, and
I'm trying to figure out how I can make this work in my code base. And it's a learning
process, but I'm optimistic. So Windows COM is 25 years old and this shows in many corners. Yes it's still relevant
today more than ever because Microsoft has bet
its entire WinRT API on it, so we're gonna live with it. And with the advent of C++17, using COM objects and the new WinRT APIs feels like a new
experience, in my opinion. So definitely something to strive for. So the dark side of COM is
the pathway to many abilities some consider to be unnatural, but I'm very optimistic and I
still enjoy programming COM. So with that, thank you. (crowd applause)
Maybe I'm really late to the party, but I actually didn't know about C++/WinRT. I think the title is rather misleading, but it's such a great library/tool being finally able to write C++/C# without the ugly C++/Cli.