{"id":4713,"date":"2008-12-08T15:29:00","date_gmt":"2008-12-08T15:29:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2008\/12\/08\/inheriting-from-a-native-c-class-in-c\/"},"modified":"2019-02-18T18:53:57","modified_gmt":"2019-02-18T18:53:57","slug":"inheriting-from-a-native-c-class-in-c","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/inheriting-from-a-native-c-class-in-c\/","title":{"rendered":"Inheriting From a Native C++ Class in C#"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Hi, this is Jim Springfield, an architect on the Visual C++ team.<span>&nbsp; <\/span>I have blogged in the past about our IDE and Intellisense work.<span>&nbsp; <\/span>I am still heavily focused on that and we are working hard to deliver an improved experience, but this post is about a completely different topic.<span>&nbsp; <\/span>A few months ago, I started thinking about how to access C++ classes from managed code and came up with this technique, which I haven&rsquo;t seen mentioned anywhere else.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">There are many ways that native code and managed code can interact and call each other.<span>&nbsp; <\/span>If you have native code that you want to call from C# you have several choices depending on the nature of the API.<span>&nbsp; <\/span>If you have a flat &ldquo;C&rdquo; API, you can use P\/Invoke to directly call the API.<span>&nbsp; <\/span>If the native code is exposed using COM, the CLR&rsquo;s COM Interop can provide access.<span>&nbsp; <\/span>If you have a C++ class, you could go add COM support, or write a custom wrapper using C++\/CLI and expose a new managed class.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">I really wanted something more direct than these.<span>&nbsp; <\/span>Initially, I was just trying to see if I could call a native C++ class from C#, but as I started playing with it, I realized that I could actually &ldquo;inherit&rdquo; from the native class.<span>&nbsp; <\/span>I put &ldquo;inherit&rdquo; in quotes, because you could make an argument that it isn&rsquo;t truly inheritance, but I will let the reader make the final decision.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Let&rsquo;s say I have a C++ class exposed from a DLL that I want to consume in C#.<span>&nbsp; <\/span>The class looks like the following.<\/font><\/p>\n<p class=\"MsoNormal\"><span>class<\/span><span> <span>__declspec<\/span>(<span>dllexport<\/span>) CSimpleClass {<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>:<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>int<\/span> value;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>CSimpleClass(<span>int<\/span> value) : value(value)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>~CSimpleClass()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>printf(<span>&#8220;~CSimpleClassn&#8221;<\/span>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>void<\/span> M1()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>printf(<span>&#8220;C++\/CSimpleClass::M1()n&#8221;<\/span>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>V0();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>V1(value);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>V2();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>virtual<\/span> <span>void<\/span> V0()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>printf(<span>&#8220;C++\/CSimpleClass::V0()n&#8221;<\/span>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>virtual<\/span> <span>void<\/span> V1(<span>int<\/span> x)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>printf(<span>&#8220;C++\/CSimpleClass::V1(%d)n&#8221;<\/span>, x);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>virtual<\/span> <span>void<\/span> V2()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>printf(<span>&#8220;C++\/CSimpleClass::V2()n&#8221;<\/span>, value);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>};<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">The __declspec(dllexport) means that the class is exported from the DLL.<span>&nbsp; <\/span>What this really means is that all of the class methods are exported from the DLL.<span>&nbsp; <\/span>If I look at the list of exports using dumpbin.exe or depends.exe, I see the following list of exports.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">??0CSimpleClass@@QAE@ABV0@@Z<br \/>??0CSimpleClass@@QAE@H@Z<br \/>??1CSimpleClass@@QAE@XZ<br \/>??4CSimpleClass@@QAEAAV0@ABV0@@Z<br \/>??_7CSimpleClass@@6B@<br \/>?M1@CSimpleClass@@QAEXXZ<br \/>?V0@CSimpleClass@@UAEXXZ<br \/>?V1@CSimpleClass@@UAEXH@Z<br \/>?V2@CSimpleClass@@UAEXXZ<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">These are decorated (i.e. &ldquo;mangled&rdquo;) names.<span>&nbsp; <\/span>For most of these, you can probably guess what the name is actually referring to.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">(Note: Name mangling may change between versions of C++ and mangling is different between x86, x64, and Itanium platforms.<span>&nbsp; <\/span>The example here works on both VS2008 and the CTP release of VS2010.)<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">There is a nifty tool called undname.exe that ships with Visual Studio, which can take a mangled name and undecorate it.<span>&nbsp; <\/span>Running it on each of the names above gives the corresponding output.<\/font><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>__thiscall<\/span> CSimpleClass::CSimpleClass(<span>int<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>__thiscall<\/span> CSimpleClass::~CSimpleClass(<span>void<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>class<\/span> CSimpleClass &amp; <span>__thiscall<\/span> CSimpleClass::<span>operator<\/span>=(<span>class<\/span> CSimpleClass <span>const<\/span> &amp;)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>const<\/span><span> CSimpleClass::`vftable&#8217;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>void<\/span> <span>__thiscall<\/span> CSimpleClass::M1(<span>void<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>virtual<\/span> <span>void<\/span> <span>__thiscall<\/span> CSimpleClass::V0(<span>void<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>virtual<\/span> <span>void<\/span> <span>__thiscall<\/span> CSimpleClass::V1(<span>int<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span>: <span>virtual<\/span> <span>void<\/span> <span>__thiscall<\/span> CSimpleClass::V2(<span>void<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Other than the methods we explicitly defined, there is also a compiler generated assignment operator and a reference to the vtable for this class.<span>&nbsp; <\/span>OK, so I know that using P\/Invoke, C# can call into native DLL entry points, and I just happen to have a list of native entry points.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">First, however, we need to define a structure in C# that corresponds to the native class.<span>&nbsp; <\/span>Our native class only has one field: an int.<span>&nbsp; <\/span>However, it does have virtual methods, so there is also a vtable pointer at the beginning of the class.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">(Note: I am only dealing with single inheritance here.<span>&nbsp; <\/span>With multiple inheritance, there are multiple vtables and vtable pointers.)<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><span>[StructLayout(LayoutKind.Sequential, Pack = 4)]<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span> unsafe <span>struct<\/span> __CSimpleClass<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span> IntPtr* _vtable;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span> <span>int<\/span> value;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Next, I am going to define a C# class that wraps the native class and mimics it.<span>&nbsp; <\/span>I want to expose synchronous destruction, so the C# equivalent of that is implementing IDisposable, which I do here.<span>&nbsp; <\/span>I also create a matching constructor and the &ldquo;M1&rdquo; method of CSimpleClass.<span>&nbsp; <\/span>I use &ldquo;DllImport&rdquo; to specify the DLL name, entrypoint, and calling convention.<span>&nbsp; <\/span>The &ldquo;ThisCall&rdquo; convention is the default for C++ member functions.<\/font><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">(Note: to be safer, I should explicitly specify calling conventions and structure packing in my native code, but that is left out for brevity.<span>&nbsp; <\/span>If they aren&rsquo;t explicitly specified, compiler options can change the defaults.)<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">There are calls in the code below to Memory.Alloc and Memory.Free.<span>&nbsp; <\/span>These were implemented by me and just forward to HeapAlloc\/Free in kernel32.dll.<\/font><\/p>\n<p class=\"MsoNormal\"><span>public<\/span><span> unsafe <span>class<\/span> CSimpleClass : IDisposable<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>private<\/span> __CSimpleClass* _cpp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>\/\/ CSimpleClass constructor and destructor<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>[DllImport(<span>&#8220;cppexp.dll&#8221;<\/span>, EntryPoint = <span>&#8220;??0CSimpleClass@@QAE@H@Z&#8221;<\/span>, CallingConvention = CallingConvention.ThisCall)]<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>private<\/span> <span>static<\/span> <span>extern<\/span> <span>int<\/span> _CSimpleClass_Constructor(__CSimpleClass* ths, <span>int<\/span> value);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>[DllImport(<span>&#8220;cppexp.dll&#8221;<\/span>, EntryPoint = <span>&#8220;??1CSimpleClass@@QAE@XZ&#8221;<\/span>, CallingConvention = Cal\nlingConvention.ThisCall)]<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>private<\/span> <span>static<\/span> <span>extern<\/span> <span>int<\/span> _CSimpleClass_Destructor(__CSimpleClass* ths);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>\/\/<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>void M1();<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>[DllImport(<span>&#8220;cppexp.dll&#8221;<\/span>, EntryPoint = <span>&#8220;?M1@CSimpleClass@@QAEXXZ&#8221;<\/span>, CallingConvention = CallingConvention.ThisCall)]<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>private<\/span> <span>static<\/span> <span>extern<\/span> <span>void<\/span> _M1(__CSimpleClass* ths);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>public<\/span> CSimpleClass(<span>int<\/span> value)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>\/\/Allocate storage for object<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_cpp = (__CSimpleClass*)Memory.Alloc(<span>sizeof<\/span>(__CSimpleClass));<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>\/\/Call constructor<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_CSimpleClass_Constructor(_cpp, value);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>public<\/span> <span>void<\/span> Dispose()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>\/\/call destructor<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_CSimpleClass_Destructor(_cpp);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>\/\/release memory<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Memory.Free(_cpp);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>_cpp = null;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>public<\/span> <span>void<\/span> M1()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><\/span><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi, this is Jim Springfield, an architect on the Visual C++ team.&nbsp; I have blogged in the past about our IDE and Intellisense work.&nbsp; I am still heavily focused on that and we are working hard to deliver an improved experience, but this post is about a completely different topic.&nbsp; A few months ago, I [&hellip;]<\/p>\n","protected":false},"author":289,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4713","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Hi, this is Jim Springfield, an architect on the Visual C++ team.&nbsp; I have blogged in the past about our IDE and Intellisense work.&nbsp; I am still heavily focused on that and we are working hard to deliver an improved experience, but this post is about a completely different topic.&nbsp; A few months ago, I [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/4713","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/289"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=4713"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/4713\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=4713"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=4713"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=4713"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}