Experiences in porting UWP app to Win3 and .Net 8

Sometimes I play a developer on the Internet. This is one such day. Let me tell you about my latest experience…

Background

I have a looooong running side project that I started over 40 years ago.

I was a developer writing in a variant of Fortran on proprietary hardware, OS, and even are own compilers. The company decided to get out of the hardware business to focus on software (in essence giving our graphics hardware to a fledging startup called Sun Micrososystems).

I needed to learn “C” fast. I had one of these new things called a PC (a Compaq Clone actually) at home and so I bought a “Mark Williams C compiler” and decided to play. The PC didn’t have a graphics card, so I first wrote a “Curses” library that would handle the text display without just scrolling down. But what to use that for? I decided to write a solitaire game. It was the classic Klondike game, using Ascii characters to “draw” the cards on the screen and a command prompt to enter moves, like “T2F” to move the exposed card on the second table pile to the foundation. I distributed it freely on bulletin boards of the day.

Now over time I have added a lot of other games into the collection (currently 121), but rewrote the software to port it to newer technologies as they became available. Graphics support, mouse support, Unmanaged C++, .Net Framework, Managed C++, and finally a port to C# and UWP. It is about 30,000 lines of code in the UWP release 5.x. By the way, the game is free in the Microsoft Store (SolmanSolitaire).

But this month I took on the task to modernize again to C# with WinUI 3 and .Net 8. This is the story.

Earlier failed attempts

I tried to modernize this UWP app before, but abandoned those efforts when it appeared that the work was too great. I actually failed at that three different times, from .Net 2 and up. But now I have success on the fourth effort.

The key to my porting success

There are two reasons for success this time. I used the preview Visual Studio Extension “.Net Upgrade Assistant” from Microsoft (0.5.651.27530) to help.

1) Scope limit. In the past, porting to a new thing for me on this project involved pretty much a complete rewrite. The games all run on a “rules engine” that I create when I port, and then over time I add new games that need different kinds of rules and eventually the rules themselves start to smell. I have found that re-writing those rules for commonality is a big boon to inventing new kinds of games down the road. And this was part of the previous failures, as that part of the rewrite would get interrupted by the other things I needed to do as part of porting. In this instance, I decided to avoid all of this and just deal with the porting.

2) Better converter from Microsoft. Microsoft has had a converter to help you take your existing project to the then latest version of .Net for many years. And those converters were… let’s just say minimally helpful. The project converter today is much better. Not perfect, but much better.

What I did.

Opening the current UWP solution in Visual Studio, added the Upgrade Assistant extension, shut down VS and opened it back up.

To run the wizard, you just right click on the project and chose “Upgrade”. I had it use the “side-by-side” method, where it adds a new project into the existing solution for the conversion. So all the files were copied over into a separate folder for the conversion with a separate project. Since all existing customers would not be able to take the new version (Windows 11 only and we are talking consumers from all over the world), it was necessary for me to continue to be able to service both customer bases.

A lot of the grunt work was done by Microsoft for the conversion, and it provided a list of things that I’d have to handle myself. That list wasn’t complete, but it was a great start.

Namespaces

The new project output involved changing the namespaces from Windows.xxx to Microsoft.xxx. It is a fairly simplistic change, and Microsoft used the same names most of the time to make this easy (not like going from .Net Framework). This was just minutes.

Unfortunately, while the tooling converted the “using” statements just fine, it missed all of the code that referenced the full class namespaces. So, there was a lot of effort manually changing those over. And no, I couldn’t just do a global replace because there were a few that the new framework didn’t actually have, but I could still use the old reference on those. So that was another couple of days work.

Missing Nugets

Ah those wonderful NuGet Packages that made my life so easy, like to show analysis scatter plots of how people play particular games. Yeah, it was a Microsoft NuGet for UWP but they don’t have one for WinUI. But fortunately, I discovered ScottPlot and it was easy to rewrite the code to it to replace the functionality. It only took a day to find it, plus one more to do the work. It even runs much, much, faster and looks better too.

I should probably mention that I don’t make money on this software. It’s a free app up in the Microsoft store so spending money on paid NuGet packages was out of the question.

There were four others I had to replace; each took a couple of days to figure these dependencies out.

WinUI 3 changes

Changing the user interface from UWP to WinUI was much easier than the port from .Net Framework to UWP. But it was not without it’s challenges.

The UWP app had a MainWindow and a single MainPage. And that remained in place in WinUI. It’s just that I had to move some things from the MainWindow class to the MainPage class and visa-versa. This required running a build in the Visual Studio debugger to figure out what had to move.

Audio clips and Notifications

The app has some small audio clips which proved problematic. Not only were the old interfaces not working, the replacement (Windows.Media.Playback) turned out to be more complicated than I expected. For example, a beta tester complained that launching the game, even though it wasn’t playing any sounds, would cause their background music streaming app to stop playing.

Similarly, the notification calls needed manual intervention.

My developer cert expired

OK. Not due to the port, but I still had to create yet another certificate since the one from a year ago expired.

It still Gauls me that I have to do this. It is a self-made certificate and the only requirement is that I keep the subject name the same. Microsoft can’t verify that I signed it, and strips it out to replace with their cert when I publish to the Microsoft store. So why do we have to deal with this? If the rogues can get my partner credentials for uploading to the store, surely, they must be smart enough to be able to make their own cert with that subject field, no?

Publish Profile Issues, Bundles, and Upload files

Once I had something ready, I wanted to upload to the store. This is where I learned about PublishProfiles.

The old produce was built as a AppXBundle with both a x86 and x64 version. This was automatically done by the “Publish and Package” menu on the project right-click. [A pet-peeve of mine is that since I only do this about once a year I can NEVER find this menu. Why is it not next to the “Publish” item on the same right-click?].

But when I used the Publish and Package wizard, it would not let me add the x86 build version. I was OK with this, as the game would only run on Windows 11 systems and I am willing to believe that the universe of x86 Windows 11 systems is infinitely small. I also was unable to convince the solution Configuration Manager to let me add the AnyCPU project type. So I just asked for the x64 build to be packaged. Whether or not I requested it to be bundled, I would only get the MSIX package file format, whereas previously the wizard created the bundle and placed it in the appxupload filetype. And breaking into that msix file I found it was actually not the x64 build.

Eventually, I learned about PublishProfiles and the FolderProfile.pubxml file. After manually editing the solution, project, and this FolderProfile files to clean things up, I eventually kind-of got what I needed. The Wizard would produce an msix file with just the x64 build.

But because I want to upload to the store, I need an upload file. It turns out that this is just a zip file containing the package or bundle file, and optionally the appxsym or msixsym file containing the symbols.

Making the msixupload file was simple enough, but didn’t work when updating the new submission to the store. There were two problems. First, because the original app was a bundle, the new upload also needed to include a bundle file. Second, I could not get the msixsym file to be accepted, the store always complained that it was an invalid file type (even if I renamed it to appxSym).

Info on the internet told me how to create the bundle by using the MakeAppX command using the bundle option. Although that always failed saying that “bundle” was unexpected. I was using the latest MakeAppx commands from the SDK. Fortunately I had an old copy squirreled away that I had taken from the Microsoft GitHub project “msix-packaging” (the old version still hasn’t been replaced when I checked).

So I put the MSIX file in a folder by itself, and told the old MakeMsix to create the bundle out of all (one) files in the folder. I then created a zip file containing just the bundle and renamed the extension to msixupload.

This I could submit to the store. The 6.0 release using WinUI 3 and .Net 8 is still in beta at the time of this posting, but is available via email request.

Summary

The upgrade assistant was extremely helpful in this port. It cannot do everything for you, but the move from UWP to WinUI3 was considerably easier than I anticipated. The resulting app looks much better in WinUI3.

Just image what the upgrade assistant will be able to do if it EVER gets out of preview!

By Tim Mangan

Tim is a Microsoft MVP, and a Citrix CTP Fellow. He is an expert in App-V and MSIX.