Kind of silly question, but I still can't understand it.
Let's assume I have A.exe file and B.dll file.
B.dll is located in ../Somewhere/Debug/B.dll.
I make a reference to this .dll from my A.exe project, but when I execute the A.exe it looks for the B.dll in the same directory, where A.exe is located. So why do I have to specify the path to B.dll (../Somewhere/Debug/B.dll) in my A.exe project if it does not actually use that?
I can build A.exe which references ../Somewhere/Debug/B.dll with CopyLocal = true. Then, I can make changes to the B.dll (for example add a new Class) and build it. After that I am able to use that new Class in my A.exe project, but if I set CopyLocal on B.dll reference to false and build A.exe, I will face the TypeLoadException once I try to execute A.exe, since it uses the old version of B.dll which is located in the same folder where A.exe is located.
The same thing with strongly named assemblies which are located in the GAC. I reference this assembly (e.g. ../Test/My.dll), but CLR still uses those which are located in the GAC.
Do references in Visual Studio play any role or they just serve for development purposes (like IntelliSense)?
References to assemblies (aka DLLs or EXEs) are used in two ways. Let's take your example. In B.dll, we have a class named ClassB which has a public default constructor and a public method (ClassB.MethodB) which takes a string and has a void return type.
In A.exe, the code instantiates an instance of a ClassB and calls MethodB on that instance:
var b = new ClassB();
b.MethodB("SomeString");
At compile time, the compiler needs to know about the existence of ClassB, as well as what its public methods/properties/etc. are. In C++, that would be encoded in an H file. In COM, it would be in a type library. In .NET languages (VB, C#, etc.), that is exposed as metadata in the assembly itself.
So, when you compile the A project into A.exe, you need to provide the compiler with a reference to the B.dll assembly; that's how the compiler knows what a B is. Note that there is nothing Visual Studio-specific in this; if you were to do this with csc.exe (the C# compiler), you'd still need to include the reference on the command line.
But, in addition to this, when A.exe runs, the runtime needs to load and JIT the B.dll assembly so that when the b.MethodB("SomeString"); line of code in A.exe runs, it can call the right method in B.dll. (JIT means just-in-time compile -- which really isn't pertinent here)
As #waleedNaveed points out, the runtime behavior is different if the assembly is strongly named or not. In particular, the runtime assembly search path for strongly-named assemblies starts with the GAC.
For non-strongly-named assemblies, the easiest place to find a referenced assembly is in the same folder as the currently executed assembly. That's why you typically set "copy local" to true; it makes it just work.
To answer your confusion regarding GAC. .NET framework starts by searching the dll(assembly) in the GAC (obviously if it is strongly named). If assembly is not found in the GAC, and if there is a .config file, then .NET framework searches the location in the configuration file, else .NET framework searches the assembly in the directory containing the executable (.exe). Even if the assembly is not found in the executable directory, then it gives the error.
Hope that answers your question and clears you confusion.
The compiler will copy B.dll every time you compile your solution.
The objective is to allow you to target a dll somewhere outside of your solution. As an example, this dll can be maintained by another team. (but in this case, an internal nuGet package would be a better solution.)
Note that referencing dlls in your compilation folder is not a good idea, because the content of this folder can be cleaned between compilation. In that case you use a source control for your referenced dlls, the read-only flag on the dll in this folder can cause compilation issues.
Related
I have project with managed DLL A.dll which depends on managed B.dll and C.dll.
I expose A.DLL to unmanaged C++ project D via COM interface. Everything is okay... But A.DLL can't find D.dll and C.dll and raises appropriate exception. I tried putting them in the same folder but it does not work. How and where should I reference those dependencies?
In C++ I would just build A.dll with static linking but .NET does not have this option.
Update: putting library in the same directory as .exe file works, I just lost my binary.
Normal CLR search rules apply here. It first looks in the GAC and next looks in the directory in which the EXE is located. You can convince the COM runtime to locate A.dll from the registration, it can be stored anywhere the Regasm.exe /codebase option tells it to look. But that does not affect where the CLR looks for dependencies, it only considers the EXE location.
You can troubleshoot this by using the Fuslogvw.exe utility.
Alternatives are in general troublesome. As long as you have a [ComVisible] type in A.dll that's guaranteed to be instantiated first (think "Application") then you can subscribe the AppDomain.CurrentDomain.AssemblyResolve event in the constructor to help the CLR locate the other DLLs. But it is very important the constructor doesn't need types from B or C, you'll still crash when the jitter needs them to compile the constructor.
If that's not a suitable option then writing an appname.exe.config file can be somewhat useful if you prefer deploying the DLLs in a subdirectory of the EXE install directory. This is however rarely a good idea in a COM scenario since you are typically not in control over the EXE, it is usually somebody else's responsibility. Deploying locally is fine when you test your code. For production deployment you ought to seriously consider the GAC. In general a good idea in COM anyway since registration is machine-global which gives it strong DLL Hell headaches.
I have a solution that includes several projects. A few are libs that are building dll's used in my main project in this solution.
My main project builds with output type console application.
This all works fine.
When i change the build output type to a class library (since i want to use this project as a plugin eventually). The project will still build, this time to a dll.
When i use this plugin in an application where i use it as a dll however, it will run up to a certain point where it's trying to load a type defined in an external dll (so NOT built by my solution) and throw the exception:
Could not load type 'externalinterface' from assembly 'externallib, version=3.0.0.0, Culture=neutral, PublicKeyToken=null'.
The dll's are all in the correct folder,etc.
Also worth noting, the plugin is tested in another location than where i built it. The executable still works on this location, the dll/plugin does not. Same amount of dll's in their folders etc.
EDIT: I already used ILSpy (dll inspector) to open the actual dll that is being referenced (so externallib in the errormessage) and checked if 'externalinterface' was present and it is.
EDIT2: RESOLVED! The program that loaded my plugin was loading the same dll that caused the exception. The dll it loaded was of another version than the one i loaded.
Check whether the type externalinterface is present in the referred dll.
You didn't include the details of the exception the application is throwing. However, based on the message you gave, it appears your assembly does not have a strong name. If the application attempting to load your assembly as a plugin does have a strong name, then .NET will require all assemblies loaded by it also have a strong name, so you need to configure your assembly to have a strong name before continuing.
Maybe some supported dll's which is used by the 'externalinterface' is missing in the target machine. In the target machine, check is all the necessary dll's are present in the output folder.
Or blindly copy paste all the dlls in the output folder from the machine where the code is working to the target machine where you have the problem. After this, if the code is working in the target machine, then try to analyze which supporting dll you are missed to copy.
I have a class library that represents my logic layer. To that library I've added a nuget package for Google.Apis.Analytics.v3 - it installed the package and all it's dependencies.
I have a console application that uses that logic class library (regular reference). everything is written and compiled fine.
The problem is that during runtime it threw an exception that Google.Apis.dll wasn't found. This DLL is a dependency that was downloaded with the nuget.
Checking the BIN folders, I've found that in the class library bin folder this DLL was present, but in the console application BIN folder it wasn't (while other related DLLs were). So this means that the not all references where copied during compilation.
I've searched online, and found all kind of workarounds that didn't really work (like manually editing the project file and removing a true xml line on that dll definition).
What I ended up doing is adding the same nuget library to my console application - it works but feels a little dirty and not the way it should be. I think the console app is the client who's supposed to get it's services from that logic class library which should know it's stuff without the "client" worrying about it.
Also, that console app is not the only one who's gonna use that service, I'm also planning on a web app that will use that functionality - so I will need to add the same nuget to that web app as well - again, feels a little messy...
Is it just me? is that the right way to go about it? I was thinking about writing a WCF project to handle that functionality - but that seems a little of a overhead for just on functionality, and probably slow my workflow down just to keep things "cleaner" in my opinion.
Am I just over-thinking it?
Thank
Explanation
For a sample scenario let's say we have project X, assembly A, and assembly B. Assembly A references assembly B, so project X includes a reference to both A and B. Also, project X includes code that references assembly A (e.g. A.SomeFunction()). Now, you create a new project Y which references project X.
So the dependency chain looks like this: Y => X => A => B
Visual Studio / MSBuild tries to be smart and only bring references over into project Y that it detects as being required by project X; it does this to avoid reference pollution in project Y. The problem is, since project X doesn't actually contain any code that explicitly uses assembly B (e.g. B.SomeFunction()), VS/MSBuild doesn't detect that B is required by X, and thus doesn't copy it over into project Y's bin directory; it only copies the X and A assemblies.
Solution
You have two options to solve this problem, both of which will result in assembly B being copied to project Y's bin directory:
Add a reference to assembly B in project Y.
Add dummy code to a file in project X that uses assembly B.
Personally I prefer option 2 for a couple reasons.
If you add another project in the future that references project X, you won't have to remember to also include a reference to assembly B (like you would have to do with option 1).
You can have explicit comments saying why the dummy code needs to be there and not to remove it. So if somebody does delete the code by accident (say with a refactor tool that looks for unused code), you can easily see from source control that the code is required and to restore it. If you use option 1 and somebody uses a refactor tool to clean up unused references, you don't have any comments; you will just see that a reference was removed from the .csproj file.
Here is a sample of the "dummy code" that I typically add when I encounter this situation.
// DO NOT DELETE THIS CODE UNLESS WE NO LONGER REQUIRE ASSEMBLY A!!!
private void DummyFunctionToMakeSureReferencesGetCopiedProperly_DO_NOT_DELETE_THIS_CODE()
{
// Assembly A is used by this file, and that assembly depends on assembly B,
// but this project does not have any code that explicitly references assembly B. Therefore, when another project references
// this project, this project's assembly and the assembly A get copied to the project's bin directory, but not
// assembly B. So in order to get the required assembly B copied over, we add some dummy code here (that never
// gets called) that references assembly B; this will flag VS/MSBuild to copy the required assembly B over as well.
var dummyType = typeof(B.SomeClass);
Console.WriteLine(dummyType.FullName);
}
If you have the following dependency chain:
Lib1 <-- Lib2 <-- MyApp
TLDR version: by not making assumptions, the build system avoids introducing uncertainty/unexpected behavior.
When you build MyApp, Lib2 will get copied to MyApp's bin directory for you, but Lib1 will not. You will need to add a reference to Lib2 and Lib1 in MyApp in order to get Lib1's dlls in MyApp's bin directory (otherwise you'll get the runtime error). It would be impossible (or maybe just really difficult) to identify the exact set of files that end up in Lib2's bin directory that would be safe & appropriate to copy over to MyApp's. If the build system made assumptions that everything in Lib2's bin directory was safe for MyApp, or if it implicitly referenced Lib1 for you, it could change the behavior of MyApp unintentionally.
Imagine a solution where more than 1 project depends on Lib2 but one of those projects wants to load an adjacent .dll file using Assembly.LoadFrom/Activator.CreateInstance/MEF/etc. (a plugin) and the other one does not. An automatic copy operation could grab Lib2 along with the plugin dll and copy it over to the first and the second project's build directory (since it's in the Lib2's bin directory as a result of a build operation). This would change the behavior of the second app.
Alternatively, if it was a little smarter and implicitly referenced Lib1 for you when you referenced Lib2 (and didn't just copy bin directory contents), it could still cause unintended consequences. What if MyApp already depended on Lib1, but it was using a GAC'd/ngen'd copy that was compatible with the one that Lib2 requires. If adding a reference to Lib2 implicitly created a reference to Lib1 for you, that could change which Lib1 got loaded and change the runtime behavior of your application. It could maybe detect that there already is a Lib1 in MyApp's bin directory and skip it, but then it would be making assumptions that the Lib1 that's already there is the right one. Maybe it's a stale .dll waiting to get wiped away by a Clean operation and the overwrite was the right move.
NuGet addresses the problem you're describing with package dependencies. If Lib1 and Lib2 both had nuget packages and the Lib2 package depended on the Lib1 package, when you add Lib2 to MyApp, Lib1 would get added as well. Both pacakges' dlls would end up in MyApp's bin directory.
The best thing to do is invert your thinking a little bit. Instead of thinking:
Lib2 needs a reference to Lib1 in order to compile so I'll add a reference to Lib1
Think:
MyApp needs Lib2. Whatever Lib2 needs, I need. So MyApp & Lib2 both get a reference to Lib1.
If you have 10's of dlls it's easier to do a copy with a postbuild event:
xcopy "$(ProjectDir)bin\*.dll" $(SolutionDir)MyTargetProject\bin\" /y
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does Copy-local work?
I have following situation:
there's a project named OLAF.Tools, and that project references Microsoft.Data.SqlXml in C:\Program Files\SQLXML 4.0\bin\Microsoft.Data.SqlXml.dll. Reference Copy Local property is set to True. When I build that project in bin directory I can see both OLAF.Tools.dll and Microsoft.Data.SqlXml.dll
there's a console application named OLAF.Generator, and that application references OLAF.Tools (I've added reference using Project tab). When I build that application in bin directory I can see only OLAF.Generator.exe and OLAF.Tools.dll - there's no Microsoft.Data.SqlXml.dll, what supprises me. Another wierd thing is that even though that dll is missing application is executing properly.
So my questions are:
why Microsoft.Data.SqlXml.dll is not copied to bin folder of OLAF.Generator console application?
how application resolves directory where Microsoft.Data.SqlXml.dll can be found?
Thanks,Pawel
EDIT 1: (after response from Marc Gravell)
#Marc Gravell: Your answer gave me food for thought, as I could swore that I could always see indirectly dependant assemblies in main application's bin directory. And IMHO I don't agree with you - with all due respect :)
Of course, references are not cascaded physically (we're are talking about strong relationship to classes, interfaces etc) - and it's exactly what I wanted to achieve when building OLAF.Tools library. That library provides a level of abstraction, it contains factories, and one factory accepts as a parameter string and returns interface. One particular implementation of that interface uses Microsoft.Data.SqlXml components. As a result,
OLAF.Generator uses interface that is located in OLAF.Tools, but doesn't know about components in Microsoft.Data.SqlXml.
Apart from that (I think we both know what I tried to explain in preceding paragraph), when building application, dependant assemblies should be copied (if Copy Local is set to TRUE). I just wrote sample application, Project B lib has reference to Project A lib, and Project C (console app) has reference to Project B. In Project C's bin directory I can see all: Project A.dll, Project B.dll & Project C.exe. So in discussed scenario, the reason why Microsoft.Data.SqlXml doesn't end up in OLAF.Generator bin folder has something to do with that assembly itself.
Does compiler/visual studio knows that Microsoft.Data.SqlXml is located in directory which is automatically probed (or it's in GAC) and this is the reason why that assembly is not copied?
EDIT 2: I've just checked GAC, and indeed, Microsoft.Data.SqlXml.dll is installed in GAC.
How does Copy-local work? log4net.dll is not being copied to MyProject output directory - this is answer to my question. When library is installed in GAC, it won't be copied even though COPY LOCAL setting is used.
References are not automatically cascaded, so adding a reference to OLAF.Tools does not also add a reference to SQLXML. If you want to deploy SQLXML with your exe, then the most convenient way to do that is to explicitly add a reference to SQLXML from your exe, and set it to copy local. Without this, it is not deployed. Basically, the onus is on the developer to decide which files are actually needed at runtime (which is often a subset of the references used, and depends on a number of deployment decisions which only you can know).
Re how it is resolved at runtime... the probing paths are a bit of a black art, mainly meaning "the app folder", but it depends on the config, and indeed the GAC may be consulted. You also get an opportunity to provide your own resolver, via AppDomain.Current.AssemblyResolve.
I have a C# project which references a DLL (call it external DLL) which comes with another application. When I build my project, due to the reference, the external DLL gets automatically added to my project output. And when I run my project it loads the external DLL from my project folder.
The other application, which the external DLL belongs to, is developed by another team and the DLL is regularly updated. I don't want to package their DLL with my project. Instead I would like to have my project load their DLL when executed -- rather than pick the DLL copy from my project's folder.
Now I know that this is possible through reflection. I know that I can do an "Assembly.Load" and pick the DLL. But because I use the types from the external DLL all through my code, I would like the code to be statically type checked.
Here's what I would like:
Be able to compile my project by referencing the external DLL and thus get static type checking.
When the project is run, the external DLL is picked up from the other application's folder and not the copy of the DLL which is in my project's output folder.
Is there any way to solve this problem? Is there some middle ground between adding a reference and using reflection?
The most immediete solution to your problem is to change the properties of the reference. There is a setting called Copy Local. Set that to false and it'll stop copying the DLL to your project's output. You can access the properties of the reference by expanding the references folder in your solution, right-clicking on the reference in question, and clicking properties to open the properties pane.
The fact that Visual Studio copies the DLL to your project's output folder at build time doesn't really matter to the .Net Framework at runtime. All that matters is that the assemblies you reference are available to the framework either in the paths it searches or in the global assembly cache.