Wondering about a practice that a contractor did

5/10/2025

I have a question I want to pose to those of you who are freelance software developers, or have done that in the past. It concerns the way a contractor who works for us, has conducted himself. I’ll explain below.

Currently I work in state government as a senior software engineer, for one of the largest state departments in my state. I’ve been in my position for 10 years. About 7 years ago the task of administering our on-prem TFS was added to me, as the former TFS Admin was leaving. Since then another software engineer was also assigned the TFS Administrator role. Two years ago we were instructed to migrate all our source code from TFS to GitHub in GitHub.com. We are now our state department’s GitHub administrators, too. The code in our TFS is from longer ago than 10 years, as developers moved copies of their code from several years ago, into TFS. Some of this code goes back 15+ years. And because there are so many developers involved over the years, there are hundreds, perhaps thousands of Visual Studio solutions in our TFS, which we’ve migrated to GitHub. It is hard to tell because of the different ways TFS handles what is called TFS Projects (now Azure DevOps (ADO) Projects) vs. what GitHub Projects are like, which is nothing like what TFS/ADO Projects are like. The migration of our code base from our on-prem TFS to a GitHub Organization in GitHub.com took us 9 months, because our employer wouldn’t allow us to use GitHub Import, so we had to do it all by hand. When we migrated our code to our organization in GitHub, we gave all the repositories a visibility status of Internal. A visibility of Internal is available in GitHub Organizations and is between Private and Public. It does allow everyone who is a member of the Organization to see all the code of all those repos, but not modify any of the code unless they are a member of that repo.

Early last year a new contractor was hired, named Richard (not his real name). He was hired just a few weeks after we finished migrating the code out of TFS into GitHub. We added him to the GitHub organization. He then went about looking at various repos. He then complained to everyone about the habit all developers over several years had of leaving important secrets, such as database connection strings, in the source code. This upset everyone because Richard was judging a lot of people’s coding styles when that code had all been written and saved to TFS that was on-prem. i.e: everyone felt like it was safe because it was in-house and when most of it was written the concept of not saving secrets in source code was not considered a bad practice. It was clear to all developers that Richard was just trying to make himself look competent and all the rest of us incompetent, rather than considering the conditions under which that old code had been written. Upper management isn’t aware of these considerations, so Richard looked brilliant, whereas the rest of us didn’t. Most of the code is so old that it is no longer in production, but management wanted it to be migrated for historical reasons. All the new code’s secrets are not kept in the code, but that wasn’t mentioned in Richard’s accusation.

This behavior by Richard at the beginning of his employment with us should have been a warning to me that he isn’t someone I can put full trust in, but I didn’t take heed. This has recently become apparent to me, but even now I’m not sure if it is wrong or only customary practice done by contractors. When the other GitHub Admin and I configured our GitHub organization we developed some rules that we intended to have everyone adhere to. One of these is the default branch would be named main. Another was that on those projects where more than one developer is involved, then no one could create a pull request (PR) to merge their changes back into the default branch, and approve their PR. (We make exceptions for a single contributor to a repo, as we have a lot of projects like that, due to how small the project is, only one developer is needed for the project/repo.) A year ago, when Richard started contributing, he asked that the default branch on the repos he was working in be named master. At the time I thought I would make the exception to our rule, because I know some people who’ve developed software for many years and saved their code changes in Git, are used to the original default branch name of master. So, I created a master branch from main in the two repos Richard was working in, then changed the default branch’s name from main to master. Also, Richard said that having to wait for someone else on the team to approve his PRs was too burdensome, since he lives two time zones east of us and, he claimed, he works late at night and wants to commit and approve his PRs, rather than wait. At the time I thought that a valid argument, so I lifted that restriction as well.

Now I regret having made those two exceptions. One of the repos Richard is no longer working with, so he’s handed that over to another developer, a coworker and regular employee, who has taken it over. This other developer’s name is Charles (not his real name). Charles is used to the default branch being named main, so he asked me to reconfigure the repo with main as the default branch name and move all the code Richard had written from master back into main. This seems like an easy enough request, so I tried doing it. I’ve spent two days at it, because I discovered that I couldn’t merge master back into main, as they no longer have a common commit Git history. This really surprised me, but doing a git log at the command line provided that master’s history had nothing in common with main’s history. I know there are multiple ways of having two branches in the same repo not have a common Git history, but there’s no way I can detemine how Richard overwrote master’s history since all such actions would overwrite master’s history. All I know for certain is that Richard wiped out master’s history 20 days after I had created master.

Now another interesting thing was revealed when I looked further into the code Richard put into master. He had replaced README.md using some completely unrelated project name. The project name isn’t related to anything we have at work. Since Richard works as an independent freelance developer, I conclude that he has taken the source code that he wrote for another client, that was saved in a branch named master and rewrote the history in our repo’s master branch with that other code. I’ll also mention that his repo was for a project he wanted to do for us, because it was for a software solution that although well written, wasn’t anything we needed. It really is the situation where this project is a solution in search of a problem. We already have a third-party product that does much of what Richard’s solution does, so although Richard’s app has some features not in the third-party app, it isn’t something we really needed.

At this point I’ve given you all the pertinent facts. I have limited independent freelance experience. I have never written something for one client, then tried to pass it off to another client as something they should use and pay me for. But, because of my limited experience, I don’t know if what Richard has done is common, acceptable practice amount freelance developers. Please, let me know, as doing freelance work is something I want to do more of. But I don’t want to do something that shouldn’t be done, if it is unethical, if not illegal.

Revisiting Action at a Distance

Three years ago, I did a couple blog posts on the software anti-pattern called Action at a Distance. I thought I was finished with that, but recently I learned of a way of utilizing an in-memory database. I love it! You don’t have to have a SQL Server instance around and it’s perfect for demo purposes. So, I’m going to update the relevant points from those blog posts, taking the in-memory database into consideration.

Learning more about EF Core’s Power Tools

Continuing my series on steps I’ve recently taken to help upskill myself, I decided to go through a tutorial by Eric Ejlskov Jensen on his excellent Entity Framework Core’s Power Tools. This tutorial can be found at Add an admin CRUD web page to your ASP.NET Core web app in 5 minutes using EF Core Power Tools and CoreAdmin. I decided to create an ASP.NET MVC Core website using .NET 6. I stored my code in an Azure DevOps Services private repo. (Note: I use both Azure DevOps and GitHub, alternating between the two. I like to use both and keep my skills at writing CI/CD pipelines using both up to date. Also please note that I am using a local SQL Server Developer Edition database I have at home. Because of that I felt it best to keep this repo private.)

Following Eric’s instructions, I added Ed Andersen’s CoreAdmin NuGet package. (Eric didn’t say this, so be sure to add it.). Then I added the code lines of:

builder.Services.AddSqlServer<JobsearchContext>(builder.Configuration.GetConnectionString("Database"));

builder.Services.AddCoreAdmin();

After adding those lines and the app.MapDefaultControllerRoute() line just before app.Run(), I ran the app and got this when I went to the /CoreAdmin page:

That has all my tables in my local database for easy access!

Tom Murdock

Today I learned that a good friend of mine, Tom Murdock, has passed away. We have long been involved with Microsoft development technologies, going back to VB6. We were involved in a VB group in the Albuquerque, NM area, in the late 90’s. That group eventually died, but along came .NET, C#, ASP.NET, etc. and we (along with a few other people) started the New Mexico .NET User Group (NMUG), still going today.

Tom was one of the biggest drivers of NMUG and supporters of NMUG. It is no exaggeration to say that NMUG exists today, in large part, due to Tom. His friendship was very important to me. He will be very sorely missed.

The Action at a Distance Anti-pattern, Part 2

In the first part of this short series on the Action at a Distance I showed how a colleague of mine had, without knowing it, made the application we worked on, only run once. How the first instance would kill any other instances of it coming up. In this second, and final post I’ll show how another mistake was made which also illustrates the action at a distance anti-pattern. I’ve duplicated the pattern in the Action At A Distance 1 application I introduced in part 1. Please refer to that, as it has screen captures of the three windows that comprise Action At A Distance 1.

In Action At A Distance 1, there’s a Save and Cancel button on both the Authors and the Mystery Books windows. Recall that we were instructed to make the original WPF a launch a separate window for each function that the user wanted to do. I duplicated that UI/UX by having a separate Authors window come up, if the user clicked on the Authors button on the main window. The Authors window comes up as many times as the user clicks on the Authors button on the main window. The same is true with the Mystery Books button on the main window causing a Mystery Books window to popup up, as many times as the user clicks on the Mystery Books button on the main window. I discovered that this could cause the application to save data that the user may not have wanted to save. Or just as bad, it might cause the user to stop saving all data, even data they wanted saved, when canceling a save on another window. I copied the logic my co-workers had written into the original WPF app, into Action At A Distance 1. If you want you can look at the code in my GitHub repo for this project. For this part of the tutorial be sure to be in the master branch.

How Saving could be an action at a distance

To see what happened you have to look in a couple places. First the code from App.xaml.cs. In there the original coder wrote this property:

public static AuthorsModel MainDataContext
{
    get { return authorsModel; }
    set { authorsModel = value; }
}

The coder had written MainDataContext as a global variable seen throughout the application. The next part that contributed to the action at a distance anti-pattern being used was in all of the ViewModels. Here’s the ExecuteSaveCommand() from the AuthorsViewModel:

private void ExecuteSaveCommand()
{
	if (! SelectedAuthor.DateOfBirth.HasValue)
	{
		return;
	}

	//retrieve relevant data
	int selectedAuthorID = SelectedAuthor.ID;
	DateTime newDateOfBirth = SelectedAuthor.DateOfBirth.Value;

	var rec = App.MainDataContext.Authors.Where(a => a.ID == selectedAuthorID).FirstOrDefault();
	if (rec != null)
	{
		rec.DateOfBirth = newDateOfBirth;
		App.MainDataContext.SaveChanges();
	}

}

The problem is the App.MainDataContext.SaveChanges() entity framework command. It will save all pending changes. Including ones that the user might not want to save. Here’s the user case where this is a problem. The user opens Authors to make a change to one of the author’s birthdays. Then they open the Mystery Books window and make a change to one of the books publish date. They return to the Authors window, to save the change to that author’s birthday. But they decide that they publish date was fine the way it was, so they click the Cancel button. Well, its too late, they’ve already saved all of the pending changes.

How Canceling can be an action at a distance

In the original WPF app’s cancel function that my colleague wrote, he had a function which undoes all pending changes. I’ve reproduced the logic in the Action At A Distance 1 app:

        private void ExecuteCancelCommand()
        {
            var changedEntities = App.MainDataContext.ChangeTracker.Entries()
                .Where(x => x.State != EntityState.Unchanged).ToList();

            foreach (var entry in changedEntities.Where(x => x.State == EntityState.Modified))
            {
                entry.CurrentValues.SetValues(entry.OriginalValues);
                entry.State = EntityState.Unchanged;
            }

            foreach (var entry in changedEntities.Where(x => x.State == EntityState.Added))
            {
                entry.State = EntityState.Detached;
            }

            foreach (var entry in changedEntities.Where(x => x.State == EntityState.Deleted))
            {
                entry.State = EntityState.Unchanged;
            }
        }

Here’s how this code can cause an action at a distance anti-pattern mistake, using the Action At A Distance 1 app. Suppose the user opens the Authors app and modify some writer’s date of birth, then before saving their change they open the Mystery Books window and make a change to a publish date. But then they decide they didn’t want to save that change so they cancel the change to the mystery book’s publish date. Now they return to the author’s window and click the Save button. Well, it’s too late, because the above code will have undo all pending changes.

How I resolved these action at a distance problems in the FixedBranch

I removed the global MainDataContext variable from App.xaml.cs.

Then, using the AuthorsViewModel.cs as an example, the ExecuteSaveCommand() I changed as follows:

private void ExecuteSaveCommand()
{
	if (! SelectedAuthor.DateOfBirth.HasValue)
	{
		return;
	}

	//retrieve relevant data
	int selectedAuthorID = SelectedAuthor.ID;
	DateTime newDateOfBirth = SelectedAuthor.DateOfBirth.Value;

	using (var ctx = new AuthorsModel())
	{
		var rec = ctx.Authors.Where(a => a.ID == selectedAuthorID).FirstOrDefault();
		if (rec != null)
		{
			rec.DateOfBirth = newDateOfBirth;
			ctx.SaveChanges();
		}
	}
}

I used an local variable, ctx, to open a connection to the database, update the author’s date of birth, saved it, then closed the connection. This is what’s known as the Law of Demeter, which says you’re only to affect objects near by.

Because my Action At A Distance 1 app is so simple I didn’t have a cancel method – merely closing the window is sufficient to cancel the save.

I hope that this will help others to avoid writing action at a distance in their code. To learn more about action at a distance for software development, check out this Wikipedia article.

The Action at a Distance Anti-pattern

I’ve been writing code for a long time. The last 10 years or so I’ve delved into learning good software design principles, how to use them, when to use them and so on.

Also, I think it is very good for software developers to learn about software design anti-patterns. There are plenty. Indeed, I sometimes wonder if there aren’t more anti-patterns that good software design patterns. 🙂

One such anti-pattern which I’ve known about for years, but only recently learned it’s name, is known as Action at a Distance. At first I wasn’t sure about that name, but upon reflection I realized that it aptly describes what’s happening. Basically, its this, something happens in the application that was influenced by something else, somewhere else in the application, seemingly unrelated to what the user is currently doing. To the user it might almost look like magic. To a software developer, they might call it a Heisenbug, something that’s very hard to pin down, due to the seemingly arbitrary nature of the bug. Action at a distance bugs are hard to duplicate, unless you understand what is going on with that particular bug. And as all developers know, being able to duplicate a bug is paramount to getting it fixed.

Because of the difficult nature of identifying action at a distance bugs, I thought it would be good to explore some examples, which I have seen in production environments. No, I didn’t write them. But I do have an example of action at a distance anti-pattern, which I’ve taken from a place I worked. The application that we wrote was a Windows Presentation Foundation (WPF) application. Just because this is a WPF don’t dismiss it as some are likely to do. The technology involved isn’t as important as the action at a distance anti-pattern. Hopefully, seeing the code fragments I share here will help you identify the same thing in code you have to maintain or have written yourself. This application is a simple app displaying a few mystery authors and a few of their books. Note: This app is not a full featured app. I put it together in a short period of time, to illustrate the action at a distance anti-pattern, rather than being something that someone would use.

This application, named “Action At A Distance 1” (Originally I thought I might make two apps, but later rejected that idea) is not a typical WPF app. Most WPF apps I’ve seen or helped write, bring up a main hosting window, which other user controls come in through navigation. In this job the UI/UX designer rejected the normal WPF apps navigation, instead he chose a system of having lots of child windows pop up in reaction to users clicking buttons. In keeping with that design and especially as it is a part of the cause of action at a distance anti-pattern, I’ve duplicated it in the Action At a Distance 1 UX design. Here is a screen shot of the “landing page” as we called it:

Action At A Distance 1 start window

If the user clicks on the Authors button, then another window popups up with a small list of authors I’ve entered into a database. There can be as many Authors windows as the user clicks on/cares to bring up (that’s the UX design favored by the project manager/product owner):

September 15, 1890 is Agatha Christie’s birthday

The only field that I have editable in this window, is the date of birth field. (Remember, I wrote this in a short time and was more focused upon illustrating action at a distance, than providing a full featured app.) The Save and Cancel buttons are as they are in the app I helped write at that job.

And finally the Mystery Books button brings up the Mystery Books window, which the user can bring up as many times as they wish, following the pattern given us by the project manager/product owner:

This has the book title, author, the genre of mystery and the date of publication as best I could determine

(You can find my Action At A Distance 1 app at my GitHub repo here. I’ve designed this repo to have two branches, the master branch which illustrates two action at a distance issues, and another branch named FixedBranch, in which I show how I resolved the action at a distance problems.)

In the original WPF app at the job, at one point I wanted to bring up that app a second time. After all, I can bring up Excel or Word more than once. So I tried launching it a second time, but it didn’t come up. After testing it again, I decided to get into the Windows Event Viewer and see what it showed me. There I discovered that I was getting an error message that said, “The process was terminated due to an unhandled exception.” I’ve duplicated the problem in this simple Action At A Distance 1 app. If you either clone or download my repo, then open up the App.xaml.cs file, you’ll see in the constructor these lines of code:

if (ConfigurationManager.AppSettings["EnableLogging"] != null)
{
	if (ConfigurationManager.AppSettings["EnableLogging"].ToLower() == "true")
	{
		Logging.CoreEventLog.EnableLogging = true;
		try
		{
			Logging.CoreEventLog.IP_Address = Dns.GetHostAddresses(Environment.MachineName).FirstOrDefault().ToString();
		}
		catch (System.Net.Sockets.SocketException ex)
		{
			Logging.CoreEventLog.LogEvent("Dns.GetHostAddresses", null, null, ex.Message, true);
		}
	}
}

The Logging.CoreEventLog.EnableLogging = true; line invokes the CoreEventLog class. The person who wrote that class, which I’ve copied into this project’s master branch with few changes, has this class level variable assigned:

public static StreamWriter log_file = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + @"\CoreEvent.log", true);

Later in CoreEventLog you’ll see a function named WriteToLogFile(). Here it is from the master branch:

public static void WriteToLogFile(string Action, string Table_name, long? Record_id, string Message, bool? Error)
{
	if (EnableLogging == false)
	{
		return;
	}

	log_file.WriteLine();
	log_file.WriteLine("AppName: {0}", App.Name);
	log_file.WriteLine("AppVersion: {0}", App.Version);
	log_file.WriteLine("AppName: {0}", Environment.UserName);
	log_file.WriteLine("MachineName: {0}", Environment.MachineName);
	log_file.WriteLine("IP_Address: {0}", IP_Address);
	log_file.WriteLine("ActionDateTime: {0}", DateTime.Now);
	log_file.WriteLine("ActionTaken: {0}", Action);
	if (Table_name != null) log_file.WriteLine("TableName: {0}", Table_name);
	if (Record_id != null) log_file.WriteLine("RecordID: {0}", Record_id);
	if (Message != null) log_file.WriteLine("tSQL: {0}", Message);
	if (Error != null) log_file.WriteLine("ErrorMessage: {0}", Error);
	log_file.Flush();
}

Putting this all together, you can see that he opens the .log file at the beginning of the application and never closes it! This is the cause of “The process was terminated due to an unhandled exception” error. Once the first instance of Action At A Distance 1 gets a hold of the .log file, no other instance of Action At A Distance 1 can ever run again. At least not from the master branch.

If you have cloned/downloaded my repo, then switching to the FixedBranch you’ll see how I’ve resolved this issue. In the CoreEventLog.cs file I got rid of the class level variable named log_file. Then I changed the WriteToLogFile() method to look like this:

public static void WriteToLogFile(string Action, string Table_name, long? Record_id, string Message, bool? Error)
{
	if (EnableLogging == false)
	{
		return;
	}

	using (var log_file = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + @"\CoreEvent.log", true))
	{
		log_file.WriteLine();
		log_file.WriteLine("AppName: {0}", App.Name);
		log_file.WriteLine("AppVersion: {0}", App.Version);
		log_file.WriteLine("AppName: {0}", Environment.UserName);
		log_file.WriteLine("MachineName: {0}", Environment.MachineName);
		log_file.WriteLine("IP_Address: {0}", IP_Address);
		log_file.WriteLine("ActionDateTime: {0}", DateTime.Now);
		log_file.WriteLine("ActionTaken: {0}", Action);
		if (Table_name != null) log_file.WriteLine("TableName: {0}", Table_name);
		if (Record_id != null) log_file.WriteLine("RecordID: {0}", Record_id);
		if (Message != null) log_file.WriteLine("tSQL: {0}", Message);
		if (Error != null) log_file.WriteLine("ErrorMessage: {0}", Error);
		log_file.Flush();
	}

}

Here you can see that I made use of the using command to instantiate the local log_file variable. Then I’ve followed the original code to write the unhandled exception to the .log file. Once that block of code exists, then the connection to the .log file is closed.

I will resume this tutorial, showing the other action at a distance error illustrated in this small app, in another blog post.

Trying to change my email account to push to GitHub

Something happened, when I last tried to push a commit to GitHub on a public repo I’ve started there. It told me that I might expose my private email address, which of course I don’t want to do. I’ve only been using git since last year (2019), so I’m not an expert at git, yet.

I’ve changed what email is used in my local git repo associated with my GitHub repo. However, I’m still getting an error. Talking with the GitHub support people they’ve suggested that it I might have to modify the last email address used in the previous commit to my GitHub repo. I’ve been looking around for a possible way of handling that. This link (interestingly enough on Atlassian) might help. If I’m on the wrong track, I’d love to know!

Undoing Commits in Git

I’ve begun using Git for my personal software development about a year ago. So far, I’ve been very careful in what I commit and push back to the current branch. Making sure that it’s good before proceeding.

But any good VCS must have a undo function. And so far, I haven’t learned how to use that. Kinda silly of me; as that’s what any VCS should do. So, I’ve been searching the Internet to learn how Git does it. I’ve found this excellent, detailed description on how you can do it with Git on the command line.

Next I’d like to learn how to do this in Visual Studio Code and Visual Studio 2019. I’d appreciate any pointers to good references on doing this, please.

Visual Studio’s Shared Projects

Background and Motivation

At work we’ve been slowly replacing really old, legacy applications with some more modern technologies. This is so we can better maintain them and put them under source control. (Something that the old “apps” couldn’t do.) Our approach has been to write a single, base project in Visual Studio. Once we did that we branch the main project within source control, into new projects to replace an old, legacy app. (Yes, I know that our habit of branching within source control into a new VS solution which never gets merged back into main, isn’t standard. I’m working on trying to convince my colleagues that isn’t standard procedure.) This was OK, at the beginning. But the violation of the DRY principle has really got me wondering how we could better approach this, especially when it comes to code components which are identical for all of the projects, including the main trunk. Once we get to the point of having 10 or so projects, it’s going to become very tedious to have to make any necessary modifications to the common components within all of those projects.

Shared Projects, a way to help

So, I started to look around for some way of mitigating this. I came across something called Shared Projects within Visual Studio, which I didn’t even know existed. I learned that Shared Projects have been a part of Visual Studio since version 2015. I found a tutorial by Praveem Kumar called Shared Project: An Impressive Feature of Visual Studio 2015 Preview. It is very helpful, with only one problem for me. Praveem put the shared project in the same Visual Studio solution with the 3 client projects that he used to illustrate using shared projects. It’s my hope that at work we’ll create one solution and put one or more shared projects into it. Then later when we branch the main trunk into a new solution, we’d reference the shared project. So, what I’ve done to illustrate how I think we should do it is created 2 VS 2017 solutions, one with the Shared Project and the other a Windows Forms project that consumes the Shared Project, which I’ve shared in my GitHub account.

The Shared Project: MySharedProject

Following Preveem’s tutorial, to a point, I created a MySharedProject solution in VS 2017. (I decided to use .NET Framework 4.6.2.) You can find it in my GitHub repo MySharedProject. My GetEmployeeDetail() method is a little different in the property assignments and I was able to use string interpolation, which wasn’t available to Preveem when he wrote his.

public string GetEmployeeDetail()
{
EmployeeID = 247;
FirstName = "Ved";
LastName = "Prakasj";
Phone = "5055551234";
Address = "Albuquerque, New Mexico";
return $"Name: {FirstName} {LastName}\nEmp ID: {EmployeeID}\nAddress: {Address}\nPhone: {Phone}";
}

One thing I noticed that was different from what Praveem got, was that I couldn’t compile my version of the MySharedProject. Indeed, it wasn’t even an option:

Couldn't Build

My Client App: SharedWindowsFormsApp

Next I created a new VS 2017 solution that I called SharedWindowsFormsApp, which I’ve got in a different GitHub repo. (Please note that on my system, I put both repos under one folder. Also note that I decided not to replicate both the MVC and console apps that Praveem did.) My code for SharedWindowsFormsApp isn’t really different in any way from Praveem’s. So, how to make SharedWindowsFormsApp aware of the shared project? I right mouse button clicked on the solution and choose Add Existing Project:

Add Existing Project

Looking for the shproj file:

Select shproj

Now my SharedWindowsFormsApp’s solution looks like this:

New SharedWindowsFormsApp solution

Of course, I had to add a reference to the shared project, in SharedWindowsFormsApp project:

Add a Reference to MySharedProject

One thing that I thought would happen is I thought Visual Studio would copy the MySharedProject code under that SharedWindowsFormsApp solution, into a folder named MySharedProject. I opened up File Explorer on my Windows 10 machine to verify that, but found I was wrong! So, I opened up the SharedWindowsFormsApp.csproj file to see what was going on.  At the bottom I found this:

Solution View

Looking at this you can see the first Import is a relative reference to the place, on my machine, where I found the .shproj file. And it has the attribute of Label=”Shared”.
Once that was done, I could build SharedWindowsFormsApp and it ran fine:

Running SharedWindowsFormsApp