I am developing PowerShell binary module. It uses Json.NET and other libraries.
I am getting this exception "Could not load file or assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.'
On hard drive I have an updated version of it (version 7.0.2)
Problems like that are easily solved in console, web or desktop application, with app.config or "web.config" via lines like this
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
How can I do something similar for PowerShell binary module?
After coming across this issue myself while developing a PowerShell module that uses multiple 3rd party libraries (Google API, Dropbox, Graph, etc) I found the following solution was the simplest:
public static Assembly CurrentDomain_BindingRedirect(object sender, ResolveEventArgs args)
{
var name = new AssemblyName(args.Name);
switch (name.Name)
{
case "Microsoft.Graph.Core":
return typeof(Microsoft.Graph.IBaseClient).Assembly;
case "Newtonsoft.Json":
return typeof(Newtonsoft.Json.JsonSerializer).Assembly;
case "System.Net.Http.Primitives":
return Assembly.LoadFrom("System.Net.Http.Primitives.dll");
default:
return null;
}
}
Note in the method, I've got two possible ways to reference the assembly, but both of them do the same thing, they force the current version of that assembly to be used. (Regardless if it is loaded via class reference or dll file load)
To use this in any cmd-let add the following event handler in the BeginProcessing() method of the PSCmdLet.
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_BindingRedirect;
The closest i've found so far is:
Add the problem assembly to the manifest's RequiredAssemblies - this causes it to be loaded into the AppDomain when the module is loaded.
Use the code from this SO answer - it adds an AssemblyResolve handler to the current AppDomain, which searches for assemblies already loaded and returns ones which match by strong name and PublicKeyToken
After using the module, you have to do the following to avoid stack overflows when exiting: [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($OnAssemblyResolve)
Steps 1 and 2 could both be encapsulated in the module, but step 3 can't, which means this isn't suitable as a general solution - the caller has to know about it. So I'm still searching for a better way.
You need to add a manifest to your module.
The simplest way is:
New-ModuleManifest -RequiredAssemblies:"path\to\newtonSoft.dll"
Then modify the manifest file manually for any other tweaks.
If the manifest doesn't solve the problem, you may need to pull out the nuclear hammer and set the binding redirects for ALL of powershell as mentioned in Powershell - Assembly binding redirect NOT found in application configuration file
For binary modules, there are a few keys that need to be populated in the manifest file for successful import/functionality:
RootModule = <'binaryModule.dll'>
RequiredAssemblies = <'binaryModule.dll'>
CmdletstoExport = '*' <--no need to restrict anything here, as
only the public functions you've developed in the assembly
will be exported to the user.
Those are the only keys you need to populate for the module to 'work' -- though I highly suggest combing through the .psd1 file generated byNew-ModuleManifest -path MyNewModule.psd1 to what other metadata values would help enrich the functionality of your module.
Also, ensure the names of the directory structure, .psd1 file, and assembly are all consistent.
SampleModule\
SampleModule\SamleModule.dll
SampleModule\SampleModule.psd1
...that should do it.
Related
This seems like the standard issue of an incorrect version of a DLL is being found and to put an assembly binding redirect (or update the DLL version referenced) to resolve. However...
The full error message appearing is;
System.IO.FileLoadException: Could not load file or assembly 'Microsoft.SqlServer.Smo, Version=15.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Microsoft.SqlServer.Smo, Version=15.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'
at DatabaseManagement.clsDatabaseManagement.DeployDatabase(clsDatabaseDeploymentElement deployment, String scriptPath, String dataFilePath, String logFilePath)
at DatabaseManagement.clsDatabaseManagement.DeployDatabase(clsDatabaseDeploymentElement deployment) in C:\agent\_work\4\s\src\Database\DatabaseManagement\DatabaseManagement.cs:line 920
at DBAdmin.clsProgram.ProcessAction(Action action, clsDatabaseManagement manager) in C:\agent\_work\4\s\src\Database\DBAdmin\Program.cs:line 664
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
Processing aborted!
Picking apart that message, the file it's looking for is 'Microsoft.SqlServer.Smo, Version=15.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'.
The file it's finding is: 'Microsoft.SqlServer.Smo, Version=15.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'
The strings are identical.
The assembly binding configuration;
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Smo" publicKeyToken="89845dcd8080cc91" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.0.0.0" newVersion="15.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
A hindrance here is that .NET's assembly binding logging doesn't appear to be in effect (as per the error message). I've gone through the steps to enable it but it remains disabled. The registry settings are correctly set and persist after a restart. It...just doesn't work, and the text that it's not enabled still appears as part of the error message.
What I've tried/confirmed so far;
The correct version of the DLL is present in the exe's folder (v15)
The dependencies of the DLL are also present in the same folder (dependencies found via ilasm, and cross-checked with ilspy)
The specific version of .NET that is used by the DLL (v15 was build on .NET 4.5), is installed and present on the target machine
The assembly binding config is being used, as if I change the newVersion to 16.0.0.0, the message changes accordingly
Registering the correct version of the file into the GAC completed successfully but had no effect on the error message.
Putting v16 of the DLL into the exe's folder had no effect on the message. It still finds v15. This suggests the binding is not looking at the DLLs in the exe's folder (at least for that assembly).
The various tests point me at the conclusion that it is finding the correct version of the file (somewhere, I'm assuming the GAC), but that there is some other incompatibility that's triggering the error message.
Without the Fusion logs, I'm left with black-box testing to try and diagnose.
The solution was ultimately the usual one, putting in the appropriate assembly binding redirects, but getting the right redirect was a bit of a journey.
The message displayed (assuming that .NET's binding logging is not enabled) is the assembly's display name. This string contains a version as part of it but this is distinct from the assembly's actual version or file version. This can make figuring out what's going on, as well as the correct binding, troublesome.
The display name will get shown as part of the error message. In Windows Exporer, right-clicking and going to Properties and the Details tab will list the file version. Getting the actual version was a bit trickier but you can use ildasm or ILSpy to open the dll and look.
In my case, the message was correct for the display name - they are indeed the same. What was actually happening is that the assembly being found was registered as 15.100.0.0, not 15.0.0.0, so there was a minor version mis-match. A further annoyance is that the assembly in the GAC was actually the correct version (I did a bit comparison to be sure), but had been registered incorrectly.
I was able to find this out by getting the binding logging turned on. While I had set the registry settings manually, they weren't functional. I used FusionLog++ to capture the binding steps, and these clearly showed;
the assembly it was finding
the reason for the binding failure
Some nuances to keep in mind when assessing the bindings/references;
there are some expected binding failures that are normal but will show as an error in the logs
some assembles will look for a *.resources.dll with the local culture setting (e.g., it may look for en-US, then en). This is normal and if those assembles don't exist, .NET will use the strings in the calling DLL.
some assembles look for msvcm90.dll (or there's no specified calling assembly in the log). This can be ignored.
if your application uses the XmlSerializer object, the .NET logic will look for an assembly YourApplication.XmlSerializers.dll. This is by design and can be ignored.
the binding will look in the GAC first for the referenced assembly. If found, this will ignore any DLL in the same folder as the application. It won't matter if you have the correct version deployed with your app if the target machine has a version in the GAC.
if your application is being built in a pipeline, the VM doing the build will need the appropriate 3rd party libraries installed if they are not specified by Nuget. Ensure they are the correct/same version as your development/target machine.
reference external packages via Nuget where possible rather than a direct reference
Using .NET 4.6.2 and an older Web-Site (not Web-Application) project. If I clear the BIN directory and then build and run it works, but sometimes after multiple builds and runs, it fails with this error.
Server Error in '/' Application.
Cannot load a reference assembly for execution.
....
[BadImageFormatException: Cannot load a reference assembly for execution.]
[BadImageFormatException: Could not load file or assembly 'netfx.force.conflicts' or
one of its dependencies. Reference assemblies should not be loaded for execution.
They can only be loaded in the Reflection-only loader context.
(Exception from HRESULT: 0x80131058)]
....
Is there any trick to getting Web-Site projects to work correctly when the libraries they use are beginning to pull in netstandard 2.0?
Also, it seems that this assembly binding redirect is necessary to get it to run but nuget adds a redirect to an older version 4.1.1.0. Any suggestions on how to fix that other than manually editing this line after most nuget updates?
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
Would all these issues go away if the project was migrated to a Web-Application project?
Most likely you are encountering this error because of a mismatch between x86 and x64 compiled assemblies or similarly a mismatch between .NET Framework and .NET Core.
Here are some troubleshooting options for you that you may not have tried.
Option #1
In the AppDomain there is an event handler for when the program is trying to resolve an assembly that is reference. If you subscribe to it, you should be able to get more information about what is missing. This is how the implementation would look:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Event Handler:
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
System.Diagnostics.Trace.TraceInformation($"Trying to resolve assebly: {args.Name} requested by {args.RequestingAssembly.FullName}");
// This event handler allows you to help the find the assembly for the CLR.
// you can dynamically load the assembly and provide it here.
return null;
}
Option #2
There is also a tools in the .NET SDK for troubleshooting binding issues.
Here is more information about the Assembly Binding Log Viewer
You need to enable it before it will emit any interesting information, but this should get you to the root of your problem, if the AssemblyResolve event doesn't help.
Yes, you can to convert web-site to web application:
If you can't to see this configuration - probably you have problem with names of classes, for example:
/Directory/Index.aspx.cs
/Directory2/Index.aspx.cs
This question already has answers here:
Is it possible to replace a reference to a strongly-named assembly with a "weak" reference?
(3 answers)
Closed 4 years ago.
I am developing a class library (MyClassLibrary).
I depend on a third party class library (ThirdPartyClassLibrary).
I need to use the same version of ThirdPartyClassLibrary as my users. e.g., if I set a static value in ThirdPartyClassLibrary the user needs to see that change.
Users of my class may be depending on any one of 4 different versions of ThirdPartyClassLibrary.
ThirdPartyClassLibrary is large, I do not want to distribute it with my software.
I have reflected on all 4 versions of ThirdPartyClassLibrary and validated that the things I will be doing with them are compatible across all versions (interfaces are the same, methods signatures are the same, etc.).
I need calls into ThirdPartyClassLibrary to be performant! I can't reflect on everything every time I need to call something.
MyClassLibrary will be loaded at runtime, so I can't expect users to mess with assembly binding redirects or other develop-time settings (or any settings at all, my users are resistant to doing anything).
I would like to benefit from compile-time checking of my code, so ideally no reflection at all.
How can I write MyClassLibrary such that when it is loaded into the process everything works correctly with whichever version of ThirdPartyClassLibrary the user has loaded?
One workaround would be to use the AppDomain.AssemblyResolve event at runtime. This fires whenever the resolution of an assembly fails. You can use this to load a different version of an assembly to that which the CLR is trying to load.
I've added a very simple demo on GitHub here:
https://github.com/danmalcolm/AssemblyResolutionDemo
This is set up as follows:
The main application App.exe directly references assembly ThirdPartyLibrary.dll version 2.0.0.0.
It also references MyLibrary, which references an older version of ThirdPartyLibrary version 1.0.0.0.
The AppDomain.AssemblyResolve event is used to redirect to the version used by the application when version 1.0.0.0 fails to load
AssemblyResolve is handled as follows:
public static void Initialise()
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveThirdPartyLibrary;
}
private static Assembly ResolveThirdPartyLibrary(object sender, ResolveEventArgs args)
{
// Check that CLR is loading the version of ThirdPartyLibrary referenced by MyLibrary
if (args.Name.Equals("ThirdPartyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fbcbfac3e44fefed"))
{
try
{
// Load from application's base directory. Alternative logic might be needed if you need to
// load from GAC etc. However, note that calling certain overloads of Assembly.Load will result
// in the AssemblyResolve event from firing recursively - see recommendations in
// http://msdn.microsoft.com/en-us/library/ff527268.aspx for further info
var assembly = Assembly.LoadFrom("ThirdPartyLibrary.dll");
return assembly;
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
return null;
}
We need to bind to the event before ThirdPartyLibrary is loaded, hence the explicit Initialise method.
Note also that the event only fires when the resolution of an assembly fails. If the version of ThirdPartyLibrary referenced by MyClassLibrary (1.0.0.0) were available in the GAC, then it would be loaded successfully and AssemblyResolve wouldn't fire. There would then be 2 different versions in use.
I'm demonstrating here that this mechanism could be used, I'm not saying it's a good idea. There are several things you'd need to take into account based on the environment in which your app is running and how it is set-up / installed / maintained etc.
No, you can't build MyClassLibrary with a reference to ThirdPartyClassLibrary in a way that says "just use whatever version of ThirdPartyClassLibrary.dll is available at runtime".
When you build your library, the version number of any referenced assemblies are included in the assembly manifest. Running the ILDASM tool against your assembly would show something like this:
...
.assembly extern ThirdPartyClassLibrary
{
...
.ver 1:0:0:0
}
...
Both the name and version of ThirdPartyClassLibrary are specified. At runtime, the CLR will attempt to load ThirdPartyClassLibrary.dll when it first runs instructions in MyClassLibrary.dll that reference it. It will look specifically for version 1.0.0.0 of ThirdPartyClassLibrary.dll (and will also require a matching public key if it's a strong-named assembly).
Here's a quick overview of how the CLR locates and binds to assemblies at runtime (full details at http://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110).aspx):
Step 1 - Determine the correct assembly version by examining configuration files - we'll return to this below, but for now, if you don't tell it otherwise, the CLR will attempt to load the exact version specified in the referencing assembly, so it will be looking for version 1.0.0.0.
Step 2 - Check whether the assembly name has been bound to before and, if so, uses the previously loaded assembly. Note that "assembly name" in this context includes the name and version, public key token etc, not just the name of the dll file.
Step 3 - Check the Global Assembly Cache GAC (strong-named assemblies only)
Step 4 - Locate the assembly through codebases or probing - essentially the CLR looks in different places to try to find (the specific version of) AssemblyB.dll somewhere. An error will occur if it can't find the specific version. It won't automatically fall back to an earlier or later version.
Unfortunately, this means that things won't "just work" and support what you describe above. If an application that references MyClassLibrary itself references a later version (2.0.0.0) of ThirdPartyClassLibrary, some bad things could happen when resolving MyClassLibrary's reference to ThirdPartyClassLibrary:
The CLR can't find version 1.0.0.0 of AssemblyB used by Assembly A and an error occurs
Version 1.0.0.0 happens to be installed in the GAC and is loaded successfully. While the code in the application is using ThirdPartyClassLibrary version 2.0.0.0, your library is using ThirdPartyClassLibrary version 1.0.0.0.
One thing that you can do is configure the application using your library so that the CLR will unify references to different versions of ThirdPartyClassLibrary.dll to a single version. This brings us back to step 1 of the assembly binding process outlined above - we essentially change the version of ThirdPartyClassLibrary that the CLR is looking for.
Binding redirects (http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx) are designed to channel references to different versions of an assembly to a single version. These are usually defined within an application's configuration file (Web.config, MyApp.exe.config), but can also be defined globally at machine level (machine.config).
Here's an example of a binding redirect that redirects all earlier versions of ThirdPartyClassLibrary.dll to version 2.0.0.0:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="AssemblyB" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Note that this can be automatically handled by Visual Studio 2013, which detects cases where different versions of an assembly are referenced and adds the binding redirects for you. There's also an Add-BindingRedirect command available in the NuGet Package Manager Console.
Binding redirects are a possible solution and could be a pragmatic choice in some scenarios. However, they could also confuse users of your library. If practical, you should consider distributing different versions of your library, built against the different versions of the third party library.
Due to a serious headache with log4net's strong naming and versioning, i.e., the old 1.2.10 name, the new 1.2.11 name, and (heaven help me) the 1.2.10 one SAP made and shoved into the GAC with its own strong name, my application will refuse to run on any machine but my own. It keeps looking for version 1.2.11 of the strongly named SAP version, which doesn't exist.
As nothing in my project is strongly named, I want to stop the compiler from looking for a strongly named assembly and just load the one I put in the directory. Since it is complaining about the manifest not matching, I'm trying to find a reference in my project for that version and public key, but I am coming up short. It seems this manifest is created at compile time.
How can I override that? is there a property or switch or conditional compilation I can use to specify exactly which assembly I wish to reference?
Ultimately I want to use the 1.2.11 version of log4net, and force the assembly to ignore the version in the GAC, to look only in the local bin directory. This really shouldn't be this painful.
Runtime Directive
At runtime, you can use AppDomain.AssemblyResolve to combat the assembly versioning issue when you don't care what specific version is present, you just want to load what's in the /bin folder. This code was created with inspiration from here.
string[] ignoreAssembyVersions = new string[] { "log4net" };
AppDomain.CurrentDomain.AssemblyResolve += (_, assembly) =>
{
//ignore the vesion number and return any version that has been loaded
var name = new AssemblyName(assembly.Name);
var shortName = name.Name; // retrieve short name
if (ignoreAssembyVersions.Contains(shortName)) // compare against list of assemblies we ignore revisions for
{
// check if this assembly is already loaded under a different version #
Assembly[] allAss = AppDomain.CurrentDomain.GetAssemblies();
List<Assembly> list = new List<Assembly>(allAss);
var loadedAssembly = list.Find(ass => new AssemblyName(ass.FullName).Name == shortName); // check if we have any version loaded yet
if (loadedAssembly != null)
return loadedAssembly;
else // assembly has not yet been loaded in this domain
{ // probe for assembly by name
Assembly probedAssembly = Assembly.LoadFrom(string.Format("{0}.dll", shortName)); // probe for any match on assembly.dll
return probedAssembly;
}
}
return null; // ignore binding failure -> pass up the stack
};
An alternative to handling the AssemblyResolve event would be to use a custom bindingRedirect policy. However - this only works if you know what version is present in the /bin or currently loaded in the App Domain.
Build-Time Directive
If you want control of which assembly version is used at build-time, you can use the Specific Version='true' for assembly references you add.
I hope I've understood your problem. You may consider using dependentAssembly configuration for supporting custom binding policy and assembly location for each assembly.
This is the snapshot of my configuration policy for looking up for another version of the assembly System.Data.SQLite
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.81.0" newVersion="1.0.81.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
I am trying to create an ASP.Net Web application that stores it's "content" (ASPX/ASCX and assemblies) somewhere other than the file system (Inside a service, for example) and loads them in dynamically as required.
I have successfully created a VirtualPathProvider that takes care of reading ASPX/Master/ASCX files from alternate locations but I am running into problems when those ASPX pages Inherit a class - ie: when they have code behind.
Thanks to answers in another question I am now successfully able to load the assemblies into the application at run time. But when my the runtime attempts to compile my page I get the following error:
"Compiler Error Message: CS0400: The
type or namespace name 'Web' could not
be found in the global namespace (are
you missing an assembly reference?)"
[System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
public class default_aspx
: global::Web.Code.CodeBehind,
System.Web.SessionState.IRequiresSessionState,
System.Web.IHttpHandler
I have boiled down my code to a simple example of what is going wrong so that it is easy for you to see. You can download this code here.
The assembly is being loaded in dynamically at run time using the AppDomain.AssemblyResolve event. This is in the global.asax and looks like this:
protected void Application_Start(object sender, EventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Assembly_Resolve);
}
Assembly Assembly_Resolve(object sender, ResolveEventArgs args)
{
Assembly assembly = AppDomain.CurrentDomain.Load(Resources.Web_Code);
if (args.Name == assembly.FullName)
{
return assembly;
}
return null;
}
Any ideas?
EDIT
If for any reason you need to update the Web.Code assembly - you will need to copy it into the "ReferencedAssemblies" folder of the web project for those changes to take effect.
I encountered the same problem when trying to load assemblies from DB (and using the AssemblyResolve event). But it seems to work fine if you load assemblies from disk or at least save a copy of the .dll file somewhere on the disk.
OK. Can you try the following - remove the AssemblyResolve event. We will now use web.config runtime configuration to resolve this assemblies. In the <runtime> configuration add the following:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="ReferencedAssemblies" />
</assemblyBinding>
I can't explain to myself why the two approaches - AppDomain.AssemblyResolve and this runtime AssemblyBinding give different results when dealing with PageBuildProvider. I was looking around the PageBuildProvider class to find any clues about what is happening, but still no luck. I'll probably look further into this as it got my attention and will post here if I find any solution.
Let me know if this solution with web.config file works for you. The only caveat is that you can't use resources to store your assemblies. You need to store them on the file system, any folder that is under the project one - as is currently with ReferencedAssemblies folder.