Blog Post

Modern Work App Consult Blog
5 MIN READ

Packaging a .NET Core 3.0 application with MSIX

Matteo Pagani's avatar
Matteo Pagani
Icon for Microsoft rankMicrosoft
Mar 25, 2019

We have talked multiple times on this blog about MSIX and how you can use it to package existing Win32 applications, so that you can improve the deployment and distribution of your Windows applications. If you're starting from a Visual Studio solution (like a WPF or Windows Forms project), the best way to package it with MSIX is the Windows Application Packaging Project, which is a new project's type included in Visual Studio 2017 and 2019. We have talked about this project multiple times, but always using a full .NET Framework project as a reference. However, .NET Core 3.0 is emerging as the new way forward for WPF and Windows Forms application and, even it's still in preview, we can start building Windows desktop .NET Core applications today. So, what if I want to package my WPF or Windows Forms app based on .NET Core with MSIX? Can we still use the Windows Application Packaging Project?

 

The short answer is... yes! However, there are some specific steps to keep in mind. Let's see them!

The first step, of course, is to start from a WPF or Windows Forms project you have already built using .NET Core 3.0. The most recent .NET Core 3.0 version is Preview 3, which you can get from here https://dotnet.microsoft.com/download/dotnet-core/3.0. To work with .NET Core 3.0 apps you'll also need Visual Studio 2019. You can choose between the RC version, which has a Go Live license and it will be automatically updated to the final bits on 2nd April; or you can get the Preview version, which offers access to preliminary features ahead of the official release.

 

If you want to know more about .NET Core 3.0 development, here there are some great resources:

If you don't have an existing project but you want to give it a try anyway, you can just create a new project in Visual Studio 2019 and look for WPF (.NET Core) or Windows Forms (.NET Core) in the list of templates:

 

 

Or you can use the CLI by opening a command prompt and use one of the following two commands:

dotnet new wpf
dotnet new winforms

Once you have the solution opened in Visual Studio 2019, let's follow the same steps we did in the past to package a full .NET Framework application. Right click on the solution, choose Add -> New project and look for the Windows Application Packaging Project template. Give it a name, then press OK.

 

 

Once the new project has been loaded, right click on Applications and choose Add reference. You will see a list of all the projects which belong to your solution. Choose the one which contains your WPF or Windows Forms .NET Core application. At the end, you should see something like this:

 

 

So far, nothing special. It's the same exact process you would have done with a full .NET Framework application. However, as soon as you try to build the Windows Application Packaging Project, you will notice something not working as expected:

2>------ Rebuild All started: Project: ContosoExpenses.Package, Configuration: Debug x86 ------
2>C:\Program Files\dotnet\sdk\3.0.100-preview3-010431\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(228,5): error NETSDK1047: Assets file 'C:\Users\mpagani\Source\Samples\ContosoExpenses-Basic\ContosoExpenses\obj\project.assets.json' doesn't have a target for '.NETCoreApp,Version=v3.0/win-x86'. Ensure that restore has run and that you have included 'netcoreapp3.0' in the TargetFrameworks for your project. You may also need to include 'win-x86' in your project's RuntimeIdentifiers.
2>Done building project "ContosoExpenses.csproj" -- FAILED.

The Windows Application Packaging Project, currently, supports .NET Core 3.0 apps only using the Self Contained Deployment approach. This means that the output package won't include just the application, but also the full .NET Core runtime. The downside of this approach is that the package will be bigger, but the great advantage is that you'll be able to deploy it on any machine, regardless if it has or not the .NET Core 3.0 runtime installed. This is very important especially if you're planning to distribute the application through the Microsoft Store. One of the policies, in fact, requires that all the dependencies of the application must be satisfied during the installation (either because they're included in the package or because they're available on the Store). The user shouldn't have to go to a website and download additional libraries / frameworks / tools to run an application downloaded from the Store.

In order to build a self contained application, however, you must specify which are the supported target runtimes. In such a scenario, in fact, you can't build a cross-platform application anymore, because you must embed inside the executable the specific runtime version for the platform where the application is running. Since we're building a Windows desktop application, this isn't a big deal since WPF and Windows Forms application are still bound to Windows. In order to specify the runtime, we need to edit the .csproj file of our project. Right click on it in Solution Explorer and choose Edit xyz.csproj, where xyz is the name of your project.

In the PropertyGroup section add the following line:

<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>

This is how the first part of your project file should look like:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWPF>true</UseWPF>
    <RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
  </PropertyGroup>

</Project>

If it's a Windows Forms project, instead of UseWPF you will see the UseWindowsForms entry. With this property we have just added, we are configuring our .NET Core 3.0 project to explictly support the Windows platform, both using the x86 and x64 architecture. Of course, if your project supports only one of these two architectures, feel free to remove the not supported one.

That's it! Now just set as startup project your Windows Application Packaging Project and, in Configuration Manager, choose x86 or x64 as target architecture:

 

Try to compile again the Windows Application Packaging Project and this time everything will work. Your application will be packaged, along with the specific .NET Core 3.0 version specific for the CPU architecture you have chosen.

Wrapping up

In this post we have seen how we can use the Windows Application Packaging Project to package not only full .NET Framework applications, but also .NET Core 3.0 ones. This feature has been added in Visual Studio 2019 Preview 2 and makes super easy to leverage two of the most interesting technologies to modernize Windows desktop applications: MSIX and .NET Core 3.0. The only limitation is that, currently, Visual Studio supports only Self Contained Deployment, which means that the whole .NET Core runtime is embedded inside the package. This approach has the great advantage of making the application esaier to distribute but it could be redundant in enterprisce scenarios, when you have many applications depenending from the same framework and, as such, it's preinstalled on each machine. However, the team is already working to enable Framework Dependent Deployment as well, which means that the packaged application will be able to leverage the .NET Core 3.0 runtime installed on the machine.

 

Happy coding!

Updated Mar 28, 2019
Version 2.0
  • Hello juwens ,

    it's possible to achieve your goal but, unfortunately, this feature isn't well documented. These are the steps you have to follow:

     

    1. You have to create a publish profile for your .NET app first. Right click on the .NET project (not the WAP one) and choose Publish. Follow the wizard by choosing:
      • Folder as target
      • Folder as specific target
      • Leave the default publish location
    2. Once you have finished, click on Edit under "FolderProfile" to edit the profile and choose:
      • Deployment ode: Framework-dependent
      • Target runtime: win-x86 or winx-64, based on your requirements
    3. Once you have created it, go to the WAP project and click on the .NET project name you have referenced undered Applications. Choose Properties and you will see a property called Publishing Profile. Open the dropdown and you will she .pubxml you have just created by setting up a publish profile. Be aware that this property is set on a configuration basis, e.g. if you have Debug/X86 selected in your solution it will be set only for that configuration, you can edit your project file manually or change your configuration in VS to set the other values as needed.

    Now generate the MSIX package with the WAP project, just make sure to select the same CPU architecture and configuration you have configured the publishing profile for. At the end of the process, you should get a considerably smaller MSIX package, which will contain only your executable and DLLs and not the full runtime.

     

    I hope it helps!

     
  • levyas I'm glad you found my article helpful! To address your question, the reason is that MSIX and the MSIX bundle concept were born originally for UWP apps, which couldn't be compiled for AnyCPU due to .NET Native (which generates native code, which can't be cross-architecture). As such, MSIX still requires to have different packages for different architectures also for Win32 apps.

  • levyas's avatar
    levyas
    Brass Contributor

    Matteo Pagani Thank you! Your last post helped me to make my MSIX package framework-dependent.

     

    Side-question: why it isn't possible to create "AnyCPU" bundle? I had to adjust platforms and configurations in my solution to make it all work, it's kind of tedious. In the end, my app is .net-only anyway, so AnyCPU worked well.

  • Hi, the MSIX packaging is achieved through a dedicated project, the Windows Application Packaging Project. It isn't a built-in feature of .NET Core, so you can't do the packaging through the dotnet CLI. You will need MS Build to obtain a MSIX package as part of your CI/CD pipeline. The last chapter of my free e-book MSIX Succinctly (https://www.syncfusion.com/ebooks/msix-succinctly) covers exactly the CI/CD scenario. Alternatively, you can follow Exercise 6 of the workshop that my team has built about Windows apps modernization, which is available on GitHub: https://github.com/microsoft/AppConsult-WinAppsModernizationWorkshop

     

    I hope it helps!

  • Could you please share the dotnet CLI commands to build and package the Wpf?  That would be incredibly helpful to customers who are creating CI/CD pipelines or simply don't 'right-click publish.'

  • soumyamahunt's avatar
    soumyamahunt
    Copper Contributor

     Is this issue going to be solved?? I tried to add crash-analytics to my app using Microsoft.Appcenter. Now there is a folder with all different runtimes included in the package, even if I have specified the runtime identifiers to be win10-x86, win10-x64, win10-arm64, win10-arm. By the way I am using .NET 5.

     

  • Hello soumyamahunt the latest version of the Windows Application Packaging Project supports the framework dependent model as well. As such, it is enough to not specify the runtime identifiers in the .csproj file to generate a package which doesn't include the .NET Core runtime.

    Regarding the multiple runtimes you see, this is caused by the App Center library, since it has a dependency on SQLite which is a native library. As such, each folder contains the specific SQLite runtime for each platform supported by .NET 5 / .NET Core.

     

    Best

  • soumyamahunt's avatar
    soumyamahunt
    Copper Contributor

    Matteo Pagani  I get that it includes native library for all platforms in runtime folder. The issue is that I am not targeting linux or os x, also since there will be different packages for different architectures it would make sense only include native library for the architectures in their respective packages instead of including library for all architectures in all packages. This increases the deployment size of the app significantly. Is there any way I can reduce the size of deployment.

  • Hello soumyamahunt , regarding this specific problem I would open an issue on the App Center repository on GitHub: https://github.com/microsoft/appcenter Based on my investigations, I don't this problem is related to MSIX packaging, since I can see that also the standard bin of the .NET 5 application includes all the runtimes, despite I compiled it for x86 only. My feeling is that this is the way the NuGet package provided by the App Center team behaves.


    As a workaround to redurce the size of deployment, you would need to do so manual work (that can be automated with a script) to:

     

    1) Unpack the package generated by Visual Studio

    2) Remove the runtimes you don't need

    3) Recreate the package

    4) Sign the package

     

    You can do this by leveraging makeappx and signtool (which are part of the Windows 10 SDK https://docs.microsoft.com/en-us/windows/msix/package/create-app-package-with-makeappx-tool and https://docs.microsoft.com/en-us/windows/msix/package/sign-app-package-using-signtool), or with a 3rd party tool like MSIX Hero (https://msixhero.net/) if you prefer a GUI based experience.

  • juwens's avatar
    juwens
    Copper Contributor

    Hello Matteo Pagani

     

    i failed to create a framework dependent msix with:

    • .net5 WPF App (depends on native sqlite dll)
    • wap project

    My resulting msix is always about 70MB , though the compiled Framework-dependent wersion would only be 5MB.

    Its so big, because it contains the net framework.

    Excerpt from the msix with 7zip:

    Path: "MyAppPackage_1.0.16.0_x64.msixbundle\MyApp.Package_1.0.16.0_x64.msix\MyApp\"

     

     

    What have i tried without success:

    - removing RuntimeIdentifiers in my csproj (which you've suggested in your post)

    - added SelfContained=False in my wapproj



    Please provide an minimal example on github and/or an explanation on howto create a framework dependent package with net5 (with WPF and native Nuget Dependency) and WAP.

    Native Dependency i depend on: https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/5.0.1?_src=template


    I've created a minimal working example: https://github.com/juwens/WpfNet5Msix/tree/main/WpfCoreApp
    For the sake of simplicity i created it with VS 2019, so the Wpf Proj runs with dotnet core 3.1 and wpf.
    It's super straight forward, open VS create WPF Proj, add Wap Proj, configure Wap to package WpfProj, disable Bundle.
    Nothing else, no nuget packages or any code, to keep it simple.

    The Result: 60MB msix file

     

    Feel free to fork and edit my repo.

     

    Thanks in advance.