We can add custom preprocessor directives for Platform Conditional Compilation in .NET Core like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsOSX)'=='true'">
<DefineConstants>OSX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsLinux)'=='true'">
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
</Project>
I've tested, it's working fine.
Now I want to detect whether or not I'm on a 64 bits operating system. Here is my .csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Is64BitOperatingSystem Condition="'$([System.Environment]::Is64BitOperatingSystem)' == 'true'">true</Is64BitOperatingSystem>
</PropertyGroup>
<PropertyGroup Condition="'$(Is64BitOperatingSystem)'=='true'">
<DefineConstants>Is64BitOperatingSystem</DefineConstants>
</PropertyGroup>
</Project>
However when I run this code, my first if...else
is working as expected but not my Is64BitOperatingSystem
preprocessor directive
if (System.Environment.Is64BitOperatingSystem)
Console.WriteLine(64);
else
Console.WriteLine(32);
#if Is64BitOperatingSystem
Console.WriteLine(64);
#else
Console.WriteLine(32);
#endif
What am I doing wrong? I can't spot where's the mistake in my code.
Thank you
To add more details about this, I included this code in a .NET Standard library that is called by a .NET Core project.
I want my library to detect the current architecture it's running on (or has been compiled for) so I can do something like this
#if Is64BitOperatingSystem
[DllImport(@"Resources/HIDAPI/x64/hidapi")]
#else
[DllImport(@"Resources/HIDAPI/x32/hidapi")]
#endif
Before debugging, Visual Studio obviously compiles my application so at this stage checking the architecture using System.Environment.Is64BitOperatingSystem
or a preprocessor directive should give the same results but it's not. I'm on a 64 bits machine and my preprocessor directive tells me I'm on a 32 bits architecture even if I change AnyCPU
to x64
in Visual Studio Configuration Manager
Note that this answer is Windows specific and that one too because the solution is to call the SetDllDirectory
function from kernel32.dll
But I want my code to be able to run on Linux.
For the sake of sharing a minimal sample here I actually removed the faulty part of my code.
It looks like this gives the expected result:
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Is64BitOperatingSystem Condition="'$([System.Environment]::Is64BitOperatingSystem)' == 'true'">true</Is64BitOperatingSystem>
</PropertyGroup>
<PropertyGroup Condition="'$(Is64BitOperatingSystem)'=='true'">
<DefineConstants>Is64BitOperatingSystem</DefineConstants>
</PropertyGroup>
But this gives a faulty behavior:
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Is64BitOperatingSystem Condition="'$([System.Environment]::Is64BitOperatingSystem)' == 'true'">true</Is64BitOperatingSystem>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
</PropertyGroup>
<PropertyGroup Condition="'$(Is64BitOperatingSystem)'=='true'">
<DefineConstants>Is64BitOperatingSystem</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
If anybody could explain me that?
I don't understand why adding the IsWindows
condition is responsible for a different behavior on the Is64BitOperatingSystem
preprocessor directive
I'd like to thank both JLRishe and Pavel Anikhouski for their answers. Although they were very useful, they were actually both not complete so I thought I should write myself another answer with all the necessary information.
I created a sample project on Github so you can play with it. Here are the explanations
Adding a Conditional Compilation Symbol
in the Visual Studio Configuration Manager actually adds a DefineConstants
node in the .csproj
behind the hood.
It doesn't need any new MsBuild variable declaration like I did in my question. It just uses an existing Platform
variable which already exists.
Moreover the way I used in my question seems incompatible with a dotnet publish
command
With a .csproj
like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm</RuntimeIdentifiers>
<Is64BitOperatingSystem Condition="'$([System.Environment]::Is64BitOperatingSystem)' == 'true'">true</Is64BitOperatingSystem>
<Platforms>x64;x86;arm64;arm86</Platforms>
</PropertyGroup>
<PropertyGroup Condition="'$(Is64BitOperatingSystem)'=='true'">
<DefineConstants>$(DefineConstants);Is64BitOperatingSystem</DefineConstants>
</PropertyGroup>
</Project>
Running the command
dotnet publish -r win-x64 -c Release
would return the following error
DetectArchitectureSample.csproj(7,29): error MSB4185: The function "Is64BitOperatingSystem" on type "System.Environment" is not available for execution as an MSBuild property function.
We actually don't need to add an extra Is64BitOperatingSystem
variable. All we need is to reuse the existing Platform
variable like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm</RuntimeIdentifiers>
<Platforms>x64;x86;arm64;arm86</Platforms>
</PropertyGroup>
<PropertyGroup Condition="$(Platform)=='x64' Or $(Platform)=='arm64'">
<DefineConstants>$(DefineConstants);Is64Bit</DefineConstants>
</PropertyGroup>
<Target BeforeTargets="Build" Name="test">
<Message Importance="High" Text="$(DefineConstants)"/>
</Target>
</Project>
Then specify the Platform
we want during the publish
dotnet publish -r linux-arm64 -c Release /p:Platform=arm64 /p:PublishSingleFile=true /p:PublishTrimmed=true
The output of this command would return a line like that
TRACE;Is64Bit;RELEASE;NETCOREAPP;NETCOREAPP3_1
Finally in our code we can load the correct unmanaged DLL depending of the platform the program has been compiled for
#if Is64Bit
[DllImport(@"Resources/HIDAPI/x64/hidapi")]
#else
[DllImport(@"Resources/HIDAPI/x32/hidapi")]
#endif