How to undefine symbol for a C# project - c#

I have a C# project defining a symbol named WIN:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>$(DefineConstants);WIN</DefineConstants>
</PropertyGroup>
This constant is used in an #if preprocessor directive to conditionally perform some actions, namely, to import some libraries:
#if WIN
const string MyExternalLibrary = #"./lib/Win/MyExternalLibrary.dll";
#else
const string MyExternalLibrary = #"./lib/Linux/MyExternalLibrary.so";
#endif
[...]
[DllImport(MyExternalLibrary )]
public static extern IntPtr MyFunction();
I'd like the WIN constant to be defined, by default, so that the project can be built on windows machines.
At publish time, however, I'd like it to be undefined, to fall in the #else block.
Is there a way to tell dotnet build to undefine a symbol (that is, the opposite of dotnet build's -p switch)?

Related

C# references to C++ CLR x32 and x64

I created a C++ project in Visual Studio Class library CLR (.NET Framework):
#pragma once
using namespace System;
#ifdef _M_X64
namespace MyDLL64 {
#else
namespace MyDLL32 {
#endif
public ref class MyClass
{
public: static String^ Foo(String^ arg)
{
String^ str = arg->ToUpper();
return str;
}
};
}
Then I compiled two libraries(x86 and x64).
Thereafter I add them to references in my C# project
and added their methods to the code:
String res = "";
if (IntPtr.Size == 8)
{
res = MyDLL64.MyClass.Foo("test string");
}
else
{
res = MyDLL32.MyClass.Foo("test string");
}
Console.WriteLine(res);
But I am getting this error:
System.BadImageFormatException: "Could not load file or assembly 'MyDLL64, Version=1.0.8197.24341, Culture=neutral, PublicKeyToken=null', or one of their dependencies. An attempt was made to load a program with an invalid format."(or MyDLL32... if arch is 64 bit).
By the way, this code starts correctly without exceptions:
String res = "";
if (IntPtr.Size == 8)
{
res = MyDLL64.MyClass.Foo("test string");
}
if (1 > 2)
{
res = MyDLL32.MyClass.Foo("test string");
}
Console.WriteLine(res);
So how properly add x32 and x64 C++CLR dlls to my Any CPU C# DLL?
So how properly add x32 and x64 C++CLR dlls to my Any CPU C# DLL?
You can't. AnyCPU just means that the self process can be executed either as a 32 or 64 bit one depending on the architecture but once a 64 bit process is spawned it cannot access 32 bit images and vice versa.
If your C# project references native images, then instead of building one AnyCPU image you must create two separate images just like in case of the C++ project.
Specify x86 and x84 targets for the solution. And then if your 32/64-bit C++ dlls are named differently, then you can edit your .csproj file like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='x86'">
<PlatformTarget>x86</PlatformTarget>
<DefineConstants>$(DefineConstants);X86</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(Platform)'=='x86'">
<Reference Include="MyDLL32"/>
</ItemGroup>
<PropertyGroup Condition="'$(Platform)'=='x64'">
<PlatformTarget>x64</PlatformTarget>
<DefineConstants>$(DefineConstants);X64</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(Platform)'=='x64'">
<Reference Include="MyDLL64"/>
</ItemGroup>
</Project>
And then the C# code can have similar #if directives than the C++ one:
String res = "";
#if X64
res = MyDLL64.MyClass.Foo("test string");
#else
res = MyDLL32.MyClass.Foo("test string");
#endif
Console.WriteLine(res);
But IMHO it would be nicer if the C++ images used the same namespaces so the C# code does not need the #if directives and the different images could be handled purely in the .csproj file (in which case you don't need the DefineConstants either).

Get compiletime constants in C#

In C# you can define compile-time constants that may be checked to configure compilation
#define MY_CONST
#if MY_CONST
...
#else
...
#endif
But I can't find a way to see which constants are defined at current line.
I need something like
#warning DEFINED_CONSTANTS that will give me DEBUG; NET5_0
Disregarding everything else, you could just set a field
#if MY_CONST
public static bool IsMyConst = true;
#else
public static bool IsMyConst = false;
#endif
Add pepper and salt to taste.
After some time I've found that it is possible to show defined constants through MSBuild with
<Target Name="ShowConstants" AfterTargets="AfterBuild">
<Warning Text="$(DefineConstants)" />
</Target>
which gives TRACE;DEBUG;NET;NET5_0;NETCOREAPP for net5.0 build.
It doesn't show constants for given line, but at least show constants for current configuration.

C# Preprocesors directive with (c# Version)

Is there any way with a preprocessor to execute code according to the version of C#?
Example:
#if CSharpVersion = 7.3
var value = 1;
#endif
One option would be to define the LangVersion explicitly in the Project file and have the constants defined based on it. For example,
<LangVersion>7.3</LangVersion>
and
<DefineConstants Condition="'$(LangVersion)' == '7.3'">DEBUG;TRACE;LANG_VERSION_7_3</DefineConstants>
<DefineConstants Condition="'$(LangVersion)' != '7.3'">DEBUG;TRACE;LANG_VERSION_NOT_7_3</DefineConstants>
Now you could use directives as
#if LANG_VERSION_7_3
Console.WriteLine("C# 7_3");
#elif LANG_VERSION_NOT_7_3
Console.WriteLine("Not C# 7_3");
#endif
Please note the LANG_VERSION would signify the compiler accepts syntax specified version or lower.

Interfacing both Debug and Release DLLs with the same C# code in SWIG?

SWIG lets you specify the DLL to import in C# by using the -dllimport command line argument.
What about importing a DLL whose name depends on whether it is a Debug version or a Release one? This happens with DLLs that follow the Microsoft convention of appending the d suffix to the Debug version, e.g. ucrtbase.dll for the Release version, and ucrtbased.dll for Debug.
If -dllimport allowed to specify a symbolic constant, then the value of such constant could depend on whether DEBUG is defined or not, but that does not seem to be the case.
I think you can do what you want using a static constructor and the SetDllDirectory of suggestion of this this answer.
To test this out I put together this example:
%module foobar
%pragma(csharp) imclasscode=%{
[global::System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
static $imclassname() {
if (isDebug)
SetDLLDirectory("path/to/directory/with/debugDll");
else
SetDLLDirectory("path/to/directory/with/regularDll");
}
%}
You have to build it with SWIG like this in order to suppress the default static constructor:
swig3.0 -DSWIG_CSHARP_NO_IMCLASS_STATIC_CONSTRUCTOR -csharp test.i
Quite how you'll distinguish isDebug in real code I'm less clear on - maybe use this to find the current HMODULE and then GetModuleFilename to see if it's your debug build or not.

Whats the point of #define? [duplicate]

This question already has answers here:
How do you use #define?
(8 answers)
Closed 8 years ago.
They have to be placed on the top of your .cs file. You cant create those guys dynamically at runtime and you cant give them a value or change their value because there is no value at all so whats the point of #define keyword?
Here is an example:
#define DEBUG
#define MYTEST
using System;
public class MyClass
{
static void Main()
{
#if (DEBUG && !MYTEST)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
Console.WriteLine("DEBUG and MYTEST are defined");
#else
Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
}
}
Both are defined on top so why having all those ifs anyway?
Can somebody tell me scenarios where define is usefull?
Sorry if this is a duplicate just let me know in comments if so and I ll remove this question.
The point is condiional compialtion.
Like:
#ifdef x64
....
#else
....
#endif
#ifdef KIOSK
fullScreen =true;
#else
fullScreen =false;
#endif
You create condition for compile time, so your binaries will not change at runtime, but
will fit exact requirements of your target ambient you are compiling for.It could be whatever you want, it's up to you decide name and semantics of what you #define.
EDIT.
Example: you have a program that acess low level windows API, and you have to support
x86 and x64 versions. In this case you may want that in binaries (so after compilation) of your program for 32bit, there is no any evidence of 64bit functions. So you may write
#ifdef x64 //conditional compilation symbol defined by YOU
DriverInfo GetDriverInfo64(..) {...} //64bit
#else
DriverInfo GetDriverInfo(..) {...} //32bit
#endif
You don't need to define these symbols with #define, you can set them globally in the project settings or per compiler argument. But sometimes it comes in handy when you can alter the value of a symbol (set/unset) for a single file, for testing or debugging purposes. Then #define will be your friend :)
You should remove the two #define statement at the top. If you use Visual Studio to compile, you can then have the option to turn the debug on or not. If you turn on debug, the compiler will know what section to have in your code and which to leave out.
Suppose you have DEBUG turned on, your code will look like:
using System;
public class MyClass
{
static void Main()
{
Console.WriteLine("DEBUG is defined");
}
}

Categories