Wednesday, July 30, 2025

A lifetime of weird issues: VSCode and more

Upleveling my gripes + making better software

People who read my blogs know that I'll write mostly about things not working: painful Bluetooth protocols, awkward APIs, and inconvenient programs. That's good, but what advice would I give someone who's looking for a plan to improve, say, VSCode for C# developers.

Count up friction points, and reduce them

A classic PM thing is to make up a metric, measure it, do some work, and declare success. And hopefully measure again, so there's before and after numbers, but weirdly, that's not valued as much as you might think.

So what are the obvious friction points for VS Code? And for reference, here's the start-up screen for my VS Code. Look at all those projects!



The "Recent" list isn't actually my recent files. There's four things listed (blinky, ConstantHtmlTraffic, GenerateWiFiQrCode, and AstronomyOnThePersonalComputer. But in reality, the last files I edited was a set of Markdown files.

The Icons on the left are random, not planned. I have a longer gripe about not meeting the Windows design language, but the icons are particularly bad. Why is the "file" icon marked as "explorer"? Why is the universal "settings" gear marked as "options"? Why is there a "command palette" and yet there's also a "tasks" which isn't a set of my tasks; it's some other commands.

The POV of the app isn't very clear. The original POV was simple: it's an editor. Now it's morphed into a container of random stuff. 

Forward and back aren't consistent. For example, go to the welcome screen, and then run Github. The welcome screen is replaced by a big GitHub screen, but the "back" button doesn't take me back.

 Friction when figuring out a next step due to tone: The explorer in-line help says: "You haven't opened a folder" which feels accusatory instead of helpful. I've opened plenty of folders in my life including folders for VS Code. I just don't have one open right now. There's a fun old anecdote from the early days of computers. The IBM engineers had a hot new (and very expensive) computer with a light marked "idle". Watson had them change it to "ready". "Idle" means that a very expensive computer is wasting money; "Ready" means that an expensive computer is ready to get to work.

Friction because extra steps are demanded but not required: The button "Clone Repository" says "Clone a repository once the Git extension is activated". But in fact, the programmer need do nothing at this point.

Random UX is harder to puzzle out. The button "Clone Repository", when clicked, instantly jumps to a random-feeling part of the screen (it overlays on top of the search bar)

Friction in decoding what dialog boxes mean: The clone repo text box (sorry, there's no better name for it) says I can "pick a repository source". AFAICT, this is then tied to the second item in the fake dialog box, where I get clone from GitHub, but only if I log in. Which is weird, because I'm already logged into GitHub

Friction in cloning the repo. After cloning a repo, I'm asked if I want to open it. That's weird; it's hard to imagine a work flow where opening the repo I just cloned isn't the right choice. 

Friction in open folder. Clicking Open Folder after cloning a repo doesn't go to the folder I just opened. A common practice in Windows is for dialogs to remember previous uses.

Builds are flaky. I asked Chat how to build one of my projects. The resulting command was incorrect (it referenced a non-existing directory hierarchy for the csproj file). When the command was provided with a correct csproj file, it didn't build. The same project builds perfectly in Visual Studio.

Frenetic UX increased anxiety without providing value. When running a build command, the terminal frantically updates a clock every tenth of a second. As an example of the opposite way of life, I present my "Low Distraction Work Timer" that's designed to be entirely non-frenetic.

Added friction from the non-standard UX. VS Code doesn't follow any of the modern Windows UX principles here. It's not effortless or calm, or notably personal (in particular it loves to turn on dark mode even though I've never turned on dark mode on anything, ever). It's not familiar, it's not complete + coherent, or any of the other principles. 

 Solution! How to make it better!

Inspired by the Quick Settings Metric.

The best way to figure out what's wrong with VS Code (or any platform) is to design some simple work flows and use the Quick Settings Metric methodology: write down every single "decision" a user would have to make.

Some example workflows include:

  1. Clone a C# project, find a line of code, set a breakpoint, and debug it
  2. Create a WinUI3 C# project, add the latest WebView2, and make a one-page UX with a textblock title, a variable-size webview with a button to go to a single hardwired site with a custom User-Agent, and a scrolling textblock to track the webview navigation. Verify that the user-agent is correct. 
  3. Create a Blazor web page ...
  4. Create a command-line app ...

 Part of the Quick Settings Metric is that the harder the "decision" the user has to make, the more points it costs. Clicking an OK button is relatively cheap; selecting from a list is more, and making the user type something in is even more. For VS Code, telling the user to "log in" but they have to type in their MSA is more expensive than the better solution of allowing users to select from their already-in-use MSA.




Tuesday, July 29, 2025

Wierd issues in the WinUI3 / WebView2: Solving the User-Agent problem

 TL/DR: To verify that you are setting the right user-agent, you have to check the "web resource requested" event's copy of the request headers, not the "navigation starting" event's copy. And you need to call AddWebResourceRequestedFilter.


Background: why am I setting the user-agent, and why do I need to check it?

I'm writing a simple app to parse NMEA data from a small GPS tracker (because that's what I do for fun, that's why). I'm at the part of the project where I want to make a GPS trace, and the technology I've picked is the Open Street Maps project using the leaflet JavaScript library. It's actually really nice: it works smoothly, and the integration was mostly simple.

But ... one of the requirements Open Street Maps has is that if you make an app that works against their servers, you have to use a custom User-Agent header in the HTTP requests. This makes sense: they can filter requests based off of the user-agent if needed.

After a bit of work, it's not hard to set the user-agent. But how to verify that it's correct? The easiest way was to set an event handler on the WebView2 NavigationStarted event. That event provides a CoreWebView2NavigationStartingEventArgs object that includes the request headers. I just have to iterate through them (easy-peasy) and they check the User-Agent value.

And it's not set!

Side quest #1: timing

Web searches show that people do have trouble setting the User-Agent. In particular, it must be set before you get a navigation started event. But in my app, that's not a problem.

Side quest #2: threads and services

The documentation also helpfully points out that the user-agent is thoroughly screwed up if you have multiple WebView2 objects. Each object might well share some of the underlying bits, and so when you have two WebView2 objects, and the user-agent is set differently on them, you might well have the the wrong user-agent sent. This is just dumb, but I remember from previous work with Edge that it's a pretty complex beast.

Solution: check based on the WebResourceRequested event. This requires setting the event in a different place than normal (it must be set after calling the EnsureCoreWebView2Async method). It also requires setting a WebResourceRequestFilter, which hopefully won't cause a slowdown in my app. That callback also includes all of the request headers, and those headers show the correct User-Agent. 

Links: see my question at Setting a WebView2 UserAgent isn't updated in the NavigationStarting event - Microsoft Q&A

 


Monday, July 21, 2025

Weird issues in Visual Studio 2022: Solving the CS0534 error when solving the Json trimming problem

 TL/DR: Visual Studio 2022 is wrong, and your code is OK

Background: I'm having to switch from the awesome NewtonSoft JSON library over to some crappy System.Text.Json library (because VS thinks that breaking everyone to support Trimming is somehow ever OK). The sample code for that is wrong, but that's OK, it points to the right code.

Except that if you copy-paste the sample code, it fails:


The error, BTW, is CS5034: 'SourceGenerationContextErrorCS0534' does not implement inherited abstract member 'JsonSerializerContext.GetTypeInfo(Type)' and also '...does not implement inherited abstract member 'JsonSerializerContext.GeneratedSerializerOptions.get' and also 'CS7035: There is no argument given that corresponds to the required parameter 'options' of 'JsonSerializerContext(JsonSerializeroptions?)'.

Solution: just recompile. It turns out that every single thing about the error is wrong. The correct solution is to "recompile your code". That's because Visual Studio 2022 is focused on everything other than the day-to-day experience of programmers.


Saturday, July 19, 2025

Weird issues in Visual Studio 2022 ┄ missing publish profiles with WinUI Blank App Packaged

 

TL/DR: Desktop .NET Core apps might be missing a Publish directory because it's in the .github ignore list. The files are actually all very similar and can be copy-pasted from another project.

Background: 

A part of the .NET Core is that projects are both "built" and "published". This has apparently been around for ages. The problem is that there's a terrible split in the tooling: some parts of the tooling consider "publishing" to be essential and won't work if you don't have some publishing files set up. But other parts of the tooling consider "publishing" to be dangerous and therefore should be excluded from GitHub.

Helpful links:

Format of the .gitignore file: git-scm.com

There is no findable documentation for pubxml files. A web search shows a bunch of information related to ASP.NET

Solution:

The solution has two parts: get new version of the missing pubxml files and add them to GitHub.

1. Get new pubxml files from a new WinUI Blank App (packaged) project that you create in a temporary directory. Copy the entire PublishProfiles directory (it has to be called that for the builds to work). The directory should have three pubxml files in it.

AFAICT, the files are always the same for each project.

2. Update your .GITIGNORE file to allow the PublishProfiles directory to be saved. Warning: not all pubxml files should be put into GitHub. Some pubxml files include secret information like passwords and connection strings.

Perhaps the safest change is to add this line:


# 2025-07-19: shipwreck software: WinUI Blank App (Packaged) requires the win-arm64.pubxml,
# win-x64.pubxml and win-x86.pubxml files for the Release mode compile to work
# BUT pubxml files can include secret information, so we should not just allow all
# pubxml files to be added to GitHub.
!win-*.pubxml



Error messages:

These error messages pop up when building a WinUI Blank App Packaged (2025-07-18)

Code Description Project File
NETSDK1198 A publish profile with the name 'win-x64.pubxml' was not found in the project. Set the PublishProfile property to a valid file name. WinUI_Blank_App_Packaged_2025_07_15 C:\Program Files\dotnet\sdk\9.0.302\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Publish.targets
NETSDK1102 Optimizing assemblies for size is not supported for the selected publish configuration. Please ensure that you are publishing a self-contained app. WinUI_Blank_App_Packaged_2025_07_15 C:\Users\USER\.nuget\packages\microsoft.net.illink.tasks\8.0.18\build\Microsoft.NET.ILLink.targets


Tuesday, July 15, 2025

VS: Don't start your project names with letters!

 Weird issues in Visual Studio 2022 -- an ongoing saga

Ever get an error in a brand new project saying that "App.WinRTVtable.g.cs" is failing and that "global::WinRT is a namespace?Tearing your hair out because what in the world could you have done to make this fail?

It turns out it's your project name. When you make a WinUI Blank App (Packaged) in Visual Studio 2022, and the project name starts with a number, it makes a broken project.

Links:

GitHub project demonstrating the problem: link.

Existing bug: Build Errors when assemblyname starts with a number · Issue #1880 · microsoft/CsWinRT

The bug claims it will be fixed soon in "2.3.0", whatever that is.


Here's the dump from the Output window for building.

1>------ Build started: Project: 2_WinUI_Blank_App_Packaged_2025_07_15, Configuration: Debug x64 ------

1>C:\temp\2025\testrepos\VSBugReportSolution\2_WinUI_Blank_App_Packaged_2025_07_15\obj\x64\Debug\net8.0-windows10.0.19041.0\win-x64\intermediatexaml\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\_2_WinUI_Blank_App_Packaged_2025_07_15.App.WinRTVtable.g.cs(4,40,4,53): error CS0118: 'global::WinRT' is a namespace but is used like a type

1>C:\temp\2025\testrepos\VSBugReportSolution\2_WinUI_Blank_App_Packaged_2025_07_15\obj\x64\Debug\net8.0-windows10.0.19041.0\win-x64\intermediatexaml\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\_2_WinUI_Blank_App_Packaged_2025_07_15.App.WinRTVtable.g.cs(4,56,4,104): error CS0103: The name 'WinUI_Blank_App_Packaged_2025_07_15VtableClasses' does not exist in the current context

1>C:\temp\2025\testrepos\VSBugReportSolution\2_WinUI_Blank_App_Packaged_2025_07_15\obj\x64\Debug\net8.0-windows10.0.19041.0\win-x64\intermediatexaml\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\_2_WinUI_Blank_App_Packaged_2025_07_15.App.WinRTVtable.g.cs(4,2,4,164): error CS1729: 'WinRTExposedTypeAttribute' does not contain a constructor that takes 3 arguments

1>Done building project "2_WinUI_Blank_App_Packaged_2025_07_15.csproj" -- FAILED.