This is my .net code
namespace MyMathLib
{
public class Methods
{
public Methods()
{
}
public static int Sum(int a, int b)
{
return a + b;
}
public int Product(int a, int b)
{
return a * b;
}
}
}
and I'm trying to load my dll using powershell
$AssemblyPath = "D:\Visual Studio code\MyMathLib\bin\Debug\net6.0\MyMathLib.dll"
$bytes = [System.IO.File]::ReadAllBytes($AssemblyPath)
[System.Reflection.Assembly]::Load($bytes)
[MyMathLib.Methods]::Sum(10, 2)
It gives below error . dll location is ok.. can anyone please help
Unable to find type [MyMathLib.Methods].
At line:4 char:1
+ [MyMathLib.Methods]::Sum(10, 2)
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MyMathLib.Methods:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
how to resolve this?`
To write a .NET library that is compatible with both the Desktop/Windows .NET framework edition of Powershell as well as with the .NET Core edition make sure you target .NET standard 2.0 for the library/DLL (and not .NET 6).
So in your VS project use e.g.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
and not <TargetFramework>net6.0</TargetFramework>. For .NET 6.0 use/install Powershell Core (current release is Powershell 7).
Related
I am moving methods from my old .NET framework 4.5 class library to a new .NET Standard 2.0 class library. However, I am having problem with one method that uses System.Data.Entity.Design.PluralizationServices.
public static string ValueWithUnit(double value, string unit)
{
PluralizationService pluralizationService = PluralizationService.CreateService(new System.Globalization.CultureInfo("en-us"));
string valuePart = value.ToString("0.##");
string unitPart = value > 1 ? pluralizationService.Pluralize(unit) : unit;
return $"{valuePart} {unitPart}";
}
How do I add PluralizationServices in .NET Standard 2.0 class library?
I found the library Humanizer.Core that I can use for pluralization in .NET Standard. I just installed the package using Nuget package manager and updated my code like so:
string unitPart = value > 1 ? unit.Pluralize(inputIsKnownToBeSingular: false) : unit;
Background
Using visual studio 2019 v16.10.3 I have created this (standard MS example source) class library and successfully compiled into MyMathLib.DLL
using System;
namespace MyMathLib
{
public class Methods
{
public Methods()
{
}
public static int Sum(int a, int b)
{
return a + b;
}
public int Product(int a, int b)
{
return a * b;
}
}
}
My MyMathLib.csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
And then I want to try executing this code using powershell using the following script:
[string]$assemblyPath='S:\Sources\ResearchHans\DotNetFromPowerShell\MyMathLib\MyMathLib\bin\Debug\net5.0\MyMathLib.dll'
Add-Type -Path $assemblyPath
When I run this I get an error, and ask what's going on by querying the error for LoaderExceptions:
PS C:\WINDOWS\system32> S:\Sources\ResearchHans\DotNetFromPowerShell\TestDirectCSharpScript2.ps1
Add-Type : Kan een of meer van de gevraagde typen niet laden. Haal de LoaderExceptions-eigenschap op voor meer informatie.
At S:\Sources\ResearchHans\DotNetFromPowerShell\TestDirectCSharpScript2.ps1:2 char:1
+ Add-Type -Path $assemblyPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
+ FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand
PS C:\WINDOWS\system32> $Error[0].Exception.LoaderExceptions
Kan bestand of assembly System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a of een van de afhankelijkhed
en hiervan niet laden. Het systeem kan het opgegeven bestand niet vinden.
PS C:\WINDOWS\system32>
(Sorry for dutch windows messages: something like "Cannot load one or more of the types" and the loader exception Cannot load file or assembly "System.Runtime" etc...
I have seen many related issues on SO, but they are usually quite old and do not refer to .NET5.0
This works
When I directly compile the example source like below, it works just fine.
$code = #"
using System;
namespace MyNameSpace
{
public class Responder
{
public static void StaticRespond()
{
Console.WriteLine("Static Response");
}
public void Respond()
{
Console.WriteLine("Instance Respond");
}
}
}
"#
# Check the type has not been previously added within the session, otherwise an exception is raised
if (-not ([System.Management.Automation.PSTypeName]'MyNameSpace.Responder').Type)
{
Add-Type -TypeDefinition $code -Language CSharp;
}
[MyNameSpace.Responder]::StaticRespond();
$instance = New-Object MyNameSpace.Responder;
$instance.Respond();
Why?
The whole Idea to have a proof of concept that I can utilize my .NET5 libraries using e.g. powershell scripts. I actually have to load a much more complicated assembly, but this oversimplified example seems to be my primary issue at the moment.
The question
What am I missing? - How do I get this to work?
TIA for bearing with me.
You didn't state what version of Powershell that you're using. However, I can reproduce the error by the following.
In VS 2019, create a Class Library (C# Windows Library).
Rename the class from "Class1.cs" to "Methods.cs"
Methods.cs
using System;
namespace MyMathLib
{
public class Methods
{
public int Sum(int a, int b)
{
return a + b;
}
public int Product(int a, int b)
{
return a * b;
}
}
}
Compile.
Open Windows Powershell
Type: (Get-Host).version
Then type the following:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
which results in the following error:
Add-Type : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
At line:1 char:1
+ Add-Type -Path $assemblyPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
+ FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand
This occurs because .NETCore 5 isn't supported by Windows Powershell 5.1 - only on versions 6 and above. For Powershell 5.1 or below, it's necessary to use .NETFramework instead.
See Resolving PowerShell module assembly dependency conflicts which states the following:
PowerShell and .NET
... In general, PowerShell 5.1 and below run on .NET Framework, while PowerShell 6 and above run on .NET Core. These two
implementations of .NET load and handle assemblies differently.
See Installing PowerShell on Windows for how to get the latest version of Powershell.
In Powershell 7.1.3, one can call a method from MyMathLib.dll by doing the following:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
PS C:\Users\Test> $m1 = New-Object -TypeName MyMathLib.Methods
PS C:\Users\Test> $m1.Sum(2,3)
Note: Since the class/methods aren't static, it's necessary to create an instance by using $m1 = New-Object -TypeName MyMathLib.Methods
However, if it's static (as shown below):
MyMathLib.cs
using System;
namespace MyMathLib
{
public static class Methods
{
public static int Sum(int a, int b)
{
return a + b;
}
public static int Product(int a, int b)
{
return a * b;
}
}
}
Then one can do the following in Powershell:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
PS C:\Users\Test> [MyMathLib.Methods]::Sum(3,2)
Background
I'm trying to get Edit and Continue to work with a class library I'm compiling at runtime using Roslyn. This is for adding modding support to a game I'm developing.
Breakdown of problem
I have one class library project (A) with source files (.cs)
I have another console application project (B) in another solution that does the following:
Compiles all of project A's source files
Emits a dll and pdb
Loads the emitted dll and pdb via an assembly context
Calls a static method defined within project B
My desire is to be able to attach a debugger to a running process of project B in an instance of VS with project A loaded and be able to break, edit project A's code, and continue with my changes being executed
Currently, I am only able to break and continue
Any edits lead to the following notification:
This source file has changed. It no longer matches the version of the file used to build the application being debugged.
Source
Project A: DebuggableClassLibrary.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
Project A: Test.cs
using System;
namespace DebuggableClassLibrary
{
public class Test
{
public static int Ct = 0;
public static void SayHello()
{
Ct++;
Console.WriteLine("Hello World");
}
}
}
Project B: DynamicLoading.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
</ItemGroup>
</Project>
Project B: Program.cs
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
namespace DynamicLoading
{
class Program
{
static void Main(string[] args)
{
var references = new MetadataReference[]
{
MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
};
var files = Directory.GetFiles(#"C:\Users\mrbri\source\repos\DebuggableClassLibrary\DebuggableClassLibrary", "*.cs");
var assemblyName = "DebuggableClassLibrary.dll";
var debug = true;
var allowUnsafe = false;
var outputDirectory = #"C:\Users\mrbri\Documents\Test";
var preprocessorSymbols = debug ? new string[] { "DEBUG" } : new string[] { };
var parseOptions = new CSharpParseOptions(LanguageVersion.Latest, preprocessorSymbols: preprocessorSymbols);
var compilation = CSharpCompilation.Create(
assemblyName: assemblyName,
syntaxTrees: files.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, f, Encoding.UTF8)),
references: references,
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default,
optimizationLevel: debug ? OptimizationLevel.Debug : OptimizationLevel.Release,
allowUnsafe: allowUnsafe
));
var pePath = Path.Combine(outputDirectory, assemblyName);
var pdbPath = Path.Combine(outputDirectory, Path.ChangeExtension(assemblyName, ".pdb"));
using (var peStream = new FileStream(pePath, FileMode.Create))
using (var pdbStream = new FileStream(pdbPath, FileMode.Create))
{
var results = compilation.Emit(
peStream: peStream,
pdbStream: pdbStream,
options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)
);
}
var assemblyLoadContext = new SimpleUnloadableAssemblyLoadContext();
var assembly = assemblyLoadContext.LoadFromStream(File.OpenRead(pePath), File.OpenRead(pdbPath));
var type = assembly.GetTypes().First();
var method = type.GetMethod("SayHello");
while (true)
{
method.Invoke(null, null);
}
}
}
internal class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext
{
public SimpleUnloadableAssemblyLoadContext(): base(true) { }
protected override Assembly Load(AssemblyName assemblyName) => null;
}
}
Attempts at solutions and observations
Compiling project A manually through VS and loading the generated pdb and dll exactly as I do for the Roslyn compiled one does allow for Edit and Continue
Comparing project A's dlls generated via Roslyn and VS in JetBrains dotPeek did yield some interesting differences that stem from the compilation time generated .NETCoreApp,Version=v5.0.AssemblyAttributes.cs and DebuggableClassLibrary.AssemblyInfo.cs that I do not include when I compile in project B
Going through the trouble of compiling project A via a MSBuildWorkspace Project did not allow Edit and Continue, although did include .NETCoreApp,Version=v5.0.AssemblyAttributes.cs and DebuggableClassLibrary.AssemblyInfo.cs
Alternatives
I am open to Roslyn alternatives/wrappers that do have Edit and Continue support.
Edit and Continue does not support this scenario. The project for the library being edited needs to be loaded in VS (in the current solution) and the program needs to be launched with the debugger attached.
I have some exe files which has been created using either .net framework 4.5 or .net core 2.1 or .net core 3.1.
I want to get framework name and version information from this DLL using only c# application.
I have written below piece of code which is beneficial and works great with DLL files but not with exe.
var dllInformation = Assembly.LoadFrom(#"D:\\MyProgram.dll");
Console.WriteLine(dllInformation.FullName);
Console.WriteLine(dllInformation.ImageRuntimeVersion);
Console.WriteLine(((TargetFrameworkAttribute)dllInformation.GetCustomAttributes(typeof(TargetFrameworkAttribute)).First()).FrameworkName);
I have also gone through these links but I didn't found them useful for exe files:
information from exe file
Determine .NET Framework version for dll
Please let me know if any suggestions available.
The following program should display the version of the assembly. The program
loads two assemblies during runtime using Assembly.LoadFrom method. 1) is a .NET Fx assembly and 2) is a .NET Core assembly. It loads both and displays the framework version without issues. This project is in github. If you are using the github project, you need to have .NET Core 3.1 installled.
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
namespace net007
{
class Program
{
static void Main(string[] args)
{
//A .net framwwork dll in the same output fodler
//as the current executable
var fxAssembly = Assembly.LoadFrom("fx.console.app.exe");
//A .net core dll in the same output fodler
//as the current executable
var netCoreAssembly = Assembly.LoadFrom("core.console.app.dll");
ShowFrameworkVersion(fxAssembly); //.NETFramework,Version=v4.7.2
ShowFrameworkVersion(netCoreAssembly);//.NETCoreApp,Version = v3.1
}
static void ShowFrameworkVersion(Assembly assembly)
{
var attributes = assembly.CustomAttributes;
foreach (var attribute in attributes)
{
if (attribute.AttributeType == typeof(TargetFrameworkAttribute))
{
var arg = attribute.ConstructorArguments.FirstOrDefault();
if (arg == null)
throw new NullReferenceException("Unable to read framework version");
Console.WriteLine(arg.Value);
}
}
}
}
}
You can use PowerShell to detect for the target framework version:
$path = "C:\your dll here.dll"
[Reflection.Assembly]::ReflectionOnlyLoadFrom($path).CustomAttributes |
Where-Object {$_.AttributeType.Name -eq "TargetFrameworkAttribute" } |
Select-Object -ExpandProperty ConstructorArguments |
Select-Object -ExpandProperty value
This is my full class to get an "Name.exe" target framework.
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
public class TargetVersionChecker : MarshalByRefObject
{
public string GetTargetedFrameWork(string exePath)
{
Assembly fxAssembly;
try
{
fxAssembly = Assembly.ReflectionOnlyLoadFrom(exePath);
var targetFrameworkAttribute = fxAssembly.GetCustomAttributesData().FirstOrDefault(x => x.AttributeType == typeof(TargetFrameworkAttribute));
return targetFrameworkAttribute?.ConstructorArguments.FirstOrDefault().Value.ToString();
}
catch (Exception ex)
{
// I log here the error but is to specific to our system so I removed it to be more simple code.
return string.Empty;
}
}
}
what does this mean?
System.MissingMethodException was unhandled by user code
HResult=-2146233069 Message=Method not found:
'System.Data.Entity.ModelConfiguration.Configuration.PrimitivePropertyConfiguration
System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration1.Property(System.Linq.Expressions.Expression12<!0,!!0>>)'.
Source=Att.Uds.DataLayerMappings StackTrace:
at Att.Uds.DataLayerMappings.ItemTypeItemConfiguration..ctor()
at Att.Uds.DataLayerMappings.UdsContext.OnModelCreating(DbModelBuilder
modelBuilder) in
c:\TFS\ATS-MSDev\UDS\Dev\Code\Att.Uds.DataLayerMappings\UdsContext.cs:line
163
at System.Data.Entity.DbContext.CallOnModelCreating(DbModelBuilder
modelBuilder)
at System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder()
at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext
internalContext)
at System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input) InnerException:
Error happens on this class:
namespace Contoso.Fabrikam.DataLayerMappings
{
public abstract class NamedEntityConfiguration<TEntity> : EntityBaseConfiguration<TEntity> where TEntity : NamedEntity
{
public ConfigurationColumn NameColumn;
protected new int LastOrdinalPosition
{
get
{
return (NameColumn.Ordinal);
}
}
public NamedEntityConfiguration() <=== EXCEPTION HERE
{
NameColumn = new ConfigurationColumn() { Ordinal = base.LastOrdinalPosition+1, Name = "Name", IsRequired = true, Length = 128 };
this.Property(t => t.Name)
.HasColumnName(NameColumn.Name)
.HasColumnOrder(NameColumn.Ordinal)
.HasMaxLength(NameColumn.Length);
if(NameColumn.IsRequired)
{
this.Property(t => t.Name).IsRequired();
}
}
}
}
Thank you
The reason why #Saber his answer works is because when you upgrade your project to a higher .NET Version, the project file is not upgraded automatically. For example, if you upgrade from .NET 4.0 to .NET 4.5 and edit the project file, you might see the following:
<Reference Include="EntityFramework">
<HintPath>..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="EntityFramework.SqlServer">
<HintPath>..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.SqlServer.dll</HintPath>
</Reference>
You will have to change the reference to net45 instead of net40. When removing the packages, this will produce the same behaviour.
I've encountered same error.
It worked for me:
Uninstall-Package EntityFramework in Package Manager Console
Delete EntityFramework folder from 'packages' folder
Install-Package EntityFramework in Package Manager Console
In my case I had to remove the EntityFramework.dll from this folder:
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\EntityFramework