Preprocessor directive in C# for importing based on platform
Here's what you need to do.
First, go into Project-><project name> Properties...
and go to the Build tab.
In there, in the textbox labelled "Conditional compilation symbols", add WIN32
for your x86 platform (selectable at the top of the dialog) and WIN64
for your x64 platform. Then save.
Note that if you have one for "AnyCPU", you probably want to remove that platform altogether, as it won't be safe.
Then, go into the source, and write this:
#if WIN64
[DllImport("ZLIB64.dll", CallingConvention=CallingConvention.Cdecl)]
#else
[DllImport("ZLIB32.dll", CallingConvention=CallingConvention.Cdecl)]
#endif
Note that when you view the source, one of the lines will look like it has been commented out, in that the entire line is in a gray font. This line is the one for the "other platform". If you select the platform in the toolbar, you'll notice that the syntax coloring follows suit.
Of course, after re-reading my answer I notice that you don't actually need to put WIN32 into the conditional symbols list as it isn't used, but it might be useful other places to do an #if on WIN32 instead of 64.
How to use #if to decide which platform is being compiled for in C#
You can add any constants you want to the .csproj
file. These can be put into conditional property groups like the one below.
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DefineConstants>TRACE;X64</DefineConstants>
...
</PropertyGroup>
For my Release x64 build, I have defined a X64 constant that I can use like this:
#if X64
#endif
Custom 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
How to compile code (choose part of the code) according to the target platform?
There is no WINANY constant to determine OS platform your code runs on. Since .NET 3.1, the available common constants are WINDOWS, ANDROID, LINUX, IOS.
Your code above can be simplified to this:
/// <summary>
/// Will log a Windows Event. Event are visible in the Application section with EventViewer.
/// </summary>
public static void LogWindowsEvent(LogType logType, string message, Exception ex = null)
{
#if WINDOWS
string appName = AppInfo.AppName;
if (!EventLog.SourceExists(appName))
{
EventLog.CreateEventSource(appName, "Application");
if (logType == LogType.LogException)
{
EventLog.WriteEntry(appName, $"Exception happen: {ex} at StackTrace: {ex.StackTrace}.", EventLogEntryType.Error);
}
else
{
EventLogEntryType eventLogEntryType = EventLogEntryType.Error;
switch (logType)
{
case LogType.LogWarning:
eventLogEntryType = EventLogEntryType.Warning;
break;
case LogType.LogMessage:
eventLogEntryType = EventLogEntryType.Information;
break;
}
EventLog.WriteEntry(appName, message, eventLogEntryType);
}
}
#else
// Empty code for non Windows
#endif
}
For more information, see also this official document on .NET Core design document:
https://github.com/dotnet/designs/blob/main/accepted/2020/net5/net5.md#scenarios-and-user-experience
Preprocessor directives across different files in C#
In your .csproj there is a section:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;LINQ</DefineConstants>
</PropertyGroup>
</Project>
If you want extra preprocessors you can add them there.
Or via the properties of the project which will add them there automatically for you. In properties under the Build tab.
Running NUnit test depending on a condition
I don't know if there's a built-in mechanism in NUnit to handle this scenario, but at the very least you can use preprocessor directives.
For instance, create a "Debug x86" solution configuration, targeting x86. Then define the DEBUG_X86 conditional compilation symbol (in the properties of the project). Finally, surround your unit test with preprocessor directives:
#if DEBUG_X86
[Test]
public void Test()
{
// This test will only run when compiled with Debug x86
}
#endif
Edit: Actually, you don't even have to create a new solution configuration, as it's possible to define the conditional symbols depending on the platform (https://stackoverflow.com/a/1313450/869621). So define a WIN32 compilation symbol, and surround your test with it:
#if WIN32
[Test]
public void Test()
{
// This test will only run when compiled for x86
}
#endif
Related Topics
How to Get a Random Number from a Range, Excluding Some Values
How to Hide an Inherited Property in a Class Without Modifying the Inherited Class (Base Class)
Autoresize Textbox Control Vertically
Is There Any Difference Between Datetime in C# and Datetime in SQL Server
Case Statement Block Level Declaration Space in C#
Wrap Properties with Cdata Section - Xml Serialization C#
Is There a Messagebox Equivalent in Wpf
Rendering Partial Views Using Ajax
Parsing a JSON Array Using JSON.Net
How to Get the Index of the Highest Value in an Array Using Linq
Xdocument: Saving Xml to File Without Bom
C# Object Initialization of Read Only Collection Properties
C# Creating an Unknown Generic Type at Runtime
Thread Parameters Being Changed
Wix Silent Install Unable to Launch Built in .Exe: Wix V3