Automatic native and managed DLLs extracting from Nuget Package - c#

This is driving me crazy for several months now and I'm still not able to achieve it. My managed libraries are extracted from the Nuget package but not the natives ones.
We have a bunch of managed and native libraries provided by another company.
We have both x86 and x64 version of them. In order to use them in an ASP.NET Core project I have to create an Nuget Package.
My architecture is:
an ASP.NET Core class library that I changed to target full .NET Framework only. This project references my Nuget Package
an ASP.NET Core website also targeting full .NET Framework and referencing the class library
Of course, at the end, I need my native libraries being extracted to the proper runtime folder of the Website (eg: \bin\Debug\net461\win7-x64).
For the moment my solution was:
to put the native libs inside the build folder
create a targets file that copies them to the $(OutputPath) (which is even not the runtime folder)
add some MsBuild commands to the xproj of my website to get the targets file in my $(USERPROFILE)\.nuget\packages\ folder and execute it
copy by hand the native DLLs now extracted in bin folder to the runtime one
I've tried to copy them directly to the runtime folder using some configuration in project.json (I honestly don't remember all the things I've tried for this part) but this was always failing. Also even though I specified SkipUnchangedFiles="true" in my targets file, this is just ignored and my DLLs are copied to my bin folder during each build.
This is an heavy process just to achieve a DLLs extracting, now I really want to get rid of all that MsBuild and get a much simpler solution.
I know with newer versions of Nuget, it's now capable of extracting them natively without any help of adding custom MsBuild commands. As written here, C# projects don't even need a targets file
Next, C++ and JavaScript projects that might consume your NuGet package need a .targets file to identify the necessary assembly and winmd files. (C# and Visual Basic projects do this automatically.)
I kept a tab opened in my browser for several month (original link) and realize this resource has been recently removed from Nuget website. It was explaining how to use the runtimes folder to automatically extract natives DLLs. However I've never been able to get a successful result as it was explained. Now this page has been deleted and replaced by this one with so few explanations and not talking about this runtimes folder at all.
My guess is that I should use runtimes folder for native DLLs and the lib one for managed but I'm not 100% sure of that. (also should I use the build folder?)
I've tried several things (I can't recall number of attempts, as I said several months of headache...) like this architecture (I don't understand here what's the point of having build/native and also natives folders under runtimes)
I also tried to use the .NET framework version structure as described here for my managed libraries.
This seems to be also part of the solution
The architecture is ignored by the compiler when creating an assembly reference. It's a load time concept. The loader will prefer an architecture specific reference if it exists.
One trick you can use to produce an AnyCPU assembly is to use corflags to remove the architecture from your x86 assembly. EG: corflags /32BITREQ- MySDK.dll. Corflags is part of the .NET SDK and can be found in VS's developer command prompt.
That's what I did, converting both x86 and x64 DLLs to AnyCPU (don't know if it does something for x64 DLLs but I didn't get errors) and then tried several different architectures in my Nuget package but still not working.
The default runtime without any entry in project.json is win7-x64, so I decided to explicitly specify it just in case
"runtimes": {
"win7-x64": {}
},
So this is the Runtime Identifier I'm using for all my attempts in my Nuget package. However I don't care about the windows version. I would actually prefer having win-x86 or win-x64 but it seems to be an invalid value according to this page
Windows RIDs
Windows 7 / Windows Server 2008 R2
win7-x64
win7-x86
Windows 8 / Windows Server 2012
win8-x64
win8-x86
win8-arm
Windows 8.1 / Windows Server 2012 R2
win81-x64
win81-x86
win81-arm
Windows 10 / Windows Server 2016
win10-x64
win10-x86
win10-arm
win10-arm64
However this Github source is describing more RID so which source is right?
As you can see, there is so many mysteries here, mostly because of the lack of documentation or even contradictions between different docs.
If at least I could have a working example, then I could perform my tests to answer other questions like trying generic win-x64 RID or see if I can include once my managed libs whatever the .NET Framework version.
Please pay attention to my special context: I have an ASP.NET Core project targeting the full .NET Framework
Thanks for your answers, I'm desperate to get this simple thing working.

I will try to explain all the pain and solutions I've been through as detailed as possible. In my example I use simple text files AAA86.txt, AAA64.txt and AAAany.txt instead of native DLLs to simply demonstrate the extraction process.
First thing you need to know:
If you try to mix the native NuGet's architecture with a lib folder containing some managed libraries, IT WILL NOT WORK
In that case your managed DLLs will be copied to your project's output directory but NOT your native ones.
Thanks to Jon Skeet who pointed me in the good direction, advising me to take a look at the Grpc.Core package. The trick is to create a targets file that will handle the DLL extraction.
Your targets file should contain something like this
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
<Content Include="$(MSBuildThisFileDirectory)..\runtimes\win-x64\native\AAA64.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>AAA64.txt</Link>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Platform)' == 'x86' OR '$(Platform)' == 'AnyCPU' ">
<Content Include="$(MSBuildThisFileDirectory)..\runtimes\win-x86\native\AAA86.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>AAA86.txt</Link>
</Content>
</ItemGroup>
</Project>
Also make sure your .targets file is named the same as your AssemblyName. So if the name of your assembly is DemoPackage, your targets file should be named DemoPackage.targets. Otherwise, the .targets file might not be applied when referencing the package in another project.
Now few other things you need to know:
1) Visual Studio doesn't care at all about the settings you choose, it will always use a dummy RID. (In my case I always end up with a win7-x64 folder even though I'm on Windows 10...)
2) The platform setting in your project.json is also totally useless
{
"buildOptions": {
"platform": "x64"
}
}
3) In the runtimes settings if you set only win and/or win-x64
"runtimes": {
"win": {},
"win-x64": {}
}
Visual Studio will instead use win7-x64. But if you add win10-x64 while you are on a Windows 10 machine then this will be used
4) If you compile your application with a generic RID like this
dotnet build -c debug -r win
Then your targets file will receive the architecture of your machine (x64 in my case) instead of AnyCPU as I was expecting
5) With only native libraries without any managed ones, the extraction will work without a target file if you follow the architecture runtimes/RID/native
6) With only native libraries in my package, the chosen RID will always be win-x64 building with Visual Studio as I told you the runtime folder always created is win7-x64, no matter the configuration I select. If I only had one single win RID in my package then it would successfully be picked.
EDIT:
As a last useful note, when working on such tasks, you might find it convenient to print out the current directory from which your .targets file is being executed like this
<Target Name="TestMessage" AfterTargets="Build" >
<Message Text="***********************************************************" Importance="high"/>
<Message Text="$(MSBuildThisFileDirectory)" Importance="high"/>
<Message Text="***********************************************************" Importance="high"/>
</Target>
Your directory will be printed out in the Build output in Visual Studio

The problem of native DLLs not being copied to the output directory is gone nowadays when you use dotnet restore instead of nuget.exe restore.
The issue was solved in my case when using specifically dotnet restore --runtime win-x64 <path_to_the_solution.sln>.

As a continuation to the answer of #Jérôme MEVEL,
GeneratePathProperty is a new feature which is available with NuGet 5.0 or above and with Visual Studio 2019 16.0 or above.
Using this feature, you can know the exact path of the installed package in the consumer of the package.
If the package is named Mypackage.ID, then you can refer to its path as: $(PkgMypackage_id) with replacing dot '.' with underscore '_'.
In the package consumer
Add GeneratePathProperty="true" attribute to the PackageReference
<ItemGroup>
<PackageReference Include="Some.Package" Version="1.0.0" GeneratePathProperty="true" />
</ItemGroup>
In the package creator:
Path of the package can be reached as given below:
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
<Content Include="$(PkgMyPackage_Id)\runtimes\win-x64\native\AAA64.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>AAA64.txt</Link>
</Content>
</ItemGroup>
Notice the path is: "$(PkgMyPackage_Id)\runtimes\win-x64\native\AAA64.txt
For more details read: GeneratePathProperty

Related

What is the correct way to do a "dotnet publish" on Project1 before building Project2

I have a solution with 2 projects. One is a VSIX and the other is a .NET Core 2.1 Console app.
The projects do not directly depend on each other, but I need to ensure that VSIX embeds a fresh version of all output files from the other project as it is built, because it will need them at runtime.
Putting the following in the post-build event of the VSIX project used to work:
cd "$(SolutionDir)MyCoreConsoleApp"
dotnet publish --configuration $(Configuration)
It's worth mentioning that there were some strange problems with this setup in the beginning, though... The console app could be built fine if done by issuing rebuild of the project from Solution Explorer directly. Issuing dotnet publish from VSIX's before-build, on the other hand, would fail with errors, basically saying the build system could not locate all the required files.
After a lot of effort, I determined that the problem was the fact that console app used Fody Costura at the time. After removing this dependency, dotnet publish started working without any problems.
Fast forward to yesterday... I added a dependency on MSBuild Community Tasks to console app project, because I wanted to use the Zip task to embed a zipped copy of some content files as EmbeddedResource. I've added a custom Target to the csproj and confirmed that it worked correctly when doing a direct build of the project or issuing dotnet publish from command line. In case it matters, the Target itself looks like this:
<Target Name="ZipAndEmbedWwwRoot" BeforeTargets="BeforeBuild">
<CreateItem Include="wwwroot\**\*">
<Output ItemName="ZipFiles" TaskParameter="Include" />
</CreateItem>
<Zip ZipFileName="$(IntermediateOutputPath)wwwroot.zip" WorkingDirectory="wwwroot" Files="#(ZipFiles)" />
<ItemGroup>
<EmbeddedResource Include="$(IntermediateOutputPath)wwwroot.zip">
<LogicalName>wwwroot.zip</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
But now building the VSIX started failing again. The error is:
The "MSBuild.Community.Tasks.Zip" task could not be loaded from the
assembly
C:\Users\MyUserName\.nuget\packages\msbuildtasks\1.5.0.235\build\..\tools\MSBuild.Community.Tasks.dll.
Could not load file or assembly 'Microsoft.Build.Utilities.v4.0,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
The system cannot find the file specified. Confirm that the
declaration is correct, that the assembly and all its
dependencies are available, and that the task contains a public class
that implements Microsoft.Build.Framework.ITask.
As soon as I remove the custom Target, things start working again.
I'm starting to think I'm probably missing something when calling dotnet publish on a project, which has additional msbuild dependencies. This was probably the reason why Fody Costura didn't work, either.
So the question is, how should I be doing this, then?
The community Zip task is quite old and no longer works with recent versions of MSBuild or the cross-platform dotnet CLI.
However, VS 2017 15.8 and the CLI 2.1.400 introduced the built-in ZipDirectory task you could use instead.

Change the referenced dll in a SSIS script task at build / deploy or runtime

Recently I have added all of our SSIS projects into a continuous integration pipeline. The projects are built using MSBuild in TeamCity, packaged and pushed to a nuget feed. We deploy them using Octopus and some hand cranked PowerShell built on the back os SQL server management objects (SMO). It all works, with the exception of one project. The project in question contains some script tasks which reference an external assembly. That assembly is built in the same pipeline and its assembly version numbers are updated by part of the process. The problem lies in the fact that the SSIS project now references a strong named dll in the GAC which does not exist because the version numbers have changed.
Does anyone know of a way to either updated the reference at build time on the CI server or override the version number at the point of deployment?
I know this post is quite old but it has been viewed a lot so here's a solution I have found.
You need to include a 'targets' file for the build.
Here's an example Targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<E10BOs>C:\Integrations\E10 Uplifts\Epicor 10.2.600.3</E10BOs>
</PropertyGroup>
<Target Name="BeforeResolveReferences">
<CreateProperty Value="$(E10BOs);$(AssemblySearchPaths)">
<Output TaskParameter="Value" PropertyName="AssemblySearchPaths" />
</CreateProperty>
</Target>
</Project>
The property group E10BOS (and there can be more than 1) then defines the path to the dll's of the version you want to build against.
It needs to be saved as myTargetsFile.targets
Then in a regular VS project you could add this line to the project file (outside of VS in notepad)
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), myTargetsFile.targets))\myTargetsFile.targets" />
</Project>
This uses GetDirectoryNameOfFileAbove to search up the folder tree until it finds you targets file and then imports it. Very useful if you have a lot of different projects all requiring a version change and you don't have to figure out any relative paths!
In SSIS however this doesn't seem to work.
What does is hard-wiring the path to the targets file in the package .dtsx file, again edit it in notepad and add the following line (you will probably see the csharp entry near the end of the project tag as before:
<Import Project="C:\Integrations\E10 Uplifts\Epicor 10.2.600.3\myTargetsFile.targets" />
This will pass through the information of the project references into the scripts.
Then with all of you projects using a targets file changing the version is done by changing the path to the version folder you want them to use.
Hope that helps?

System.DllNotFoundException on Mono SQLite

I've been trying to figure this out lately. It is working on my Windows machine, where I got SQLite from NuGet, but...
When I put System.Data.SQLite.dll and SQLite.Interop.dll straight from my Windows machine into Linux server it says that SQLite.Interop.dll is not found, but I am sure I see it next right to executable.
Then I tried to compile System.Data.SQLite.dll with /p:UseInteropDll=false, but with no luck. This time it says that System.Data.SQLite.dll is not found.
What is this "not found" mystery?
No code changes necessary. You can build it yourself.
apt-get install build-essentials unzip
Download the SQLITE source code - you want the full source code. Currently called sqlite-netFx-full-source-1.0.104.0.zip.
unzip and cd Source,
chmod +x the compile-interop-assembly-release.sh build shell script, then run it ./compile-interop-assembly-release.sh. - It'll build an .so file in the ../bin directory.
Copy this .so file to the directory that has your application in
Run your application as normal.
Note: Ensure that your SQLite database and the directory it's inside of are writable by the user you're trying to run as.
Use Mono.Data.SQLite.dll on Linux. Take a look at the Mono manual to using SQLite on Linux or build the System.Data.SQLite.dll on Mono.
You can also map the DLL:
<configuration>
<dllmap dll="sqlite" target="libsqlite.so.0" os="linux"/>
<dllmap dll="sqlite" target="libsqlite.0.dylib" os="osx"/>
<dllmap dll="sqlite3" target="libsqlite3.so.0" os="linux"/>
<dllmap dll="sqlite3" target="libsqlite3.0.dylib" os="osx"/>
</configuration>
I started the development in Windows, but then moved the application to Mono (Ubuntu 14), which is where the SQLite provider failed to load as OP described.
I had to recompile the System.Data.SQLite.dll using the following command:
MSBuild System.Data.SQLite.2012.csproj /t:Rebuild /p:UseInteropDll=false /p:UseSqliteStandard=true
However, after this I've got the following exception:
The provider did not return a ProviderManifest instance.
Method System.Data.SQLite.UnsafeNativeMethods:GetSettingValue (string,string)' is inaccessible from methodSystem.Data.SQLite.EF6.SQLiteProviderManifest:GetProviderManifestToken (string)'
To fix this, I had to recompile the System.Data.SQLite.EF6.dll using the following command:
MSBuild System.Data.SQLite.EF6.2012.csproj /t:Rebuild /p:UseInteropDll=false /p:UseSqliteStandard=true
After copying all of the generated files to Mono project's bin directory, everything worked.
The SQLite provider source code version I had used was 1.0.98.1.
Hope this saves someone a lot of time...
I tried all the above option but those options could not solve SQLite DLL problem, It may be because I am using ubuntu 18 version, So tried other option and here the steps,
1) Download SQLite source code from the https://system.data.sqlite.org/downloads/1.0.111.0/sqlite-netFx-full-source-1.0.111.0.zip
2) unzip source code and cd to unzip directory
3) Run the following command in the terminal,
xbuild /p:Configuration=Release /p:UseInteropDll=false /p:UseSqliteStandard=true ./System.Data.SQLite/System.Data.SQLite.2010.csproj
4) Above command would create a dll file at following path,
sqlite-netFx-full-source-1.0.111.0/bin/2010/ReleaseMonoOnPosix/bin
5) Copy System.Data.SQLite.dll to your project bin folder.
6) Clean project and build again.
I hope this would help.
Starting with System.Data.SQLite.Core 1.0.109 you don't need to compile anything yourself since the native SQLite.Interop.dll files are included in the NuGet package for all platforms (Linux, macOS and Windows). Note that although the dll extension is used for all platforms, the files are actually native dynamic libraries (usually suffixed dylib on macOS and so on Linux).
$ find ~/.nuget/packages/system.data.sqlite.core/1.0.111/runtimes -name SQLite.Interop.dll -print0 | xargs -0 file
…/runtimes/linux-x64/native/netstandard2.0/SQLite.Interop.dll: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=96ce4120b31bad7d95f7b9ccf7c4bbb7717ae0b1, with debug_info, not stripped
…/runtimes/osx-x64/native/netstandard2.0/SQLite.Interop.dll: Mach-O 64-bit dynamically linked shared library x86_64
…/runtimes/win-x86/native/netstandard2.0/SQLite.Interop.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
…/runtimes/win-x64/native/netstandard2.0/SQLite.Interop.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows
Unfortunately, the MSBuild target responsible for copying the native dynamic libraries into the output directory only works on Windows. This is probably because the authors of the package assumed that .NET Framework only runs on Windows, which is not true thanks to Mono. Also, if you wanto to have a look, the CopySQLiteInteropFiles target can be found in ~/.nuget/packages/system.data.sqlite.core/{version}/build/net4*/System.Data.SQLite.Core.targets.
But it's possible to automatically copy the SQLite.Interop.dll file into the output directory on Linux and macOS. Add the FixSQLiteInteropFilesOnLinuxAndOSX target (described below) in your csproj file and you'll be able to use System.Data.SQLite.Core on Linux and macOS running mono without the DllNotFoundException. Here's how your project should look like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net452</TargetFramework>
</PropertyGroup>
<Target Name="FixSQLiteInteropFilesOnLinuxAndOSX" BeforeTargets="CopySQLiteInteropFiles">
<ItemGroup>
<SQLiteInteropFiles Condition="$([MSBuild]::IsOsPlatform(Linux)) OR $([MSBuild]::IsOsPlatform(OSX))" Remove="#(SQLiteInteropFiles)" />
<SQLiteInteropFiles Condition="$([MSBuild]::IsOsPlatform(Linux))" Include="$(PkgSystem_Data_SQLite_Core)/runtimes/linux-x64/native/netstandard2.0/SQLite.Interop.*" />
<SQLiteInteropFiles Condition="$([MSBuild]::IsOsPlatform(OSX))" Include="$(PkgSystem_Data_SQLite_Core)/runtimes/osx-x64/native/netstandard2.0/SQLite.Interop.*" />
</ItemGroup>
</Target>
<ItemGroup>
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.111" GeneratePathProperty="true" />
</ItemGroup>
</Project>
Make sure to add GeneratePathProperty="true" in the package reference. This is required for the PkgSystem_Data_SQLite_Core property to be defined.

SGEN: An attempt was made to load an assembly with an incorrect format

I have a project that can build fine on my local machine, however, when I get TFS to build it, I receive the following error -
SGEN: An attempt was made to load an assembly with an incorrect format:
After reading through many other posts here on this topic, most people just say I need to change the build type to either x86 or Any CPU, rather than x64, but after trying countless combinations, this was not the solution. My program is also a windows service, so setting the App Pool to allow 32 bit applications (as suggested by others) is also not the solution.
I encountered this same issue today. A project would not build on my PC but built fine on other PC's
I eventually fixed it by doing the following:
Right-clicked the project with the error, went into Properties
Selected the Build tab and went to the last option which is "Generate serialization assembly"
I set this to Off and the project now builds fine.
My problem was finally solved by this page - http://aplocher.wordpress.com/2012/10/12/sgen-an-attempt-was-made-to-load-an-assembly-with-an-incorrect-format-tfs-2010/
Just in case that page ever disappears in the future, here are the steps involved -
In Team Explorer, right click on your Build Definition and choose Open Process File Location
Double click on the XAML file that is selected
In the designer, select the container called Sequence (this is the top-level container that goes around everything else).
In the Arguments list (typically at the bottom), change MSBuildPlatform from Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.Auto to Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.X86.
Save and close the file.
Check the file back in to TFS and try your build again.
The problem disappears after installing the latest Windows SDK which includes the 64Bit version of sgen.exe:
http://msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx
Sometimes (if that one does not help) the older version helps:
http://msdn.microsoft.com/en-us/windows/desktop/hh852363.aspx
For some reason the 64bit version of sgen is not included in the Microsoft Build Tools
I found this issue relevant:
https://github.com/dotnet/sdk/issues/1630
While waiting for this to be fixed in a future version, I was able to solve the problem by adding two targets to the csproj file, as suggested by https://github.com/joperezr:
<Target Name="RemoveDesignTimeFacadesBeforeSGen" BeforeTargets="GenerateSerializationAssemblies">
<ItemGroup>
<ReferencePath Remove="#(_DesignTimeFacadeAssemblies_Names->'%(OriginalIdentity)')" />
</ItemGroup>
<Message Importance="normal" Text="Removing DesignTimeFacades from ReferencePath before running SGen." />
</Target>
<Target Name="ReAddDesignTimeFacadesBeforeSGen" AfterTargets="GenerateSerializationAssemblies">
<ItemGroup>
<ReferencePath Include="#(_DesignTimeFacadeAssemblies_Names->'%(OriginalIdentity)')" />
</ItemGroup>
<Message Importance="normal" Text="Adding back DesignTimeFacades from ReferencePath now that SGen has run." />
</Target>
This question still pops up first in Google when I search certain keywords, so I'll post this in case anyone finds it relevant.
In my case, I had a project that built fine in "debug" but gave the OP's error in "release" mode. None of the solutions elsewhere in this thread solved the problem.
However, I ran into an obscure comment in another forum about web service references interfering with the build. A light bulb went off. My project had a number of legacy web service references that were no longer used. So I ripped them out. Lo and behold, I could now build the project in "release" mode, without disabling assembly serialization or fiddling with the CSPROJ or messing with SGEN references in Azure DevOps/VSTS.
Hopefully this saves someone time.
I encountered the same error when I tried to compile my project (Platform target is set to x86) in Release. It compiled fine in Debug. I came to find out that in Release, Generate serialization assembly is run; hence, the call to the SGen utility. The problem was that MSBuild called the x64 version of SGen against my x86 EXE, which generated the error. I had to pass this MSBuild argument so that MSBuild uses the correct version of SGen:
/p:SGenToolPath="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools"
My answer is an extension to that of ola-eldøy. In my case I had to exclude more assemblies, because each of them yielded the same dreadful error:
Could not load file or assembly bla-bla-bla or one of its dependencies. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (Exception from HRESULT: 0x80131058)
Therefore my solution was to extend ola-eldøy's code and save it in Directory.Build.targets:
<Project>
<ItemGroup>
<ReflectionOnlyAssemblyNames Include="Microsoft.Bcl.AsyncInterfaces"/>
<ReflectionOnlyAssemblyNames Include="System.Buffers"/>
<ReflectionOnlyAssemblyNames Include="System.Numerics.Vectors"/>
<ReflectionOnlyAssemblyNames Include="System.Runtime.CompilerServices.Unsafe"/>
</ItemGroup>
<Target Name="RemoveDesignTimeFacadesBeforeSGen" BeforeTargets="GenerateSerializationAssemblies">
<ItemGroup>
<_ReflectionOnlyAssembly_Names Include="#(_ReferencePath_Names)"
Condition="'#(ReflectionOnlyAssemblyNames)' == '#(_ReferencePath_Names)' And '%(Identity)' != ''"/>
</ItemGroup>
<ItemGroup>
<ReferencePath Remove="#(_DesignTimeFacadeAssemblies_Names->'%(OriginalIdentity)')" />
<ReferencePath Remove="#(_ReflectionOnlyAssembly_Names->'%(OriginalIdentity)')" />
</ItemGroup>
<Message Importance="normal" Text="Removing DesignTimeFacades from ReferencePath before running SGen." />
</Target>
<Target Name="ReAddDesignTimeFacadesBeforeSGen" AfterTargets="GenerateSerializationAssemblies">
<ItemGroup>
<ReferencePath Include="#(_DesignTimeFacadeAssemblies_Names->'%(OriginalIdentity)')" />
<ReferencePath Include="#(_ReflectionOnlyAssembly_Names->'%(OriginalIdentity)')" />
</ItemGroup>
<Message Importance="normal" Text="Adding back DesignTimeFacades from ReferencePath now that SGen has run." />
</Target>
</Project>
In my case, this error was due not to an invalid combination of x86 / x64 settings, but due to trying to build a project targeting a specific .NET framework version (v4.5.1) whose reference assemblies had not been installed on the build server.
The combination of the following two conditions was responsible for the error:
In Visual Studio, on the Project Properties page, on the Application tab, the "Target framework" was set to ".NET Framework 4.5.1";
On the build server, in folder C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework, a folder named v4.5.1 was not present. (Other folders with version numbers, including v3.5, v4.0, and v4.5, were present.)
The fix was to install Windows Software Development Kit (SDK) for Windows 8.1 on the build server. In the install wizard, in the "Select the features you want to install" step, I unchecked all boxes except for the one for ".NET framework 4.5.1 Software Development Kit".
Running that install caused the missing v4.5.1 folder in the Reference Assemblies\Microsoft\Framework.NETFramework folder to be created, and the build to run successfully.
Per one of the comments in the accepted answer by #james-white the following worked for me:
Chagnge: GenerateSerializationAssemblies property in the project file from 'On' to 'Auto'
Wanted to pull this suggestion into an answer to make it more obvious to anyone just skimming through. Thank you James White
I had this same issue and viewing the output screen gave me more details. From that I found the Target Framework was higher than was allowed for this type of project (I was building a SQL Server CLR project). The target framework in the project was set to 4.0. Changing this back to 3.5 fixed the issue for me.
Dave
I was having a similar problem, seeing the SGEN "incorrect format" error when building in VS or MSBuild from command line. My project is x64, but MSBuild insisted on using the 32-bit version of the tool. (Some of my peers work around this by building in VS 2015, but I have only VS 2017 installed and want to keep it that way.)
Looking at the diagnostic build output, it looks like SGEN is running from the directory named by its SdkToolsPath parameter (for me: C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\). This is assigned from TargetFrameworkSDKToolsDirectory. Looking at the targets files, this comes from SDK40ToolsPath. And that is set from MSBuild's .config file.
I resolved this by editing C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe.config (requires Admin privilege), setting the SDK40ToolsPath property using
<property name="SDK40ToolsPath" value="$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK\4.6.2\WinSDK-NetFx40Tools-x64', 'InstallationFolder', null, RegistryView.Registry32))" />
(Note: If you're looking for this path in the registry on a 64-bit OS, go to HKLM\SOFTWARE\WOW6432Node\Microsoft...)
The main change is, of course, x86 to x64 to use the 64-bit tools. I also changed the framework to be what we use (4.6.2). This may mean we can reliably only use tools for 64-bit projects and for this framework, with this change in place. Still, I hope this might help someone running into this issue. (I'm shocked and dismayed MSBuild doesn't automatically change the tools path based on Framework & Architecture.)
I upgraded a project from 4.0 to 4.5.2 and installed the Microsoft .NET Framework 4.5.2 Developer Pack on the build server. After that it worked. You have developer pack for all the other .net versions.
https://support.microsoft.com/en-us/help/2901951/the-microsoft--net-framework-4-5-2-developer-pack-for-windows-server-2
In my case, the solution compiled correctly in Debug, but there was a Release error in only one project.
Using this https://social.msdn.microsoft.com/Forums/en-US/13d3cc7a-88dc-476c-8a15-fa2d4c59e5aa/sgen-an-attempt-was-made-to-load-an-assembly-with-an-incorrect-format?forum=netfx64bit, I changed the project PlatformTarget who was with x86 problems for Any CPU.
I maintained the Solution with Mixed Platform and it was possible to compile in Release
In VS2022 it helped to set the following property of the project:
GenerateSerializationAssemblies: Auto
It failed when
GenerateSerializationAssemblies: On
This worked for me on Visual Studio 2017:
I changed one of my Project's Platform to x64
then I was getting this error while PUBLISH (Not Run)
If this is your case:
Go to Publish Settings
Change Configuration Strictly from Any CPU to Release-x64 (or whatever)
Then the error while publish disappears.

How can you access the Visual Studio solution level platform from a C# project's build event?

We have a large VS 2010 solution that is mostly C# code but there are a few native DLLs that various C# projects depend upon (including our unit testing DLL). We're in the processing of trying to support both 32-bit and 64-bit versions of our libraries. So we're now build the native DLLs as 32-bit and 64-bit. The problem is that a lot of our C# project's have post-build events that copy the required native DLLs into the project's TargetDir. Now that we have two different versions of the native DLLs (32 and 64 bit), I need to be able to specify the correct dir to copy the native DLL from. I originally thought I could simply use $(Platform) in the path like so:
copy $(SolutionDir)\NativeDll\$(Platform)\$(Configuration) $(TargetDir)
But that doesn't work because $(Platform) is the project's platform and not the solution level platform. In this case $(Platform) is "Any CPU". From what I can see looking at the post-build event macros in C# project, there doesn't appear to be a way to access the solution level platform that is being built. Is there a better way to accomplish my goal?
I believe the solution's platform, unlike that of the projects is simply text.
What I have down in the past is:
Delete Win32 and "mixed platform" from solution (and keep doing so after adding projects).
Set all C# DLLs to build as AnyCPU in solution platforms AnyCPU, x86, x64.
(Do not delete AnyCPU if you want to be able to open in Blend or if you have any pure managed applications in the solution.)
Set C# EXEs and unit tests to build in x86 in x86 solution platform and x64 in x64 solution platform and not to build at all in AnyCPU solution platform.
Set all natives to build in Win32 when solution is x86 and output to $(ProjectDir)\bin\x86\$(Configuration) and intermediate to same with obj in path instead of bin. x64 the same with x64 instead of x86 and Win32.
Set pre build events of C# EXEs and unit tests to copy native DLLs they are depended on from relative path with project's configuration name: $(Config)
Set unit tests' class initialize to copy entire contents of tests bin dir (correct platform and configuration, of course) to tests' out dir. Use if #DEBUG and unsafe sizeof(IntPtr) to tell where to look for tests' bin dir.
Manually (using Notepad) add relative reference path to .csproj files outside solution that use x86/x64 assemblies from solution's deployment location, so path will include $(Platform) and $(Configuration) and will not be per user.
Microsoft: Better 32/64 bit support in Visual Studio would be really in place.
When I had to do this, I simply made all of my assemblies buildable as x86 or x64 rather than AnyCPU, and had two separate output packages. There's really no point in AnyCPU if you KNOW your process must be 32-bit or 64-bit a prori.
I've not used it myself, but Build -> Batch Build is probably what you want. With it you can build multiple platforms.
http://msdn.microsoft.com/en-us/library/169az28z.aspx
Of course, this doesn't actually enable you to access the 'platform' for the solution, but you don't need to since you'll build each platform separately.
Update:
If you want to automate the build, create a batch file with the following contents
"c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv" ../solution.sln /rebuild "platform"
where 'platform' is "Release|Any CPU", "Release|x86" etc. and repeat the line for each configuration you need to build. Use the Configuration Manager to set up each project for a a build for x86 and x64 and you should have what you want.
I don't think the 'Active Solution Configuration' has an equivalent macro property.
What I suggest is to manually add a custom property in all .csproj files, like this (see the new MyVar custom property added for each configuration/platform combination):
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
...
<MyVar>MyDebugAnyCpu</MyVar>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
...
<MyVar>MyReleaseAnyCpu</MyVar>
</PropertyGroup>
You can use the 'Unload project' and 'Edit MyProject.csproj' menus to edit the .csprojet whil in Visual Studio. What's important to know is Visual Studio will not destroy these 'unknown' values even if you save it using the normal GUI editor.
Then in the post build event, you can use these values, for example:
copy $(SolutionDir)\$(MyVar)\$(Platform)\$(Configuration) $(TargetDir)
Francesco Pretto has an extension that helps with this. It seems to have some quirks and deficiencies, but it's a start.
http://visualstudiogallery.msdn.microsoft.com/619d92a2-4ead-410d-a105-135f7b4b4df9
With source on github:
https://github.com/ceztko/SolutionConfigurationName

Categories

Resources