 All right, everybody. We're here with Mike talking about modernizing app.NET applications with.NET Core. Take it away, Mike. Yep. All right. Thank you very much. Yeah. So like we said, my name is Mike Rousos. I'm a Principal Software Engineer on the.NET Customer Engagement Team. I've been at Microsoft 15 years working on.NET the whole time and we're going to talk today about when you move a desktop application to.NET Core, beyond just making a new project file and targeting.NET Core, what other sorts of issues might you run into and how can you work past them? So I'm going to go ahead and share my screen, and we're going to take a look at some slides. So let's see if we get that working. All right. So again, this is modernizing with.NET Core beyond the basics. Hey, Mike, do you want to- And it is the fourth in a series of talks over the past few days which have looked at how you can take WPF or WinForms desktop.NET application and bring it over to.NET Core so that it runs in a more modern environment. We started with all we have shared during- Could you close that window for us there? I'm not- Oh, sorry. What's that? Could you close that little window, that little Skype window on the right-hand side? Didn't even notice that thing. No problem. It always happens. Thanks. Yep. Appreciate the pointer. Okay. So yeah. So, Allia started us off during the keynote talking about how you can use TryConvert to convert the project file and some basic steps there. And then she had a talk on day one to go in a little bit more depth. Volsa had talks talking about DevOps and AppCenter and XAML Tools, individual studio for WPF and UWP development. So what I'm going to be talking about today is sort of that next step after you've done all of these things. What feature areas are different? What things might need changed? And how can you do that? So this talk is called Beyond the Basics. Here's the things that I consider the basics that I think have already been covered. Using the portability analyzer, updating NuGet references, updating project files, targeting.NET Core, using the Windows compatibility pack. So if you haven't seen this stuff, go check out Allia's talk, familiarize yourself with that. And in many cases, after you've gone through these steps, a lot of .NET applications will just work on .NET Core at that point. But for some, there's a number of features that work differently. And so there may be additional work needed, depending on the specific application you're porting. So here are the things that we're going to talk about during this talk. We're going to talk about managed security, app domains, configuration, interrupt, and remoting. That's a lot of topics. So I'm going to have to move fairly quickly. I've got a couple of demos, but I'm going to try to keep things moving along. And we'll just sort of look at this at a high level. And then I've got some demos out on GitHub that you guys can look at afterwards if you want to spend some more time. And I'm going to save a few minutes at the end, hopefully, although it started a little late, so we'll see what happens. But we'll try to have some time for Q&A at the end of this talk, if there's things you guys want to hear a little more detail about. So jumping in, I'm going to start with security, because this one's actually really simple. And it's simple because the story is there is no managed security on .NET Core. We have the APIs for code access security. We have the APIs for transparency, so that all of your existing attributes and calls to these systems will continue to compile and work if you've got a lot of these attributes in your code base. But the thing you need to know is that there are no ops now. Since about .NET 4 or so, we've been giving the message that code access security is not a security boundary because managed code is not the right place to implement a sandbox. There's too many ways to get around that. The right way to sandbox your code and run it partially trusted is at the OS level. So you would do this with things like virtualization, containers, user accounts, maybe run as a UWP app with only certain app capabilities. Those are the right ways to restrict access that an application has. Doing that in managed code has always been a challenge and it's never worked particularly well. So in .NET Core, we go the final step and we say none of these APIs do anything. So in most cases, this doesn't require any changes in your application. You just need to understand that these attributes and calls no longer have any effect. The one case where this will require a change in your code is if you're using any APIs which restrict permissions, if you call permissionset.deny or permit only. These would have worked previously, but now in .NET Core, they throw a platform not supported exception so the developers are really clear that they are not restricting permissions because in .NET Core, all code runs as fully trusted and security critical. Now, there are some APIs which relate to ACLs and these still work as expected because that's not related to managed security. This is about access control at the OS level. So these APIs, which you'll find in the CompatPack, still work the same as you'd expect but anything CAS related, APTCA, transparency, that stuff no longer works in .NET Core. So that's item number one. And if you look on GitHub with the link I show at the end of this talk, I do have a quick sample, but I'm not gonna show the code for this one because it's just showing that you can call from code label transparent into code labeled critical and .NET Core and everything works as expected. So we're gonna move along. So I've got some more interesting demos I wanna save some time for. Okay, so secondly, and this is one of those more interesting demos, app domains. So in .NET Core, many of the app domain APIs are still present. You're still able to register an unhandled exception handler, you're able to inspect the app domain, things like that work, but the important difference in .NET Core compared to .NET Framework is that there's only one default app domain and no other app domains can be created. So if your application that you're porting was previously spinning up secondary app domains, that's going to fail now. And you're gonna have to change your app to accommodate the fact that there's only one app domain in a .NET Core process. Now the way you do this kind of depends on why you were using multiple app domains. In some cases, customers in their .NET Framework applications were spinning up additional app domains so that they could run some code in partial trust. So they could have some sort of sandbox app domain. Now, I just mentioned on the last slide, sandboxing's gone. So that scenario no longer applies. If you were using app domains as a way to set up a partial trust environment for running code, that's not going to work. You need to run that code in the primary app domain understanding that it's gonna run with full trust and that has to be all right. If that's not okay, you're gonna have to consider running out of process or some other mechanism for sandboxing because app domains don't do that. So that's the first scenario. Second scenario, let's suppose you were using app domains as a way to isolate assemblies and unload them. Sometimes you see this with like a plugin architecture. You might have different plugins for your application, some of which might use different versions of the same assembly. So you wanna load those assemblies in an isolated way so that one plugin can use version one, one plugin can use version two. And maybe when you're done with the plugin, you wanna unload them because assemblies don't unload by themselves, but an app domain can be unloaded with all of the assemblies in it. So for this scenario, we added an API in .NET Core called assembly load context. Now the idea of a load context where the loader loads an assembly is not new in .NET Core, but this API is new. And in .NET Core three, what's new is that they can be unloaded. So this is a mechanism where you can have different logical containers in your application that load assemblies in isolation. So you might have different versions of the same assembly loaded into multiple assembly load contexts, and they'll each work. And then when you're done with them, if you want, you can unload an assembly load context and all of the assemblies that were there get unloaded with it. So I'm gonna hop over into Visual Studio and we're gonna do a quick demo of what it looks like to use assembly load context in place of app domains for assembly unloading. So in here, in this solution, I've got a little folder for each of the topics I'm talking about and this will be shared on GitHub. But for the app domains part, I've got a simple main method that just demonstrates that some app domain properties are readable on .NET Core. And then I call this helper called assembly loader that use assembly and unload. And it's going to load up this type, M-B-R-O type, which is going to call into a helper library called Mathlib to calculate some Fibonacci numbers. It's not very complicated. The important thing is we're gonna do this in a secondary app domain and then unload it so that the math library never gets loaded into our primary app domain. Now you can see that I've got some pound ifs here so that we have this code working differently when it builds against .NET Framework compared to when it runs against .NET Core. So these defines Net472.NET Core app are automatically defined depending on the framework that you're targeting. So if you have a project file like this one, and let me open it up so we can take a look, that's using multi-targeting, you can see how I'm actually targeting two different frameworks. I'm targeting both .NET 472 and .NET Core app 3.0. Or if you have two separate project files, one which targets .NET Framework, one which targets .NET Core, if you end up in a situation where some code paths need to do one thing for .NET Framework and one thing for .NET Core, an easy way to do that at build time is to use these built-in defines to differentiate between the two. In this case, the only difference is that I want to display for demo purposes where we're running. So I show either the current domain's name if we're running on .NET Framework or even though this API still exists in .NET Core, it wouldn't be interesting because it wouldn't change. Instead on .NET Core, I can switch over here which target I'm looking at. You can see when we're targeting .NET Core, this one lights up. Instead, we're gonna be looking at the current assembly load context's name, okay? So the way we're doing this is with this helper type I talked about, assembly loader. And here I use another trick for targeting both .NET Framework and .NET Core at the same time because this code's gonna be fairly different. I have one implementation which I'm gonna use on .NET Framework which uses app domains to load and assembly and unload it. I have a different implementation of the same class which does the same thing using assembly load contexts for .NET Core. So we saw that you could say pound if net 472 or pound if net core app for small pieces of code in your project. If you have whole classes that need to be implemented differently for .NET Framework or .NET Core and you want to build against both of those targets, a pattern that I've worked with customers in the past to use and that's been helpful is to just have conditional item groups where you say if we're targeting net 472 or whatever .NET Framework version you're targeting you can even say if it begins with net something then we remove from compiling any source files with the net core CS suffix. And likewise if we're targeting .NET Core we remove anything with the net effects suffix. So this way I have two different assembly files one for .NET Framework, one for .NET Core and they'll be built one for framework, one for core depending on what we're targeting. I also have a none include here. This is a little trick. This ensures that the source files show up in the solution explorer even if we're not building them because by default this will only show the files for whichever target framework is first here. So by doing a none include on this this makes sure that we build only when we're targeting the correct framework but we still see both files in our solution explorer. Because that's not anything to do with app domains but some tidbits on working with different code paths as you take your application and start to have to bifurcate it between running on .NET Framework or .NET Core. Coming back to the app domain sample itself what my assembly loader is going to do in the .NET Framework case is very simple. We create an app domain. We create an unwrapped instance of that type that I was talking about. And then we just have some logging to demonstrate that the math library hasn't been loaded in either domain. We call this helper method on that type that will run in the secondary app domain to calculate some Fibonacci numbers. And then we have some more logging to demonstrate that the math library has been loaded into the secondary domain, not into the primary one. Then we call appdomain.unload to unload everything from that domain and we're done. If we do this for .NET Core it looks a little bit different. For .NET Core instead I'm going to create an assembly load context. Now here I've actually derived from assembly load context and I'll show you why I do that. Beginning with .NET Core 3 you can use assembly load context directly. It's a concrete type. But by overriding it, by deriving from it I have the opportunity to override the load method which is useful because this is the method that the assembly load context uses when it needs to load an assembly implicitly. So I'm going to load, if we come back here and look at the assembly loader code I'm going to explicitly load this demo assembly in the secondary load context because that's where the MBRO type lives. Once we've done that though this type is going to need to use that math library and so that's going to be implicitly loaded into the assembly load context and the way that will be done is controlled by the load method. So here we're just using the normal resolve behaviors to go and resolve it from our project assets. JSON file the same way it would with the default load context but this can all be customized so you can customize how you probe for assemblies how you load them and so on in your own assembly load contexts. The interesting thing though is that we will get that type via reflection from the assembly that's loaded in the other load context. So even though this type is going to have the same name as the type that's in this load context they're going to be treated as different types but via reflection we can invoke an API on it so we're going to call this call helper API and it's going to go off it's going to invoke the math library do some Fibonacci sequencing and it's going to come back with a number all fairly straightforward we've got the same sort of logging as before where we demonstrate that the math library is only loaded in the secondary context and then we call load context on load. Now in this case it's a little bit different I'm going to return a weak reference to this load context so that I can then do a GC collect and watch for this load context to be cleaned up and the reason I do this is that there's an important difference between unloading an app domain and unloading an assembly load context. When you unload an app domain it's a rude unload. Any code that's executing the app domain is aborted everything's just torn down immediately and unloaded. When you unload an assembly load context it's a cooperative unload. So this call here where I say load context on load requests that the load context and any assemblies in it are cleaned up but that won't actually happen until there's no more references to the load context. So that means there can't be any stack frames referencing assemblies from this load context on any of my threads call stacks means we can't have any references to types from assemblies in this load context. So if you're migrating an application that previously unloaded app domains and you're now unloading a load context you need to be aware of this difference so you can make sure that you're cooperating with the load context and not having any references hanging around. So in this case we do the collect and we wait to make sure that in fact that weak reference is no longer alive which is the indication that the load context has been unloaded, cleaned up and those assembly references are gone. So let's go ahead and run this. I'll do it over here so that we've got a nice big font that you guys can see. If I do a dot net run dash f net 472 remember I'm targeting both dot net framework and dot net core so I use the dash f parameter on dot net run to specify which one of these multi targeted frameworks I'm using. So we're running against dot net framework it says creating app domain we see that the math library is not loaded anywhere but then we run this Fibonacci helper in the new domain you see and now the math library is loaded in that new domain it's still not loaded in the current domain we unload the app domain so at this point the math library is not loaded anywhere and we're done. You can do something very similar if we do a dot net run dash f net core app 3.0 and so now we're gonna it's gonna look very similar but you'll wanna notice that instead of talking about app domains now we've created a new load context and that's the context that this code is running in I passed in a different input value for variety but here you see that the math library is loaded in the new context it is not in the default context and again we unload prior to the unload here's the lists of our assembly load context after the unload here's the list because the second one's gone along with the libraries that were in it like math library. All right we'll do that quick but the code will be up on GitHub and we can take questions at the end so that's a quick overview of app domains in dot net core you only get one assembly load context for everything else next up configuration so this is an interesting one it's a pretty simple story but I wanna make sure to call it out because it comes up a lot and the story is this app.config and configuration manager and those sorts of APIs they're all still present in dot net core they're in the compact package like so many other things so you can still use app settings connection strings custom config sections that maybe you take advantage of in your dot net framework app however the framework APIs themselves do not use app.config and they don't respect any sections in there other than just app settings or connection strings so previously with dot net framework there were a lot of APIs which had their own config sections that you could use to configure them things like WCF clients could be configured that way system diagnostics tracing system.net a number of others all of these config sections were defined in the global machine.config file in dot net core there is no machine.config so even having one of these sections in your config file will prevent it from loading on dot net core you need to remove them and even if you were to define these custom sections yourself well then the config file would load but dot net still doesn't use them so you may as well get rid of them instead you need to configure these dot net APIs programmatically okay so if you were previously setting up trace sources and listeners and switches in config now you do it in code you still can get information from the config file either from app settings or from a more modern Microsoft extensions configuration config file like an app settings.json or from environment variables but that's going to require still using the APIs to configure tracing WCF clients whatever and just pulling key values out of config one place where this comes up a lot is with WCF so dot net core supports WCF client API so you can call WCF services but if you previously generated your WCF client with the service util tool it will have created an app.config file where it sets up that WCF client that's not going to work anymore so the recommendation when you're using WCF clients with dot net core is to remove any auto-generated code and regenerate it with new tools that will generate the same functionality in a dot net standard or dot net core compliant way so we've got two options for this there's dot net service util which is a dot net CLI tool that can be used from the command line or from visual studio in the visual studio connected services menu you can add a WCF service and in that way you'll get a WCF client generated that's dot net standard and dot net core compatible doesn't depend on config files at all now that's WCF client you're probably also wondering about WCF server APIs because I do get asked about those a lot when I'm working with customers and there was a blog post recently that Scott Hunter you can go check out and basically at this point dot net cores feature complete we're not planning to add support for WCF server APIs so you'll want to look at alternatives like GRPC which was demoed at the keynote or possibly ASP.net core if you really must stick with WCF there's a couple options one is that there's a community-driven effort called core WCF which is working to migrate at least some parts of WCF server APIs to work on dot net core so you may want to go check out that community effort maybe contribute there or remember dot net framework isn't going away it's not as new and as exciting as dot net core but we're continuing to support dot net framework indefinitely so if you have WCF server dependencies that you really can't get away from it's fine to stay on dot net framework until you get to a point where you're able to either use core WCF because it's matured enough or where you're able to move over to something like GRPC so let's take a quick look at a config demo so I'll hop back over here and actually just given the time I might not run this one because it's not exciting to see run but the thing I'm going to show you is just if you look at these two config files I've got one for dot net core and one for dot net framework and the thing that you'll notice is that they have a lot in common they both have a custom config section which is able to be used they both have app settings which work the same but in dot net framework I was configuring tracing in configuration this isn't going to work on dot net core now I did add a custom system diagnostic section so if I wanted I could uncomment this and it won't actually fail but it won't have any effect so I may as well just remove it instead what we need to do if I come over and look at program.cs for this demo you can see that most of this code is going to run exactly the same on dot net framework as it is on dot net core you've got app settings working you've got custom configuration sections working but after I create my trace source on dot net framework this would have already been configured by the app.config since that doesn't happen on dot net core I just have one extra call I say configure trace source and I'm adding my listeners and switches when we're targeting dot net core since that's not happening in the config file that's the only difference and it's going to be equivalent if you're using something like WCF clients things like that you do it in code not in config all right interop so there's a number of different APIs you can use for interoperating between managed code and native code and most of them work more or less on dot net core all that said the recommended API here is going to be to P invoke when possible you want to P invoke because that's the only interop option that works cross-platform if you have P invokes you can P invoke on windows on linux or OS X and it all works P invokes have been in dot net core since V1 they're battle tested and they're you know for new development they're kind of the way we recommend now that said you still can use other options like com and C++ CLI but those are going to be windows only because they've got a number of dependencies on the windows operating system com in particular is ready with dot net core 3 so if you have com dependencies you can use them on dot net core the same as you could on dot net framework with only just a few tweaks one of which is that dynamic type support isn't yet ready that's on the roadmap but by and large com works and it's fine as long as you're only targeting windows similarly C++ CLI support is coming for dot net core there's a preview of it if you download the visual studio 2019 16.4 preview you'll get a new CL dot XE which has this slash CLR net core parameter so that you can build a C++ CLI assembly targeting dot net core instead of targeting dot net framework so if you want you can go play around with that today and preview the experience is still a little bit rough because it's not integrated with the build system yet so you're building everything from the command line with you know CL dot XE and link coming in a future 16.4 preview we're going to integrate that with visual studio with MS build with the project system so it's going to feel very similar to working with C++ CLI on dot net framework you'll just select dot net core as the target instead and everything's going to work so P invokes are recommended comms available now C++ CLI will be available with VS 2019 16.4 and dot net core 3.1 it's in preview at the moment when our T interop works same as always the system enterprise services API though are not supported so if you use any of those you're going to have to use an alternative interop mechanism so briefly let's go ahead and take a look at what some of that looks like in code if I come back over here I will show you my interop demo so for this one it's fairly simple I've got this program that's yes that communicates with native code in three different ways first we call a C++ CLI API so I'm calling ijwclass.greet and this is a method from an ijw assembly that I'm building and I'll show you that in a minute then I have a comm reference if we take a look at our project file you can see there's a comm reference here now comm references work for dot net core on windows just like they did in dot net framework so this continues to work for me to open up Excel and interop with Excel but comm reference is only supported on windows so if you have any comm references in your project file you can't build them with dot net build since that's a cross-platform tool you have to use msbuild but as long as you're only building on windows you can use msbuild on windows and it will build comm references they work as before similarly if we want to do a more dynamic approach to comm we can use activator.createinstance with a type from prog id to maybe get a reference to internet explorer and use comm to open up internet explorer and navigate to a page here's where I was saying with dot net framework a lot of times people would use like a dynamic type here and call visible navigate this support isn't ready yet it's on the roadmap though we don't have a definite timeline yet so for the time being on dot net core you're going to call that sort of the these sorts of APIs via reflection the comm reference one works just exactly the same though now this C++ CLI class it's very basic all it's doing is writing to the console with managed code and native code the thing that's interesting to look at if you check out the sample later is that because we haven't integrated C++ CLI support with the build system yet I'm using the 16.4 cl.exe preview to build this so I'm building this particular library with the batch script at the moment it's very low tech but what you'll notice is that I've got one for building it for dot net framework and another one for building it with dot net core the dot net core one's almost the same with just a couple differences you specify slash CLR net core slash slash CLR you have to reference the reference assemblies you're using like system.console for my console.rightline call and when we link you have to be sure to include the ijwhost.lib library to link against because that's where we have information about how to start up a dot net core ijw instance so other than those few changes the process is exactly the same as building a C++ CLI assembly for dot net framework but now it'll target dot net core now for simple C++ CLI dependencies you actually don't have to do this because remember I think Olia talked about it you can reference a dot net framework assembly from a dot net core project it's not recommended because if the dot net framework assembly goes down some code path that uses an API that's not present on dot net core you're gonna run into a run time exception but if for some reason you're not able to get a dot net core or dot net standard targeted version of an assembly either because the NuGet package is old and not maintained or it's a C++ CLI thing that you don't have the code for or something or you don't want to use the preview tools you can still reference the dot net framework assemblies and as long as they don't use any APIs not available to dot net core they'll continue to work it just adds a test burden since you have to make sure that everything works correctly at run time so very briefly I will hop over into my interop folder and although you know what using dot net build and stuff isn't going to work because of my com reference so instead I'm just going to run this one from Visual Studio when you multi-target so again I've got targets with dot net framework and dot net core in one project file here in the start debugging or launch dropdown there's now a framework piece where you can choose which one of those we target so first I'm gonna run it against dot net framework and you will see here that it says hello from an IGW assembly CLR version is 4.0 we start up Excel start up internet explorer but you didn't think you were gonna see an internet internet explorer demo today but there you have it and that works similarly we can switch this over to dot net core and it's gonna look exactly the same difference being that now we're using the dot net core version of the C++ CLI library and we've got some very small code tweaks in how we are launching i.e. since we're not using the dynamic keyword anymore but it all still works on dot net core same as on dot net framework so finally, remoting now I say remoting intentional or otherwise because something I've noticed working with customers that there's a number of cases where people are using remoting APIs when they're not even doing any remoting one of those that comes up a lot is the real proxy type real proxy allows you to wrap an object in a proxy that intercepts calls going to that type and so people use it for an aspect oriented programming design where they'll take cross cutting concerns like logging or caching and add them to objects by wrapping objects in a proxy instead of updating the object types themselves and this doesn't work on dot net core because real proxy uses remoting and it's in the remoting namespace but in order to make this scenario possible we've added a new type in dot net core called system reflection dispatch proxy which doesn't enable any remoting but it allows that same wrapping of an API so that you can intercept calls to it there's also castle dot dynamic proxy which is a third party option that's very similar so between dispatch proxy and dynamic proxy you can replace your real proxy usage another place we see remoting come up inadvertently sometimes is if you're using dot begin invoke or dot end invoke on a delegate under the covers that sort of asynchronous programming model does use remoting in its implementation so you can't do that on dot net core instead you should use task based asynchronous patterns use task dot run things like that got a blog post on this one if you want more details I'm actually working on a blog post on proxies so if you watch the dot net team blog we should have something there soon about proxies as well if you're actually doing remoting this is where it's going to affect you more that dot net core doesn't support remoting so you've got a number of APIs for simple inter process communication on the machine and for more complex scenarios across machine remoting again there's g r p c as per the core if you just want to do it yourself you can use the sockets APIs and again I'd refer you back to the keynote where we had some really cool g r p c demos because that's the technology that's going to become more and more important going forward with dot net core and it's worth checking those out to see how that works I have a demo I'm not going to do I've got a demo of replacing real proxy usage with dispatch proxy but our time is up so I'm just going to go on to here here's the link out to github where you can get the demo code I was showing you if you want to look at it in more detail and I'll let the hosts tell me if we have time for questions but if we do I'm happy to take any questions folks have at this point unfortunately we do not have time for questions you want to turn your camera on for a second for us Mike sure thing thank you let's do that all right and you want to minimize your Skype window because it's like we're getting this Skype inception the all recursive thing there it was a really great presentation everybody was funny because on the chat was we're talking about P and Vogue and all the low level libraries so everybody was not reminiscing but that's how it started we did a lot of that because of all the calm calm plus and visual basics so no it's great ton of great content thank you for referencing all the other content that we presented earlier in the conference and thank you for you for taking the time everybody enjoyed it all right awesome I'm glad to hear that thank you thank you so much all right everybody we are going to be getting our next speaker up and going here so we'll make a Ramita so Hamida is up next we're going to make the slide changes here and we'll be right back see you guys soon