C# references to C++ CLR x32 and x64 - c#

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).

Related

How to undefine symbol for a C# project

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)?

error when call the unmanaged resouce functions from c#.net core dll into vb.netcore windows application

I got Issues when I call unmanaged resource functions from C#.net core DLL into VB.net core windows application. The program execution just stops suddenly when the function is hit.
Please below is my code
DLL Unmanaged function
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace TestLibrary1
{
public class Class1
{
[UnmanagedCallersOnlyAttribute]
public static IntPtr AddNE(IntPtr a)
{
try
{
IntPtr add = a;
return add;
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
}
return (IntPtr)0;
}
}
}
VB.netcore windows code below to call dll
Imports System.Runtime.InteropServices
Imports Google.Apis.Calendar.v3.Data
Public Class Form1
Private Const DllName As String = "TestLibrary1NE.dll"
<DllImport(DllName)>
Private Shared Function AddNE(ByVal a As IntPtr) As IntPtr
End Function
Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
Try
Dim ptrAddress As IntPtr = Marshal.StringToHGlobalAnsi(txtAddress.Text.Trim())
Dim ptrResponse As IntPtr = AddNE(ptrAddress)
Dim strResponse As String = Marshal.PtrToStringAnsi(ptrResponse)
MessageBox.Show(strResponse)
Catch ex As EntryPointNotFoundException
Console.WriteLine(e.ToString())
End Try
End
End Sub
End Class
When the function AddNE (from the C# DLL) above is hit it suddenly stops the execution from nowhere.
May I know what is the solution to fix this and how to find out the error?
It's not mentioned in the question but the code uses the DNNE package to create a native library that exports all marked functions with the attribute UnmanagedCallersOnly.
The DNNE package needs that 3 files be distributed together:
The managed assembly (TestLibrary1.dll);
The native assembly (TestLibrary1NE.dll);
And the TestLibrary1.runtimeconfig.json.
The catch is that both managed assembly (TestLibrary1.dll) and native assembly (TestLibrary1NE.dll) should be distributed together. And not only that but also the TestLibrary1.runtimeconfig.json. This is because the native assembly calls the managed assembly internally so all the files should be on the same path.
DNNE needs on Windows the --runtime flag or MSBuild RuntimeIdentifier property to be set as target win-x86 or win-x64, and both projects should be the same. Check the 3rd bullet here in the docs.
Also, the project that uses TestLibrary1.dll should reference the project. Something like the following:
<ItemGroup>
<ProjectReference Include="..\TestLibrary1\TestLibrary1.csproj" />
</ItemGroup>
The changes to the C# project configuration file (TestLibrary1.csproj) are commented on below. EnableDynamicLoading should be true to generate the TestLibrary1.runtimeconfig.json. And DNNE needs both PlatformTarget and RuntimeIdentifier to be defined as x64 and win-x64 respectively.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>c_sharp</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Include the line below to generate the runtimeconfig.json -->
<EnableDynamicLoading>true</EnableDynamicLoading>
<!-- DNNE package asks to include those 2 also -->
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DNNE" Version="1.0.32" />
</ItemGroup>
</Project>

Py.GIL() is giving error pythonnet embedded python in C#

I have the following C# code:
static void Main()
{
string pythonpath1 = #"C:\Users\user\Documents\pynet_test\Python\Python37";
string pythonpath2 = #"C:\Users\user\Documents\pynet_test\Python\Python37\lib";
string envpythonhome = #"C:\Users\user\Documents\pynet_test\Python\Python37\python37.dll";
Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", envpythonhome, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PATH", pythonpath1, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pythonpath1, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONPATH", pythonpath2, EnvironmentVariableTarget.Process);
using (Py.GIL())
{
dynamic np = Py.Import("numpy");
Console.WriteLine(np.cos(np.pi * 2));
dynamic sin = np.sin;
Console.WriteLine(sin(5));
double c = np.cos(5) + sin(5);
Console.WriteLine(c);
dynamic a = np.array(new List<float> { 1, 2, 3 });
Console.WriteLine(a.dtype);
dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
Console.WriteLine(b.dtype);
Console.WriteLine(a * b);
}
Console.WriteLine(DateTime.Now);
}
The error I am getting is:
System.MissingMethodException
HResult=0x80131513
Message=Method not found: 'System.Reflection.Emit.AssemblyBuilder System.AppDomain.DefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess)'.
Source=Python.Runtime
StackTrace:
at Python.Runtime.CodeGenerator..ctor()
at Python.Runtime.DelegateManager..ctor()
at Python.Runtime.PythonEngine.Initialize(IEnumerable`1 args, Boolean setSysArgv, Boolean initSigs)
at Python.Runtime.PythonEngine.Initialize(Boolean setSysArgv, Boolean initSigs)
at Python.Runtime.PythonEngine.Initialize()
at Python.Runtime.Py.GIL()
at WrapperPython.Program.Main() in C:\Users\user\Documents\pynet_test\pynet_test\Program.cs:line 50
The Python environment is in the Project folder and this is the following specifications:
python version used : 3.7 (x64)
pythonnet version: 2.5.2-cp37-cp37m-win_amd64
OS : Windows Server 2019
Reference has been made to the python.Runtime.dll under site-packaged. CSproj looks like following:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="Python.Runtime">
<HintPath>..\Python\Python37\Lib\site-packages\Python.Runtime.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
I have tried everything provided online but can't seem to find the issue. I have a hunch it's based on the environment variables but not sure how to proceed.
okay so python.net installation is really not well documented and the folks maintaining the python.net repository don't really help a lot since it's not a "support forum".
I solved this issue by installing the python.runtime.AllPlatflorms nuget package and pointing the environment variables to the right python folders/ files.
This works with python3.8 as well.

Is there an easy way to add multiple projects to a solution?

In some solutions I use direct references to internal projects during development and switch to nuget references for release. However, adding 30+ projects to new solutions manually is a tedious task.
I was wondering whether there is a quicker way to do this. With projects it's pretty easy because you just copy/paste the xml but solution files are not that easy to edit but maybe there is some trick?
You can use the dotnet CLI.
For Linux:
dotnet sln MySolution.sln add **/*.csproj
For Windows PowerShell:
dotnet sln MySolution.sln add (Get-ChildItem -Recurse *.csproj)
I've done some reaserch on the visualstudio extensibility based on #JamesFaix link and I managed to put together this small piece of code:
using System;
using System.IO;
using System.Linq;
using EnvDTE;
using EnvDTE80;
class Program
{
static void Main(string[] args)
{
// VS2019
var dteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", true);
var dte = (EnvDTE.DTE)System.Activator.CreateInstance(dteType);
var sln = (SolutionClass)dte.Solution;
// Solution to add projects to
sln.Open(#"C:\Projects\MyProject\MySolution.sln");
// Projects should be added to the "lib" solution-folder.
var lib = FindSolutionFolderOrCreate(sln, "lib");
// These projects should be added.
var projectPaths = new[]
{
#"C:\Projects\MyLibs\Lib1.csproj",
#"C:\Projects\MyLibs\Lib2.csproj"
};
foreach (var path in projectPaths)
{
var name = Path.GetFileNameWithoutExtension(path);
// If project not already in the solution-folder then add it.
if(!(lib.Parent.ProjectItems.Cast<ProjectItem>().Any(pi => pi.Name == name)))
{
lib.AddFromFile(path);
}
}
dte.Solution.Close(true);
}
private static SolutionFolder FindSolutionFolderOrCreate(SolutionClass sln, string folderName)
{
foreach (var x in sln.Projects)
{
if (x is Project p && p.Name.Equals(folderName, StringComparison.OrdinalIgnoreCase))
{
return (SolutionFolder)p.Object;
}
}
var proj = (sln as Solution2).AddSolutionFolder(folderName);
return (SolutionFolder)proj.Object;
}
}
*.csproj file of this utility:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!--<TargetFramework>netcoreapp2.2</TargetFramework>-->
<TargetFramework>net47</TargetFramework>
<EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="EnvDTE">
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PublicAssemblies\envdte.dll</HintPath>
</Reference>
<Reference Include="EnvDTE80">
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PublicAssemblies\envdte80.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
I'm using net47 here because netcoreapp doesn't allow you to debug COM objects which makes it really a painful experience.
You'll also need references to the two EnvDTE files.

Get Assembly From C# Project File

I am trying to make a simple WinForm tool to assist with code generation, and I was wondering if it was possible to get the Assembly of one project into a different one that presides in a different solution. I want the form to show all of the classes and then properties for each class, and the easiest/best way I can think of doing that is like:
private Type[] GetTypesInNamespace(Assembly assembly, string nameSpace)
{
return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
}
If the user selects a .csproj file, is it possible to get the Assembly? Or is there a different way to get the classes/properties without recursively searching the project folder and parsing the files?
The csproj file will contain the assembly name and the output directory.
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AssemblyName>MyAppAssemblyNameOnly</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
</PropertyGroup>
</Project>
You'll have to add .dll suffix to it to get the actual file name.
The Output Path can be found in the different Configuration <PropertyGroup> nodes.
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
There's a couple of problems I can think of right off the bat.
The DLL may not be built and so it won't exist.
There are many different configurations with there being Debug and Release by default. You'll have to decide which one to look for.
For ease, you may just want to make the user feed you a DLL if the project is not part of the solution and you don't actually need anything else.
You can also look into Roslyn and parse the files with Roslyn to get you all of the information you need too.
Here's an example straight from their page. Seems super simple and straightforward. Kind of larger than I want, but don't want to just give a single link-only suggestion.
class Program
{
static void Main(string[] args)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(
#"using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}");
var root = (CompilationUnitSyntax)tree.GetRoot();
var firstMember = root.Members[0];
var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember;
var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0];
var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0];
var argsParameter = mainDeclaration.ParameterList.Parameters[0];
}
}

Categories