Referencing different libraries depending on the architecture in C# - c#

Is it possible to add to my C# project a reference to a different dll versions in x86 and x64 (and switch automatically between them, while changing Configuration Mode)?

I don't think there's anything in the IDE that will do this, but you can accomplish this by manually editing the C# project file.
Something that looks like: <Reference Include="ThirdPartyAssembly" />
Could be changed to:
<Reference Include="ThirdPartyAssembly.x86" Condition="'$(Platform)' == 'x86'" />
<Reference Include="ThirdPartyAssembly.x64" Condition="'$(Platform)' == 'x64'" />

Related

How to avoid addition of *.g.wxs file in my Wix Sharp setup project?

I have created a wix sharp setup project and it works well. msi is created as desired.
Unfortunately, wix (sharp?) creates a folder called 'wix' in the project and places an autogenerated file in it. I really don't like that. Is there a way to avoid this modification of my source files?
It's possible to remove file again in csproj by adding the 'remove' line. I should however prefer not to have it soiling my source code, so I'm still looking for a better solution!
<ItemGroup>
...
<None Include="wix\$(ProjectName).g.wxs" />
<None Remove="wix\$(ProjectName).g.wxs" />
</ItemGroup>
<Reference Include="WixSharp, Version=1.11.0.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2, processorArchitecture=MSIL">
<HintPath>..\packages\WixSharp.bin.1.11.0\lib\WixSharp.dll</HintPath>
</Reference>
<Reference Include="WixSharp.Msi, Version=1.11.0.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2, processorArchitecture=MSIL">
<HintPath>..\packages\WixSharp.bin.1.11.0\lib\WixSharp.Msi.dll</HintPath>
</Reference>
<Reference Include="WixSharp.UI, Version=1.11.0.0, Culture=neutral, PublicKeyToken=3775edd25acc43c2, processorArchitecture=MSIL">
<HintPath>..\packages\WixSharp.bin.1.11.0\lib\WixSharp.UI.dll</HintPath>
</Reference>
BR, Anders
The answer (for me) was to use nuget wixsharp.bin instead of wixsharp as per (primary wixsharp developer) Oleg Shilo's response here:
https://github.com/oleg-shilo/wixsharp/issues/661
Oleg wrote (in case the link is lost in the malmstroem of the internet):
oleg-shilo commented [2019-08-11]
Yes it is possible. Instead of WixSharp NuGet package use
WixSharp.bin. It will not modify the project. Though then building the
project will simply compile the builder console application
{ProjectName}.exe. And you will need to execute this app from your
project post-build event.
The rest is the same.

Is it possible to reference an assembly depending on the solution configuration?

I know I can reference an assembly depending on the project configuration, but can I do it based on the solution configuration? I'm thinking of something like this:
<ItemGroup Condition="'$(SolutionConfiguration)' == 'Debug1'>
<Reference Include="Library1">
<HintPath>C:\Path\To\Library1.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(SolutionConfiguration)' == 'Debug2'>
<Reference Include="Library2">
<HintPath>C:\Path\To\Library2.dll</HintPath>
</Reference>
</ItemGroup>
If not like this, is there any other way I can reference one or another assembly depending on the solution configuration?
Solution configurations are linked to project configurations. For each solution configuration, we specifiy which project configurations to use. For example, when we select solution Release, projects Release are typically selected.
So create two project configurations matching the two solution configurations you want for the dll you want. When you select a solution configuration, different project configurations will be selected and the correct dll will de referenced.

References not being resolved for Saxon's Nuget package in ASP.NET Core Application

I'm developing a .NET Web application that is relying on XSLT 2.0 and in turn Saxon-HE NuGet(version 9.8.0.12 - latest at the moment of writing this) from Saxonica is used for transformations because .NET supports only XSLT 1.0 at the moment (.NET Framework 4.7.1).
When working with a ASP.NET Core Application targeting the .NET Framework and trying to instantiate a Saxon.Api.Processor, I get a runtime System.IO.FileNotFoundException saying:
"Could not load file or assembly 'saxon9he' (...) The system cannot find the file specified."
The necessary .dll is actually missing from \bin folder. I looked at .nuspec file for Saxon-HE nuget that's located in \Users\MyUser\.nuget\packages\saxon-he\version-number and it lacks references (or dependencies) to all necessary libraries, only the reference to saxon9he-api.dll is present.
<references>
<reference file="saxon9he-api.dll" />
</references>
When investigating the \lib folder of nuget I found that there are both saxon9he.dll and IKVM dll's that are required by the NuGet package. So I edited the .nuspec file and added missing references there.
<references>
<reference file="saxon9he-api.dll" />
<reference file="saxon9he.dll" />
<reference file="IKVM.Runtime.dll" />
<reference file="IKVM.OpenJDK.XML.API.dll" />
<reference file="IKVM.OpenJDK.Util.dll" />
<reference file="IKVM.OpenJDK.Text.dll" />
<reference file="IKVM.OpenJDK.Core.dll" />
<reference file="IKVM.OpenJDK.Charsets.dll" />
</references>
And now the Saxon's processor is instantiated without errors. The problem is that this is no good because NuGet updates will brake it and it will not work on other machines that will not have edited the .nuspec file (and that shouldn't be done in the first place).
However, when doing the same thing in ASP.NET Web Application (.NET Framework) Saxon works as expected and dependencies are found. Difference here is that NuGet packages are kept in solution folder and I guess references are being resolved differently.
Am I doing something wrong or is it a bad NuGet pakage?
Is there a way to tell if NuGet package supports .NET Core Projects?
Do you have any suggestions how to solve this?
After more research, I have found on Saxon issue tracker that this same question has been asked to Saxon team on 2016-08-10; ;. It turns out that NuGet package I was referring to is not produced by Saxonica. They are saying that they should release official NuGet package themselves, but no indication that that's actually going to happen.
As Martin Honnen pointed out, here are instructions on how to tell if a NuGet package is compatible with .NET Core - https://learn.microsoft.com/en-us/dotnet/core/porting/third-party-deps .
Got same issue. You can add necessary dll's manually by editing your .csproj file:
<ItemGroup>
<Reference Include="IKVM.OpenJDK.Charsets">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.OpenJDK.Charsets.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Core">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.OpenJDK.Core.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Text">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.OpenJDK.Text.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Util">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.OpenJDK.Util.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.XML.API">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.OpenJDK.XML.API.dll</HintPath>
</Reference>
<Reference Include="IKVM.Runtime">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\IKVM.Runtime.dll</HintPath>
</Reference>
<Reference Include="saxon9he">
<HintPath>..\..\..\packages\saxon-he\9.5.0.2\lib\net35\saxon9he.dll</HintPath>
</Reference>
</ItemGroup>
This way it will work after NuGet package reinstall and on any machine. But you need change this if you change package version.

Difference between referencing System by itself and System along with System.Xml.Linq in project.config and csproj files

What's the difference between referencing System in my .csprojfile and referencing System, System.Xml, and System.Xml.Linq?
Some backstory:
Using Visual Studio 2017 Community 15.4.4, I decided to upgrade the NuGet packages that my projects were using. Post-upgrade, I noticed that my packages.config and .csproj files now contain "extra" entries.
For example, previously my .csproj files contained (among other things) a simple line that stated:
<Reference Include="System" />,
but now, post upgrade, they also include the lines
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq"/>
Likewise, my project.config files gained entries like:
<package id="System.Xml.Linq"... >
that previously did exist.
Creating a test solution, I wrote a small function that used a LINQ select statement, printed an enum that lives in System.Xml.Linq, and then manually removed the <Reference Include="System.Xml.Linq"/> in the .csproj file, and everything seemed to compile normally.
So why were these extra entries automatically added? Do they serve a purpose?
why were these extra entries automatically added? Do they serve a purpose?
Not sure why your packages.config files contain "extra" entry <package id="System.Xml.Linq"... >, may be error added or added as dependency for some packages (if you want to dig deeper into the reason for this issue, you should find out why this package added).
Generally, when you create a .NET framework project, the following .NET framework references would be added by default:
<Reference Include="System" />
<Reference Include="System.Xml.Linq"/>
Then you open the package System.Xml.Linq, you will find the this package only include a .NET framework 2.0 lib:
After install this package by nuget, the following lines will add into your project file:
<Reference Include="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\System.Xml.Linq.3.5.21022.801\lib\net20\System.Xml.Linq.dll</HintPath>
<Private>True</Private>
</Reference>
But if we install this package, above lines will replace the line <Reference Include="System.Xml.Linq"/>. Not sure why it not replace that line.
So that is the reason why you remove the reference to System.Xml.Linq in your .csproj file had no ill effects, Visual Studio still get the dll from the package.

Options for using System.Data.SQLite in a 32bit and 64bit C# world

I understand WHY the System.Data.SQLite.dll is provided in 32 bit and 64 bit builds. So lets not dwell on that and move on. :)
Since it is done this way it seems to make pure C# development a tad more difficult with 3 choices to make.
Is to support only 32-bit and force there managed
assembly to compile x86 and deal with that in running in 32 or 64
bit, and there by lose advantages of when you are on a 64 bit
environment.
Is to force 64 bit and only support 64 bit and losing the
ability to run on 32 bit but gaining all the advantages of 64 bit.
Is to create two versions of their assembly one that
compiles x86 and uses 32 bit SQLite and another that compiles x64
and uses 64bit SQLite. It prevents using "ANY" as a compile option
and being able to easily deploy a single build to either type. Its
not so horrible to manage from a development point of view as we
will need two projects. Only having the C# code officially in one,
and the other will just use "links" to the code in the other. This
is for compiling purposes only. Still leaves us with having to
manage two outputs to for deployments.
With all that said I am only looking for confirmation that the above are the only correct choices.
If however there are other choices that I am overlooking please let me know. Specifically if there is way to get a single C# DLL that can compile to ANY so it can take advantage of 32 or 64 bit depending on where its run and still use System.Data.SQLite.dll.
This is an elaboration of the answer from Springy76. Do this:
public class AssemblyResolver
{
public static void HandleUnresovledAssemblies()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += currentDomain_AssemblyResolve;
}
private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name == "System.Data.SQLite")
{
var path = Path.Combine(pathToWhereYourNativeFolderLives, "Native");
if (IntPtr.Size == 8) // or for .NET4 use Environment.Is64BitProcess
{
path = Path.Combine(path, "64");
}
else
{
path = Path.Combine(path, "32");
}
path = Path.Combine(path, "System.Data.SQLite.DLL");
Assembly assembly = Assembly.LoadFrom(path);
return assembly;
}
return null;
}
}
Make sure the paths generated point to the correct locations for either your 32 bit or 64 bit SQLite Dlls. Personally I've had good results with those in this NuGet package: http://www.nuget.org/packages/SQLitex64
(You only need to use the NuGet package to get hold of the compiled SQLite Dlls. Once you've got them, remove the reference to SQLite in your project created by NuGet and the NuGet package itself. Indeed, leaving the reference in place can interfere with this solution as SQLite will never be recognised as an unresolved assembly.)
Call 'HandleUnresolvedAssemblies()' as early as possible, preferably during any Bootstrapping.
There are 2 common solutions for keeping your main application at AnyCPU:
Install both the x86 and the x64 assemblies into the GAC: They can (should!) have identical assembly names and the GAC will automatically decide whether to use x86 or x64 version.
Hook into AppDomain.AssemblyResolve and serve the right assemblies from subdirectories using Assembly.LoadFrom
The similar problem exists with Oracle's ODP.NET vis-a-vis native 32/64-bit OCI DLLs.
We have solved it by crating both 'x86' and 'x64' platform for our project and then manually editing our project file to use the conditional references:
<Choose>
<When Condition="'$(Platform)' == 'x64'">
<ItemGroup>
<Reference Include="Oracle.DataAccess, processorArchitecture=x64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ODP.NET\x64\Oracle.DataAccess.dll</HintPath>
</Reference>
<Content Include="..\ThirdParty\ODP.NET\x64\oci.dll">
<Link>oci.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x64\orannzsbb11.dll">
<Link>orannzsbb11.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x64\oraociei11.dll">
<Link>oraociei11.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x64\OraOps11w.dll">
<Link>OraOps11w.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</When>
<When Condition="'$(Platform)' == 'x86'">
<ItemGroup>
<Reference Include="Oracle.DataAccess, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ODP.NET\x86\Oracle.DataAccess.dll</HintPath>
</Reference>
<Content Include="..\ThirdParty\ODP.NET\x86\oci.dll">
<Link>oci.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x86\orannzsbb11.dll">
<Link>orannzsbb11.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x86\oraociei11.dll">
<Link>oraociei11.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThirdParty\ODP.NET\x86\OraOps11w.dll">
<Link>OraOps11w.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</When>
</Choose>
This enabled us to avoid using 2 different projects. I'm sure you can do something similar for
SQLite.
Its not so horrible to manage from a development point of view as we will need two projects.
That's not true - you can use two build configurations within the same project. At deployment time, you'll need to build for both x86 and x64, but the code base and project can be the same.
I currently do this in a larger production project, both due to SQLite, but also other native/interop libraries that include x64 and x86 variants.
Usually option one would be the way to go unless your app needs more than 4gb of memory. Remember a 32bit app on a 64bit OS has most of the advantages of 64bit without the many of the disadvantages. That is why x86 is the default target for .exe apps in VS 2010.
Branko Dimitrijevic said, "I'm sure you can do something similar for SQLite." And that is correct. :)
Having the very same issue, I found Rodney's question and Branko's answer and tried it myself. For anyone who wants to see my working SQLite implementation, here you go:
<Choose>
<When Condition="'$(Platform)' == 'x64'">
<ItemGroup>
<Reference Include="System.Data.SQLite, Version=1.0.88.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>System.Data.SQLite\x64\System.Data.SQLite.dll</HintPath>
</Reference>
</ItemGroup>
</When>
<When Condition="'$(Platform)' == 'x86'">
<ItemGroup>
<Reference Include="System.Data.SQLite, Version=1.0.88.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>System.Data.SQLite\x86\System.Data.SQLite.dll</HintPath>
</Reference>
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
Of course, you can name HintPath to anything you like.
I found this to be the perfect solution: I can maintain a single project and quickly switch between target platforms as needed. The only potential downside is I can't see the Reference in the Solution Explorer. But that's a small price to pay for the overall functionality.
You can also resolve this by changing the compile options in your Visual Studio:
To change your compile settings in Visual Studio:
Go to the startup project of your program.
Open the properties window.
Click the compile tab.
Click advanced compile options.
Change the target CPU options to x86.
Your program will now always run in 32 bit mode, even when run on a 64 bit machine.
You could also provide two distributions, one for each environment as mentioned above. While this will become the standard in the future, for my current project this was the best and easiest option.

Categories