{"id":104459,"date":"2020-11-18T07:00:00","date_gmt":"2020-11-18T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104459"},"modified":"2020-11-18T06:43:33","modified_gmt":"2020-11-18T14:43:33","slug":"20201118-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201118-00\/?p=104459","title":{"rendered":"On combining static libraries that implement C++\/WinRT activatable objects"},"content":{"rendered":"<p>In C++\/WinRT 2.0, one of the features is <a href=\"http:\/\/web.archive.org\/web\/20190406142238\/https:\/\/docs.microsoft.com\/en-us\/windows\/uwp\/cpp-and-winrt-apis\/news#smarter-and-more-efficient-modulegcpp-for-large-projects-with-multiple-libs\"> enigmatically documented<\/a> as<\/p>\n<blockquote class=\"q\">\n<p><b>Smarter and more efficient <tt>module.g.cpp<\/tt> for large projects with multiple libs<\/b><\/p>\n<p>The <tt>module.g.cpp<\/tt> file now also contains two additional composable helpers, named <b>winrt_can_unload_now<\/b>, and <b>winrt_get_activation_factory<\/b>. These have been designed for larger projects where a DLL is composed of a number of libs, each with its own runtime classes. In that situation, you need to manually stitch together the DLL&#8217;s <b>DllGetActivationFactory<\/b> and <b>DllCanUnloadNow<\/b>. These helpers make it much easier for you to do that, by avoiding spurious origination errors. The <tt>cppwinrt.exe<\/tt> tool&#8217;s <tt>-lib<\/tt> flag may also be used to give each individual lib its own preamble (rather than <tt>winrt_xxx<\/tt>) so that each lib&#8217;s functions may be individually named, and thus combined unambiguously.<\/p>\n<\/blockquote>\n<p>What does this all mean?<\/p>\n<p>A module makes its Windows Runtime objects available to other components by exporting a function with the well-known name <code>Dll\u00adGet\u00adActivation\u00adFactory<\/code>. In its default configuration, C++\/WinRT generates code for the simple case that you have a single self-contained project that implements all of its own objects: It autogenerates a <code>Dll\u00adGet\u00adActivation\u00adFactory<\/code> function that knows how to produce the objects in the .winmd files you said you were implementing.<\/p>\n<p>However, it&#8217;s possible that you&#8217;ve broken a large project into a bunch of libraries, with each library implementing some objects, and you want to link them all together into a single module. That&#8217;s where the the <code>-library<\/code> flag comes into play.<\/p>\n<p>What you do is have each library pass the <code>-library name<\/code> flag to <code>cppwinrt.exe<\/code>. This causes the C++\/WinRT compiler to generate a function named <code><span style=\"border: solid 1px black;\">name<\/span>_get_<\/code><code>activation_<\/code><code>factory<\/code> rather than the default name <code>winrt_<\/code><code>get_<\/code><code>activation_<\/code><code>factory<\/code>. You can now provide your own implementation of <code>winrt_<\/code><code>get_<\/code><code>activation_<\/code><code>factory<\/code>.<\/p>\n<p>In the simplest case, you are just aggregating the information from the various libraries you are consuming:<\/p>\n<pre>void* __stdcall winrt_get_activation_factory(\r\n    std::wstring_view const&amp; name)\r\n{\r\n  void* factory = library1_get_activation_factory(name);\r\n  if (!factory) factory = library2_get_activation_factory(name);\r\n  if (!factory) factory = library3_get_activation_factory(name);\r\n  if (!factory) factory = library4_get_activation_factory(name);\r\n  return factory;\r\n}\r\n<\/pre>\n<p>The C++\/WinRT library does understand a little bit about WRL (the <a href=\"https:\/\/docs.microsoft.com\/cpp\/cppcx\/wrl\/windows-runtime-cpp-template-library-wrl?view=vs-2019\"> Windows Runtime C++ Template Library<\/a>), an older library for implementing Windows Runtime objects. Specifically, it understands enough that if <code>winrt_<\/code><code>get_<\/code><code>activation_<\/code><code>factory<\/code> returns <code>nullptr<\/code>, then it asks WRL if it can produce the activation factory before giving up.<\/p>\n<p>If you&#8217;re using some other library for implementing Windows Runtime objects, you can hook that other library into your custom <code>winrt_<\/code><code>get_<\/code><code>activation_<\/code><code>factory<\/code> function:<\/p>\n<pre>void* __stdcall winrt_get_activation_factory(\r\n    std::wstring_view const&amp; name)\r\n{\r\n  void* factory = library1_get_activation_factory(name);\r\n  if (!factory) factory = library2_get_activation_factory(name);\r\n  if (!factory) factory = library3_get_activation_factory(name);\r\n  if (!factory) factory = library4_get_activation_factory(name);\r\n  <span style=\"color: blue;\">if (!factory) factory = OtherLibrary_GetActivationFactory(name);<\/span>\r\n  return factory;\r\n}\r\n<\/pre>\n<p>There&#8217;s a second function that the <code>-library<\/code> flag renames: <code>winrt_<\/code><code>can_<\/code><code>unload_<\/code><code>now<\/code>. You also need to aggregate the various libraries in your implementation of that function:<\/p>\n<pre>bool __stdcall winrt_can_unload_now() noexcept\r\n{\r\n  return library1_can_unload_now() &amp;&amp;\r\n    library2_can_unload_now() &amp;&amp;\r\n    library3_can_unload_now() &amp;&amp;\r\n    library4_can_unload_now();\r\n}\r\n<\/pre>\n<p>Again, C++\/WinRT knows enough about WRL to invite it to the party: If all C++\/WinRT libraries are okay with unloading, then it also checks that WRL is okay with unloading before allowing the unload to proceed.<\/p>\n<p>As with <code>winrt_<\/code><code>get_<\/code><code>activation_<\/code><code>factory<\/code>, you can hook any other library into the custom implementation of <code>winrt_<\/code><code>can_<\/code><code>unload_<\/code><code>now<\/code>.<\/p>\n<p>Next time, we&#8217;ll look at another trick which this flag enables.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Separating the shared names, so you can combine them yourself.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-104459","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Separating the shared names, so you can combine them yourself.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104459","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=104459"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104459\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=104459"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104459"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104459"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}