I have a C# WPF project in Visual Studio 2019 that I'm trying to set the AssemblyVersion and AssemblyFileVersion of. The idea being that I am trying to set them to "0.Months since project started.Days since this month started.Minutes since midnight" to help with version control
I have a Text Template file with the code below
using System.Reflection;
[assembly: AssemblyVersion("<#= this.Release #>.<#= this.MonthsSinceProjectStarted #>.<#= this.DaysSinceMonthStarted #>.<#= this.MinutesSinceMidnight #>")]
[assembly: AssemblyFileVersion("<#= this.Release #>.<#= this.MonthsSinceProjectStarted #>.<#= this.DaysSinceMonthStarted #>.<#= this.MinutesSinceMidnight #>")]
<#+
int Release = 0;
static DateTime ProjectStartedDate = new DateTime(year: 2022, month: 5, day: 20);
int MonthsSinceProjectStarted = (int)((Int32.Parse(DateTime.Now.ToString("yyyy")) * 12) + Int32.Parse(DateTime.Now.ToString("MM"))) - ((ProjectStartedDate.Year * 12) ProjectStartedDate.Month);
int DaysSinceMonthStarted = (int)Int32.Parse(DateTime.Now.ToString("dd"));
int MinutesSinceMidnight = (int)DateTime.UtcNow.TimeOfDay.TotalMinutes;
#>
My .csproj file also looks like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<RootNamespace>LPG_Launcher</RootNamespace>
<UseWPF>true</UseWPF>
<Company>LowPoly Games</Company>
<Authors>LowPoly Games</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<PlatformTarget>x64</PlatformTarget>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<PlatformTarget>x64</PlatformTarget>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Optimize>true</Optimize>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="if $(ConfigurationName) == Release (
del /S *.pdb
)" />
</Target>
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
<ItemGroup>
<None Include="VersionAutoIncrementer.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>VersionAutoIncrementer.txt</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\TextTemplating\Microsoft.TextTemplating.targets" />
</Project>
When I clean and build my solution, it builds a .cs file with the below code in it
using System.Reflection;
[assembly: AssemblyVersion("0.0.20.673")]
[assembly: AssemblyFileVersion("0.0.20.673")]
But checking the properties of the "LPG Launcher.exe" thats built is showing the version numbers to be "0.0.0.0". Is this a suitable method of setting these values and if so, what am I doing incorrectly?
After checking from information given in the comments, I've determined that the .cs file being built by the Text Template wasn't being included in the build process. Thanks for the help everyone
Is there a simply way to load and call functions of a C# dll from F# both located in the same solution?
I've set up this very simple example:
My solution is called FSharpInterop and contains two project
One C# library called CSharpComm "CSharpComm\CSharpComm.csproj"
One F# project called FSharpInterop "FSharpInterop\FSharpInterop.fsproj"
The C# library provides this simple service:
using System;
namespace CSharpComm
{
public class CSharpService
{
public string Request()
{
return "Hello World provided by C#";
}
}
}
1st attempt: open command
After adding the C# project in the F# reference (right click on the References icon Add Reference... > Projects > Select CSharpComm), I tried to run this line in F#:
open CSharpComm
Error message: InteropHelloWorld.fsx(1,6): error FS0039: The namespace or module 'CSharpComm' is not defined.
2nd attempt: #r command
Tried to run:
#r "C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll"
or
#r "C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm"
This ends up with the error message: Invalid directive. Expected '#r "<file-or-assembly>"'.
3rd attempt: Nuclear option
I've tried using the interop service library:
open System.Runtime.InteropServices
module InteropWithNative =
[<DllImport(#"C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll", CallingConvention = CallingConvention.Cdecl)>]
extern void Request()
InteropWithNative.Request()
Error message: > System.EntryPointNotFoundException: Unable to find an entry point named 'Request' in DLL 'C:\src\FSharpInterop\CSharpComm\bin\Debug\CSharpComm.dll'. at FSI_0005.InteropWithNative.Request() at <StartupCode$FSI_0005>.$FSI_0005.main#()
Here I guess I need to instantiate a CSharpService object first but the Interop doesn't let me create a ctor in F#. Also, even if I manage to call the C# service that way, that looks a bit overkill for two technologies that supposes to cohabit easily.
Last attempt: Stack overflow
Following this question Import/open DLLs in F#, I tried to add an <ItemGroup> in my F# project but didn't find where and how I can do that (I thought Visual Studio would take care of that when adding the reference project).
Additional info on my solution
F# App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
F# packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="5.0.1" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>
[Edit] FSharpInterop.fsproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>425bd4f6-3577-4c20-9ceb-3f95609706d8</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>FSharpInterop</RootNamespace>
<AssemblyName>FSharpInterop</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<UseStandardResourceNames>true</UseStandardResourceNames>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(FSharpTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets') ">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
<Import Project="$(FSharpTargetsPath)" />
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Program.fs" />
<None Include="App.config" />
<Content Include="packages.config" />
<None Include="InteropHelloWorld.fsx" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.5.0.1\lib\netstandard2.0\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.ValueTuple">
<Private>True</Private>
</Reference>
<Reference Include="CSharpComm">
<HintPath>..\CSharpComm\bin\Debug\CSharpComm.dll</HintPath>
</Reference>
</ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
Disclaimer: This is my first ever attempt at F#
Add a reference to the C# project from the F# project
Unsure why this "doesn't work" (reference the project, not the dll as #user1981 stated)
C# Library Project in Solution:
using System;
namespace SampleLibInCSharp
{
public class CsharpService
{
// a static function to do trivial test....
public static string Request()
{
return "Hello World provided by C#";
}
}
}
Trivial F# Console App (from VS 2019 template), set as startup app in VS: Program.fs
open System
open SampleLibInCSharp
// Define a function to construct a message to print
let from whom =
sprintf "from %s" whom
[<EntryPoint>]
let main argv =
let message = from "F#" // Call the function
printfn "Hello world %s" message
let cs = CsharpService.Request()
printfn "ola %s" cs
0 // return an integer exit code
Run the F# console app:
Hello world from F#
ola Hello World provided by C#
Note: I did see some odd red squiglies denoting some syntax error with the message you posted, but it seems more like a VS artifact or bug. Rebuilding and/or running is fine (as above).
As stated, this is a trivial F#/C# test only...
Hth...
I have two .csproj projects, with the exact same source code:
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Assembly jsonAssembly = Assembly.LoadFrom(#"C:\path\to\Newtonsoft.Json.dll");
Type reflectionType = jsonAssembly.GetType("Newtonsoft.Json.Linq.JObject");
Console.Write("Types match: " + (reflectionType == typeof(JObject)));
Console.Write("Typenames match: " + (reflectionType.FullName == typeof(JObject).FullName));
JObject cast = (JObject)Activator.CreateInstance(reflectionType);
Console.Write("Success!");
}
}
Both .csproj files have an identical reference to Newtonsoft.Json:
<Reference Include="Newtonsoft.Json">
<HintPath>C:\path\to\Newtonsoft.Json.dll</HintPath>
</Reference>
The only difference between the two projects is that I created the first one using .NET Core 2.0, and the second one using .NET Framework 4.6.1.
Framework:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A6D2C332-B60D-41F1-9983-DE10065973A1}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ConsoleApp1</RootNamespace>
<AssemblyName>ConsoleApp1</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>C:\path\to\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Core:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>C:\path\to\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
When I run the above code using .NET Core, I get the following response:
Types match: True
Typenames match: True
Success!
When I run the above code using .NET Framework, I get the following response:
Types match: False
Typenames match: True
Unhandled Exception: System.InvalidCastException: [A]Newtonsoft.Json.Linq.JObject cannot be cast to [B]Newtonsoft.Json.Linq.JObject
Why do .NET Framework and .NET Core work differently when loading assemblies via reflection?
The problem maybe is, that in .NET Framework you use 2 different assemblies (2 different files on disk). Try to run the .NET Framework project in debug mode and see where these types are coming from or add these lines.
Console.Write("Reflection type module: " + (reflectionType.Module.FullyQualifiedName));
Console.Write("Static type module: " + (typeof(JObject).Module.FullyQualifiedName));
Inspired by Walter Branson's answer, I did some research and found how some of the Load methods behave.
/* Loads closest assembly given by FullName loaded by default, in this case it is from the same location as executable */
Assembly jsonAssembly1 = Assembly.Load(#"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed");
/* Loads assembly exaclty from the file specified by path */
Assembly jsonAssembly2 = Assembly.LoadFile($#"{pathToAssembly}\Newtonsoft.Json.dll");
/* In NET.Framework loads same assembly as LoadFrom, but in .NET Core loads same assembly as Load */
Assembly jsonAssembly3 = Assembly.LoadFrom($#"{pathToAssembly}\Newtonsoft.Json.dll");
I believe this is occurring because the reference compare fails for .NET. When you look at the objects there is a different handle in .NET. When you look at the objects in core they both have same handle.
I think this occurs because you are loading the assembly from file. Try loading the assembly by its fully qualified name.
Assembly jsonAssembly = Assebmly.Load("NewtonSoft.Json,Version=12.0.0.0,Culture=neutral,PublicKeyToken=30adfe6b2a6aeed")
.NET Framework one copies the .dll that you are referencing to its executable directory. So the loaded .dlls are actually different.
Try to change the path from "C:\path\to\Newtonsoft.Json.dll" in .NET Framework project to "Newtonsoft.Json.dll", the result will be the same as in .NET Core one.
I am trying to get the Unmanaged Export Basic sample working.
The steps I am following:
Create a new classlibrary project.
Add the UnmanagedExports with
nuget.
Change CPU target to x86.
Add the code from the tutorial I am following to the .cs file.
Build
The project is built sucesfully, but when I inspect my dll with DLL Export Viewer I can not see any of my functions.
I am using 32 bits OS and SharpDevelop 4.4 (I also have tried with other SharpDevelop versions and with 64bits OS with the same result).
My sln file:
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}.Debug|x86.ActiveCfg = Debug|x86
{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}.Debug|x86.Build.0 = Debug|x86
{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}.Release|x86.ActiveCfg = Release|x86
{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}.Release|x86.Build.0 = Release|x86
EndGlobalSection
EndGlobal
My csproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{AFAA816C-65B2-4B58-9FB2-EB7482AA0F5F}</ProjectGuid>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<RootNamespace>test</RootNamespace>
<AssemblyName>test</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>True</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="RGiesecke.DllExport.Metadata">
<HintPath>..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MyClass.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
My cs file:
using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace Testme
{
class Test
{
[DllExport("Add", CallingConvention = CallingConvention.StdCall)]
public static int Add(int left, int right)
{
return left + right;
}
........
Dll Export Viewer of my dll
Dll Export Viewer of a working dll (downloaded from the tutorial web)
Both dlls (mine and the downloaded) have the same size for my OS but not for the DLL Export Viewer.
What I am missing??
I had the same problem and I solved it this way,
You need add the follow line below this line csproj file
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- Remove this comment and insert This line-->
<Import Project="packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets" Condition="Exists('packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets')"/>
Check file exist in your folder "packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets"
There are a few minor places where code for my project may be able to be drastically improved if the target framework were a newer version. I'd like to be able to better leverage conditional compilation in C# to switch these as needed.
Something like:
#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif
Do any of these symbols come for free? Do I need to inject these symbols as part of the project configuration? It seems easy enough to do since I'll know which framework is being targeted from MSBuild.
/p:DefineConstants="NET40"
How are people handling this situation? Are you creating different configurations? Are you passing in the constants via the command line?
One of the best ways to accomplish this is to create different build configurations in your project:
<PropertyGroup Condition=" '$(Framework)' == 'NET20' ">
<DefineConstants>NET20</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Framework)' == 'NET35' ">
<DefineConstants>NET35</DefineConstants>
<OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>
And in one of your default configurations:
<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>
Which would set the default if it wasn't defined anywhere else. In the above case the OutputPath will give you a separate assembly each time you build each version.
Then create a AfterBuild target to compile your different versions:
<Target Name="AfterBuild">
<MSBuild Condition=" '$(Framework)' != 'NET20'"
Projects="$(MSBuildProjectFile)"
Properties="Framework=NET20"
RunEachTargetSeparately="true" />
</Target>
This example will recompile the entire project with the Framework variable set to NET20 after the first build (compiling both and assuming that the first build was the default NET35 from above). Each compile will have the conditional define values set correctly.
In this manner you can even exclude certain files in the project file if you want w/o having to #ifdef the files:
<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />
or even references
<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
<HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>
An alternative that is working for me so far is to add the following to the project file:
<PropertyGroup>
<DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
<DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
</PropertyGroup>
This takes the value of TargetFrameworkVersion property, which is like "v3.5", replaces the "v" and "." to get "NET35" (using the new Property Functions feature). It then removes any existing "NETxx" value and adds it to the end of the DefinedConstants. It may be possible to streamline this, but I haven't got the time to fiddle.
Looking on the Build tab of the project properties in VS you will see the resulting value in the conditional compilation symbols section. Changing the target framework version on the Application tab then changes the symbol automatically. You can then use #if NETxx preprocessor directives in the usual way. Changing the project in VS does not seem to lose the custom PropertyGroup.
Note that this doesn't give seem to give you anything different for the Client Profile target options, but that's not an issue for me.
I had problems with these solutions, possibly because my initial constants were pre-built by these properties.
<DefineConstants />
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>
Visual Studio 2010 also threw up an error because of the semi-colons, claiming they are illegal characters. The error message gave me a hint as I could see the pre-built constants seperated by commas, eventually followed by my "illegal" semi-colon. After some reformatting and massaging I was able to come up with a solution that works for me.
<PropertyGroup>
<!-- Adding a custom constant will auto-magically append a comma and space to the pre-built constants. -->
<!-- Move the comma delimiter to the end of each constant and remove the trailing comma when we're done. -->
<DefineConstants Condition=" !$(DefineConstants.Contains(', NET')) ">$(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
<DefineConstants Condition=" $(DefineConstants.Contains(', NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
<DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 2.0 ">$(DefineConstants)NET_20_OR_GREATER, </DefineConstants>
<DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 3.5 ">$(DefineConstants)NET_35_OR_GREATER</DefineConstants>
<DefineConstants Condition=" $(DefineConstants.EndsWith(', ')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", "))))</DefineConstants>
</PropertyGroup>
I would post a screenshot of the Advanced Compiler Settings dialog (opened by clicking the "Advanced Compile Options..." button on the Compile tab of your project). But as a new user, I lack the rep to do so. If you could see the screenshot, you would see the custom constants auto-filled by the property group and then you'd be saying, "I gotta get me some of that."
EDIT: Got that rep surprisingly fast.. Thanks guys! Here's that screenshot:
If you are using the .NET Core build system, you can use its predefined symbols (which actually match your example already and don't require any changes to your .csproj!):
#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif
The list of predefined symbols is documented in Developing Libraries with Cross Platform Tools and #if (C# Reference):
.NET Framework: NETFRAMEWORK, NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472, NET48
.NET Standard: NETSTANDARD, NETSTANDARD1_0, NETSTANDARD1_1, NETSTANDARD1_2, NETSTANDARD1_3, NETSTANDARD1_4, NETSTANDARD1_5, NETSTANDARD1_6, NETSTANDARD2_0, NETSTANDARD2_1
.NET Core: NETCOREAPP, NETCOREAPP1_0, NETCOREAPP1_1, NETCOREAPP2_0, NETCOREAPP2_1, NETCOREAPP2_2, NETCOREAPP3_0
Begin with clearing the constants:
<PropertyGroup>
<DefineConstants/>
</PropertyGroup>
Next, build up your debug, trace and other constants like:
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
</PropertyGroup>
Last, build your framework constants:
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">
<DefineConstants>NET10;NET20;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.0' ">
<DefineConstants>NET10;NET20;NET30;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.5' ">
<DefineConstants>NET10;NET20;NET30;NET35;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
<DefineConstants>NET10;NET20;NET30;NET35;NET40;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">
<DefineConstants>NET10;NET20;NET30;NET35;NET40;NET45;$(DefineConstants)</DefineConstants>
</PropertyGroup>
I think this approach is very readable and understandable.
In a .csproj file, after an existing <DefineConstants>DEBUG;TRACE</DefineConstants> line, add this:
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace("v",""))' >= '4.0' ">NET_40_OR_GREATER</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace("v",""))' == '4.0' ">NET_40_EXACTLY</DefineConstants>
Do this for both Debug and Release build configurations.
Then use in your code:
#if NET_40_OR_GREATER
// can use dynamic, default and named parameters
#endif
#Azarien, your answer can be combined with Jeremy's to keep it at one place rather than Debug|Release etc.
For me, combining both variations works best i.e. including conditions in code using #if NETXX and also building for different framework versions in one go.
I have these in my .csproj file:
<PropertyGroup>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace("v",""))' >= '4.0' ">NET_40_OR_GREATER</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion.Replace("v",""))' == '3.5' ">
<DefineConstants>NET35</DefineConstants>
<OutputPath>bin\$(Configuration)\$(TargetFrameworkVersion)</OutputPath>
</PropertyGroup>
and in targets:
<Target Name="AfterBuild">
<MSBuild Condition=" '$(TargetFrameworkVersion.Replace("v",""))' >= '4.0' "
Projects="$(MSBuildProjectFile)"
Properties="TargetFrameworkVersion=v3.5"
RunEachTargetSeparately="true" />
</Target>