I am having an issue (i.e. ReflectionTypeLoadExceptions) with assembly loading in a netcore3.1 application, specifically when using "NUnit.ConsoleRunner.NetCore --version 3.12-beta2" to load unit test assemblies which depend on other assemblies, CopyLocal is set to false for the project-references, although all assemblies are in the same directory.
When CopyLocal is set to false for a particular reference, the .deps.json file will not contain all the entries required for the dependee to be loaded by the AssemblyLoadContext directly, and so the AssemblyLoadContext.Resolving callback/event is fired, but not handled in nunit, or handled incorrectly. The ReflectionTypeLoadException is thrown later, when nunit calls assembly.GetTypes().
A simple demo of the issue can be found here: https://github.com/Ian144/NunitAssemblyLoadIssue.git, that includes a console-app that uses a custom AssemblyLoadContext to demonstrate the issue, or if "NUnit.ConsoleRunner.NetCore --version 3.12-beta2" is installed, just run "nunit AssemblyA.dll" from the output directory. In the demo, AssemblyA contains nunit tests, and has a project reference to AssemblyB with copyLocal set to false.
So, is there an issue with the nunit netcore runner, which should be handling the AssemblyLoadContext.Resolving event? or is there a different way to get assembly loading working under this circumstance?
thanks, Ian
There's some known issues with assembly loading in the beta2 version that were highlighted in the release notes.
The good news is, we've just merged a pull request that we think might fix it. There's a link to the pull request below, which also has details of where to find an updated version, and an appeal for people to try it out. Would appreciate if you could give it a whirl and let us know how it goes! đź‘Ť
https://github.com/nunit/nunit-console/pull/942
Related
I am wondering if there are any heuristics for when to set copy-local=true for references?
If referenced types are only used internally can I set copy-local to true but if referenced types are exposed as parameters or return values I set copy-local to false and indicate that a specific version of the dependency should be referenced when my library should be used?
Can anyone clarify this for me?
Copy local is important for deployment scenarios and tools. As a general rule you should use CopyLocal=True if the reference is not contained within the GAC.
Copy Local essentially means I must manually deploy this DLL in order for my application to work. When it's false it essentially means "I depend on another component which must be installed separately or chained, the DLL will just be there already".
Copy local was implemented really to support local debugging. When you perpare your application for package and deployment you should build your projects to the same output folder and make sure you have all the references you need there.
CopyLocal is especially a pain when building large source trees. There was a related question about how to disable CopyLocal here on SO you can see it at How do I override CopyLocal (Private) setting for references in .NET from MSBUILD. As well as Best practices for large solutions in Visual Studio (2008).
I have written about how to deal with building large source trees in the article MSBuild: Best Practices For Creating Reliable Builds, Part 2.
So in short I would say disable CopyLocal when the file copying is causing your builds to take more time then you are willing to spend for every build.
It's really about the target environment. If copy local is false, you're saying that the assembly will already exist in the target environment (normally in the GAC). Setting it to true ensures it will appear in the output of your build, so makes it easier to deploy to the target environment.
Check out the following MSDN reference which explains CopyLocal behavior in detail.
Project References
Unfortunately there are some quirks and CopyLocal won't necessary work as expected for assembly references in secondary assemblies structured as shown below.
MainApp.exe
MyLibrary.dll
ThirdPartyLibrary.dll (if in the GAC CopyLocal won't copy to MainApp bin folder)
This makes xcopy deployments difficult if you don't plan on installing the third party assembly into the GAC on the target machine.
This option only affects build phase. It just copies the reference to local directory of the built assembly.
If another assembly (T) wants to use a method from the assembly you are building (A) which has return type or parameters from another referenced assembly (R), it (T) should be able to access that assembly (R). It might be able to do so without doing anything special if the referenced assembly (R) is installed in GAC. Otherwise, it needs a local copy of that.
Set CopyLocal=false will improve build time, but can cause different issues during deployment time.
My experience with setting CopyLocal=false wasn't successfull. See summary of pro and cons in my blog post "Do NOT Change "Copy Local” project references to false, unless understand subsequences."
Conservative way to set CopyLocal false is to check that the reference is found in the output path of the project. This should allow you to dodge some nasty runtime issues, while still reducing the amount of IO.
In the process I created CopyLocalFixer, which you can run for a folder. I tried this with one large build, but the results weren't that impressive to be honest. I guess it comes down to the folder structure of the project.
My application compiles fine, but I get the following runtime error:
System.IO.FileNotFoundException was unhandled
HResult=-2147024770
Message=Could not load file or assembly {Wrapper} or one of its dependencies. The specified module could not be found.
The reference to Wrapper in the calling Application looks correct. The Wrapper dll exists in the correct location.
This project used to build and run on someone else's system, I saw it demonstrated several times. That person/computer is no longer available. Some paths of some dependencies have changed since the last time it was successfully built and run, I have fixed all compile errors related to this.
Just to clarify my project structure:
Digraph G
{
App [ label = "My C# Application"]
Wrapper [ label = "C++/CLI Wrapper"]
Lib [ label = "C++ Library"]
Dll [ label = "My helper C# DLL"]
CDep [ label = "Series of deep C++ dependencies managed \n by CMake for Lib, hard coded relative paths for Wrapper."]
App->Wrapper->Lib->CDep;
App->Dll->Wrapper->CDep;
}
Wrapper is a C++/CLI wrapper around a C++ library. The error is triggered when we try to load a class in the Application that has a using statement for the Wrapper.
Wrapper does have a lot of dependencies, but the error message does not indicate which dependency is problematic. This is a large, complicated system, most of which is built by other teams. The C++ components use CMake to get all the dependencies correct, but CMake does not natively support C#.
I tried using fuslogvw to find the binding error, but it showed me absolutely nothing until I changed settings to include all binds, and then it only showed successful binds.
http://msdn.microsoft.com/en-us/library/e74a18c4(v=vs.100).aspx
The paths are long, but not over 256 characters.
I had a warning in the compile for Dll (see graph above):
Warning 1 There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "{cli_wrapper.dll}", "AMD64". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.
In Configuration Manager, the Dll is building for Platform "Any CPU" and Wrapper is building for "x64". I changed the dll to x64. I still get the runtime error.
Could not load file or assembly or one of its dependencies
Performed a clean, deleted build directory contents. No change.
Re-opened Visual Studio. No change.
Tried changing assembly name, default namespace, and project name to match. No change.
Could not load file or assembly 'xxx' or one of its dependencies. An attempt was made to load a program with an incorrect format
I believe we have to compile for 64 bit. We are dependant on a 64 bit C++ library.
Could not load file or assembly ... The parameter is incorrect
I am local administrator.
How to enable assembly bind failure logging (Fusion) in .NET
Tried the registry settings, but they appear to just be fuslogvw settings. No improvement in available log data.
Many other similar questions had ASP or service install specific answers.
I found the problem. There was more than one version of the library available to me, and I was using the include files from one version and the compiled library of the other. Dependancy Walker was key to finding which library had the problem, and if Aschratt had posted that as an answer rather than as a comment I would have accepted his answer.
I had the same error with a similar project earlier this week. First, when using C++/CLI, "Any CPU" doesn't exist. I had to build both for x86 to get through things.
Also, though my wrapper's dependencies were satisfied, it's the wrapper's exception that is caught by the CLR so it claims that the wrapper is missing a dependency. I was actually missing a dependency for the C++ DLL my wrapper wrapped (in my case, it was just a matter of forgetting to drop SDL2.dll and OpenAL32.dll in my new "Release" configuration output folder... I'd previously only worked with the Debug configuration where I'd already dropped those DLLs.
If you're positive you've got your wrapper's dependencies worked out, I'd recommend using Dependency Walker to check out the native DLL's dependencies. You could use Assembly.GetReferencedAssemblies with an outside script/program to double-check your wrapper as well (or ILSpy: http://ilspy.net/).
Last but not least, take a look at this: http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI. He details the first two recommendations I made as well as some others.
Good luck!
One alternate root cause I've been running into quite a bit later, is that the application I'm working on uses impersonation, and the impersonated user does not have permissions to the GAC or to the folder that some dlls are in. You need to either give that user permissions, or change the way you do impersonation to load the dependencies before impersonation starts.
I have some C# which fixes an issue that is already fixed in a newer release of Sitecore CMS.
I want the fix to be removed if and when we upgrade to the newer version, but that may be some time in the future, and the presence of this fix would be easily forgotten.
Is it possible to break the build or otherwise draw attention to this section of code when the referenced assembly version changes (i.e. higher than v6.5.x) ? A conditional around an #error directive may work, but I don't know if or how this can refer to a referenced assembly version?
Note that I'm hoping this can happen at build-time, not runtime, and specifically draw attention to the code in question so that it can be reviewed.
Surely to do this all you would need to do would be to make sure your projects have the "SpecificVersion" flag set to true for that reference?
How about using a post-build (or pre-build I suppose) event in Visual Studio? You could run a powershell script or something that will check your assembly version and alert you that the assembly version has changed.
A bit ugly but I think it will work.
Info on build events...
http://msdn.microsoft.com/en-us/library/ke5z92ks.aspx
While the following solution won't run at build-time, you could do this with unit tests in your health check (assuming you are doing CI with unit tests).
You write a unit test to test that piece of code. Make sure the unit test asserts the version of the Sitecore DLL reference. Your health check build will fail when the unit test runs.
You could even gate the check-in with this to make sure nobody could check in without that unit test passing.
I'm getting the following error:
error CS1704: An assembly with the same simple name
'Interop.xxx.dll, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null has already been imported. Try removing one of the
references or sign them to enable side-by-side.
Everything I've seen says that I am referencing two assemblies with the same name and I need to remove one of them. However, I've checked and I'm only referencing it once.
This also only happens when I'm using msbuild to build from the command line on my dev box. If I build through Visual Studio or do a clean build on our CI server I don't see this error.
I've tried completely removing all of my source and building from scratch to more closely resemble the build machine but no luck.
So it looks like I can't read today!
The project had a reference to the Interop and a COM reference that generated the "same" interop. So there were two and I just didn't search very well. I still don't understand why it worked in other places but this did fix it.
In the Error List window, the project that was triggering this error was listed in the Project column. I got around the error by doing the following:
I unloaded the listed project (right-click => Unload Project)
Opened the XML for edit (right-click the unloaded project => Edit {ProjectName.csproj}).
Searched for the offending .dll, and noticed it was listed multiple times in the XML
Removed the entire Reference tag related to the offending dll, and did so for every copy of the reference except the first one listed
The reason it was listed multiple times was because several referenced libraries used that dll. This shouldn't be a problem, in and of itself, so I'm not sure what caused this error to suddenly pop up for me. I'll update this answer if I figure that out.
In my case the duplicate entry was caused by a NuGet package reference and a direct file reference to the same assembly in the packages folder. I am not sure how the project got into this state, but unloading the project and searching the XML file for the offending assembly name resolved the issue for me.
Note that in my case this started happening after updating a NuGet package to a newer version with no other changes to the project, so this maybe caused by a bug in NuGet.
If this is a web project, are there any strong-named references to the other version there? Those won't show up as a project dependency, but will cause a run-time error like you describe. Hope that helps
I had this problem but in my case, I had an old copy placed in the current folder for the EXE loading my component, that was loaded together with the current one, that was loaded by hand from my projects folder. Deleting that old copy solved my problem.
I used Debug > Windows > Modules window to see which modules were loaded at that time and that solved my problem.
For others facing the same as me: if building via command line using property AssemblyName, it will overwrite all assemblies generated by all solution projects - in other words, you will end up with (N -1) assemblies named the same where N is the no. of projects - the startup one (which generally will generate an exe).
This happens because all build command line properties are global and overwrite any project-specific setting. See this and this.
From the msdn link mentioned above:
Global properties are properties that are set by using the
/property switch on the command line, or properties that are set by
the integrated development environment (IDE) before a project is
built. These global properties are applied to all projects that are
built by using this Engine.
In my specific case, where Jenkins is the CI tool, I ended up adding a windows batch command at the end to rename the .exe only to what I originally intended when passing the AssemblyName parameter.
For those developing UWP projects that have project references that include specifically the Microsoft.Windows.SDK.Contracts nuget package (or other dependencies that reference it), this is a common error when the version of the SDK contracts is targeting a different version of the runtime to how your project is configured.
For instance, when targeting Windows 10, version 1903:
Any dependencies or reference projects should target or at least support the same runtime version.
it is common thought process to update all NuGet packages when a new stable version is available, but this is not always a helpful practise on its own. Just because a new stable version of a package is available does not mean that you should or that you can easily use that version.
Even though this package for SDK contracts has a stable update, it is not compatible with my main project configuration, Nuget does not know this so it allows the update.
This package is specifically designed to provide windows dlls for project types that DO NOT have windows platform targeting support, it copies the same dlls that are included by the UWP targeting config. By installing later versions of the package the references from the satellite project will be included in the output along with those provided due to platform targeting, ultimately causing OPs error.
There are similar SDK and targeting packs for Windows IoT Device Runtimes, this information should help you identify and resolve those issues if you get stuck on this issue as my team often does :)
In my case, the issue was on wrong characters in the ProjectReference section of my csproj file.
Background
I have a project that references another library I maintain, which I publish as a NuGet package.
Whenever I make changes to my library, I usually reference the local dll in my project to test and make sure everything looks good before I publish the library as a NuGet package.
When testing, I just comment out the PackageReference line and uncomment the ProjectReference one so it references my local dll, like so:
<ProjectReference Include="..\..\my-class-library\MyClassLibrary.csproj" />
<!--<PackageReference="MyClassLibrary" Version="2.0.1"/>-->
Root cause
I had the slashes inverted, so I was using / rather than \ in the path, like so:
<ProjectReference Include="../../my-class-library/MyClassLibrary.csproj" />
Once corrected, the issue went away.
Try this instead: remove Interop.xx.dll from the reference section in Solution Explorer and Rebuild the project
In our case this error was shown when we had a duplicate reference inside the .csproj file (although I have no idea how this happened).
The difference to an already posted answer is that, in our case, one was a project reference and another one was direct binary reference to a dll.
Once we removed one of those, project correctly compiled.
I'm developing a custom PowerShell snap-in, which references another project in the solution.
When I try to debug the snap-in (following [these instructions][1]), the assembly fails to load and my cmdlet fails with the message "Could not load file or Assembly..."
How do you instruct PowerShell on how to locate assemblies, or how do you specify where are located the assemblies needed by the snap-in?
I'd prefer to avoid registering the assemblies in the GAC, at least during development.
Not sure of the exact behaviour, but I would try and make use of fuslogvw to see exactly where the runtime is looking for the problematic assemblies. That would give you a clue as to how to get them to be copied into the correct place on build. This post by Scott Hanselman is quite useful, and this is the official documentation.
If you find the solution, please add an answer, as this must be a common scenario.
SOLUTION (Posting it here as suggested by one of the comments to my question)
My problem was rather specific, as I'm developing on a 64 bits server machine, but I'm posting the solution in case it could help someone else as well.
Using fuslogvw as suggested, I saw that the dependent assembly was being searched using the machine.config file under C:\Windows\Microsoft.NET\Framework64 and then the binding failed; launching the project with "start without debugging", instead, the machine.config file under C:\Windows\Microsoft.NET\Framework was taken (notice the missing 64 at the end).
I thought that the problem could be due to the image format, and infact the dependent assembly was being compiled with x86 as CPU target; I changed it to "Any CPU" and now the assembly is loaded correctly.