Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build fails when contents include certain C# System DLLs #1691

Open
chris-t-jansen opened this issue Dec 11, 2024 · 9 comments
Open

Build fails when contents include certain C# System DLLs #1691

chris-t-jansen opened this issue Dec 11, 2024 · 9 comments

Comments

@chris-t-jansen
Copy link

Apologies if this is a known issue already, I checked for a duplicate to the best of my ability.

I'm trying to create an installer for an application I've built in the Unity game engine, but I'm running into an issue with certain .dll files being included in the contents of the installer. Built Unity games are packaged with all the .dll files they need to run (e.g. MyGame/MyGame_Data/Managed/UnityEngine.dll), and that includes a number of C# libraries that seem to be colliding with the libraries that WixSharp uses to create the .msi installer, specifically System.dll and System.Windows.Forms.dll.

System.Windows.Forms.dll

In the case of the System.Windows.Forms.dll file, the build completes with a warning, and the .msi installer seems to work without issue. The gist of the issue seems to be these lines in the warning:

There was a conflict between "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" and "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".

"System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" was chosen because it was primary and "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" was not.

Removing the System.Windows.Forms.dll file from the build directory resolves the issue. Here's the full error for completeness:

System.Windows.Forms Error Output
Warning (active)	MSB3277	Found conflicts between different versions of "System.Windows.Forms" that could not be resolved.
There was a conflict between "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" and "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".
    "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" was chosen because it was primary and "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" was not.
    References which depend on "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" [...\MyInstaller\Contents\MyApp_Data\Managed\System.Windows.Forms.dll].
        ...\MyInstaller\Contents\MyApp_Data\Managed\System.Windows.Forms.dll
          Project file item includes which caused reference "...\MyInstaller\Contents\MyApp_Data\Managed\System.Windows.Forms.dll".
            System.Windows.Forms
    References which depend on or have been unified to "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" [C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll].
        C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.WPF.dll
          Project file item includes which caused reference "C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.WPF.dll".
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.WPF.dll
        C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.dll
          Project file item includes which caused reference "C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.dll".
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.dll
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.WPF.dll
        C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.dll
          Project file item includes which caused reference "C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.dll".
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.dll
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.WPF.dll
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.MsiEventHost.exe
            C:\Users\Christian\.nuget\packages\wixsharp_wix4.bin\2.4.3\lib\WixSharp.UI.dll	MyInstaller	C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64\Microsoft.Common.CurrentVersion.targets	2412		

System.dll

In the case of the System.dll file, however, the build fails completely, citing an issue of being unable to find the Queue<> type name:

Error (active)	CS1069	The type name 'Queue<>' could not be found in the namespace 'System.Collections.Generic'. This type has been forwarded to assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Consider adding a reference to that assembly.	MyInstaller  ...\MyInstaller\Dialogs\FeaturesDialog.xaml.cs	229

This then leads to two cascading errors raised from the same FeaturesDialog.xaml.cs file:

Error (active)	CS0019	Operator '!=' cannot be applied to operands of type 'method group' and '<null>'	MyInstaller  ...\MyInstaller\Dialogs\FeaturesDialog.xaml.cs	247
Error (active)	CS0119	'Extensions.Parent(XElement, string)' is a method, which is not valid in the given context	MyInstaller  ...\MyInstaller\Dialogs\FeaturesDialog.xaml.cs	248

Removing the System.dll file from the build directory resolves the issue.

Project Information

I used the WiX4 version of the Managed UI (WPF) template to create the installer, but set it to just use the default WPF dialogs when the errors began. I have WiX version 5.0.2 installed and WixSharp version 2.4.3 installed (specifically: WixSharp-wix4.WPF 2.4.3). Here's my Program.cs file for completeness:

Program.cs
using System;
using System.Windows.Forms;
using WixSharp;
using WixSharp.UI.WPF;

namespace MyInstaller
{
    public class Program
    {
        private static string companyName = "My Company";
        private static string productName = "MyApp";
        private static string productVersion = "0.1.2";

        static void Main()
        {
            var project = new ManagedProject(productName,
                              new Dir(@"%ProgramFiles%\" + companyName + @"\" + productName + @"\" + productVersion,
                                  Files.FromBuildDir(@"Contents")));

            project.GUID = new Guid("some-actual-guid");
            project.Version = new Version(productVersion);

            project.ManagedUI = ManagedUI.DefaultWpf; // all stock UI dialogs

            project.BuildMsi();
        }
    }
}

Any help or guidance is greatly appreciated.

@oleg-shilo
Copy link
Owner

I will try to reproduce it.
Will see if I can find any clue but I think on my environment I have the same versions of the tools.

@chris-t-jansen
Copy link
Author

And if I can provide any more details or information to help, let me know

@oleg-shilo
Copy link
Owner

OK, I quickly realized that I will succeed and always will if I include the same "system.dll" as the build script is running with.

Can you please share with me a simple "hello-world" style sample that shows the problem? It will be enough for it to include those two problematic assemblies and to have the same deployment structure as yours.

@chris-t-jansen
Copy link
Author

Here's a minimal project where I was able to reproduce the same errors I detailed: DllErrorExample.zip

It includes copies of the same two .dll files that caused my original errors, copied directly from that original project, in the Contents folder of the project. This should allow you to hopefully reproduce the errors.

@oleg-shilo
Copy link
Owner

Great, I can reproduce the problem now.
Leave it with me.
Will update after I dive into it

@oleg-shilo
Copy link
Owner

WOW, you have discovered a .NET bug. A juicy one.

First, I found that there is nothing wrong with your WixSharp project and you can even build the msi from the batch file project.BuildMsiCmd(...) if you rename your system*.dll files to build the batch file and rename the assemblies back before running the batch file.

The next break through came when I realize that even if I remove EVERYTHING from static main, the project still fails. Meaning that it is the project structure that is causing the problem.

The next one... is a console app sample (attached). It has only one line implementation:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var ttt = new Queue<int>();
        }
    }
}

And it fails from the moment you drop the Contents folder in the project dir.

ConsoleApp1.zip

Let me think about it. It's hard to "fix" .NET problem from the WixSharp code.

@oleg-shilo
Copy link
Owner

OK, fortunately the WiX toolset has enough flexibility to trick the .NET build system.

You will need to rename the offending files so the .NET builder is not upset. But in your wxs file you need to add an extra attribute Name to the File element. This attribute should be the file name as you want it to appear on the target system. Thus if you add a file to the project manually you should rename your Contents\system.dll to Contents\_system.dll and then in the code do this:

// instead of this 
// new File(@"Contents\System.dll"), 

// use this
new File(@"Contents\_System.dll") { TargetFileName = "System.dll" },

But since you are using Files.FromBuildDir aggregator, you will need to inject XML attributes manually:

project.WixSourceGenerated += (doc) =>
{
    doc.FindAll("File")
       .Single(x => x.HasAttribute("Source", a => a.EndsWith("_System.dll")))
       .SetAttribute("Name", "System.dll");

    doc.FindAll("File")
       .Single(x => x.HasAttribute("Source", a => a.EndsWith("_System.Windows.Forms.dll")))
       .SetAttribute("Name", "System.Windows.Forms.dll");
};

I have attached the corrected working sample.

DllErrorExample.zip
. . .

And... I do recommend that you report the .NET bug. Having some files in the project structure (e.g. to be included as resources) should bot fail the build.

@chris-t-jansen
Copy link
Author

Well, finding a .NET bug was not on my 2024 bingo card but I guess here we are! Thanks for figuring that out, I would never have solved that on my own! The XML injection solution you proposed worked wonders, and I was able to get my Unity program to install.

Do you think this issue is worth adding some functionality to WixSharp to avoid in the future? Like some kind of prefix action to automatically rename .dll files beforehand and automatically inject the appropriate XML? Given that I seem to be the first person to bring it up, it doesn't seem to be too common (which I find surprising), so it may not be worth it.

As for reporting the bug, where do you think would be the most logical place to report it?

@oleg-shilo
Copy link
Owner

Do you think this issue is worth adding some functionality to WixSharp to avoid in the future?

Not exactly, but it's kinda already there. WixSharp allows you to have the file name on the target system different two what you have during the build:

new File(@"Contents\_System.dll") { TargetFileName = "System.dll" },

If you want to have automated solution with wild cards (as in your your sample) then you can unpack the FromBuildDir, which is extremely simple:

public static Files FromBuildDir(string buildDir, string fileExtensions = ".exe|.dll|.xml|.config|.json")
    => new Files(buildDir.PathJoin("*.*"),
                 f => f.EndsWithAny(true, fileExtensions.Split('|')));

And if you now use Files you can massage the File object that was created on your behalf. Something like this:

string fileExtensions = ".exe|.dll|.xml|.config|.json";
. . .
new Files(@"..\Release Folder\test\*.*", x => x.EndsWithAny(true, fileExtensions.Split('|'))")
{
    OnProcess = file =>
    {
        file.TargetFileName = Path.GetFileName(file.Name).Replace("_System", "System");
    }
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants