Shadow copying is a feature in the .NET framework to allow assemblies used in an app domain to be updated without unloading the app domain. Very useful in web site binary updates update/release for example.
Is there a programmatic way to read the formal location assembly folder i.e. the website \bin folder instead of the shadow copy folder?
For example when GetExecutingAssembly like this:
System.Reflection.Assembly.GetExecutingAssembly().Location
It returns ShadowCopy location folder:
C:\Windows\system32\config\systemprofile\AppData\Local\assembly\dl3\4LPBW244.CR4\DZO34RQG.YAW\caa6207b
I want to obtain the the \bin folder as per my example...
4 years without a response!
And I needed a response to this exact question!
I finally found a possible answer. This is what I did when trying to obtain the Location of an assembly. The assembly file name was "IKVM.Runtime.DLL"
System.Reflection.Assembly.ReflectionOnlyLoad("IKVM.Runtime").Location
Note the the file extension had to be removed!
Related
I'm using WinSCPnet.dll in my project and to run correctly it needs WinSCP.exe in launch directory e.g. project_path/obj/Debug. But if I want to use my program, it needs to have this .exe file all the time in same folder otherwise the program will crash.
Is it possible to place WinSCP.exe into project resources so I don't have to place it in same directory everytime I move it?
(Sorry for my clunky English, don't downvote me for that :/ If you need some additional info I'll gladly post it)
The winscp.exe has to exist as a real file, at the moment you call the Session.Open.
So while you can store the winscp.exe to your application resources, you have to extract it somewhere (e.g. to a temporary folder), and set the Session.ExecutablePath accordingly, before you call the Session.Open.
Inspired by your question, I have added instructions for Embedding WinSCP executable as resource to the assembly documentation.
I have been trying to get any one of the following directories:
Root of solution folder
Startup project folder
I came across this:
System.IO.Path.GetDirectoryName( System.Reflection.Assembly.GetExecutingAssembly().Location );
but i'm unsure what it's actually showing me.
for example,
the folder i'm trying to get to is:
C:\Users\James\Documents\Visual Studio 2013\Projects\Test\Test.WebUI\Uploads
And the above function returns this:
C:\Users\James\AppData\Local\assembly\dl3\1RC2V770.35T\WML2RQJT.GX5\9ebc1d95\b8f9a830_5676d001
Is this at all helpful to me?
The context of the issue is that i'm running an entity framework seeder and need access to the startup projects folder structure.
What you are seeing is the shadow copy folder that .NET uses for cache. It is specified in Windows Registry under HKCU\Software\Microsoft\Fusion\DownloadCacheLocation. Shadow copying is a feature in the .NET framework to allow assemblies used in an app domain to be updated without unloading the app domain.
There is a similar question here.
That is the reason why you can't retrieve your original folder location using System.Reflection.Assembly.GetExecutingAssembly().Location
You might want to look at AppDomainSetup.ShadowCopyDirectories if you want to change/read the directories where the dlls are stored.
Hope it helps
Update - Original Question Below
I've done some additional testing and I now believe that the source of my problems is how .NET is locating the assemblies.
As I mentioned below there are .dll files in subdirectories but the .dll files I mentioned below are not the only .dll files in each directory. For example, in the \translation\Customer directory there is a Customer.dll file and a couple standard files (Translation.dll, Mapping.dll, Execute.dll). In the \translation\Standard directory there is a Standard.dll and then the other standard files (Translation.dll, Mapping.dll, Execute.dll). These files are all generated code (from different projects) which is why each directory has copies of the standard files as well as a file that's directly related to the subdirectory.
When a method from the Customer.dll file is executed in my Windows Service application other methods in the standard assemblies (Translation.dll, Mapping.dll, etc...) will also be called. I believe that calls to other .dll files are not being executed on the .dll in the same directory.
For example, I call a method on /translation/Customer/Customer.dll and, in turn, that calls a method on Translation.dll. However, instead of the calling the method on /translation/Customer/Translation.dll the application is finding /translation/Standard/Translation.dll first and calling the method on that file.
Is it possible to force .NET to make calls to these associated .dll files within the same directory? Or do I have to do something to make the "standard" assemblies uniquely named in each directory?
Original Question:
I have a C# Windows Service application that monitors a directory and processes incoming files. Each file that gets processed has a configurable set of tasks run against the file. The code for each task is contained in an external .dll that is loaded dynamically at run time using reflection. The external .dlls exist in several subdirectories of the main application folder and access to them is granted via entries in the application's <probing> element in the app.config file.
I've recently expanded the number of subdirectories to better organize the external .dlls and I've encountered some unexpected functionality with the <probing> element. I am running a test to process a file that requires 2 tasks to be executed against the file. The first task exists in the file Customer.dll and is located in a subfolder called "\translation\Customer". The second task exists the file Standard.dll and is located in a subfolder called "\translation\Standard".
If my probing element looks like this <probing privatePath="translation\Customer;translation\Standard"> the second task (running a method on a .dll in the "Standard" folder) will fail to execute.
However, if I switch these entries so that the probing element looks like this <probing privatePath="translation\Standard;translation\Customer"> both tasks execute successfully.
Can anyone help me understand why the order of subdirectories in the probing element would have an effect on the execution of .dlls contained within those subdirectories?
Further investigation has revealed that this was not a reflection or assembly loading problem. The problems I was experiencing was due to build issues inside the assemblies I was reflecting on. The errors were obscure and it made the problem look like something it wasn't. Thanks to SWeko for noting to look into the inner exceptions. That comment helped a lot!!
Due to problem with referencing application config in excel addins (Excel Add-In not loading app.config with service reference config information) I have referenced the assembly location for the config file.
The problem I am now encountering is that each time I launch a debug instance, the assembly is put in a different path (GUID folders under local AppData directory). So I cannot put a copy of the config file in there.
Any suggestions to get around this?
Best solution turned out to be discard above solution and use solution from post:
Change default app.config at runtime
Because while the original link would then allow you to access the config file, you would have to manually extract the information to bind the webservice.
However originally I did persist with the original and found:
To enable the solution in original link to work I had to change the way of accessing directory (which I have now submitted this change to the original linked solution):
AppDomain.CurrentDomain.BaseDirectory
rather than
new FileInfo(Assembly.GetCallingAssembly().Location).DirectoryName
AppDomain.CurrentDomain.BaseDirectory would reference a stable location while debugging (the source directory where the original app.config is located), and the correct install directory for the installed version of the Addin (the addin directory rather than Microsoft Excel directory).
I have a solution containing two projects. One project is just for doing all data stuff and the other one, the startup project, do all the web stuff.
Now I want to get the TasksDataBase.xml from the TaskManagerHelpers class by first getting the projects root directory. But all I get is the TaskManager.Web root directory. (I call the method inside TaskManagerHelpers.cs from a controller inside TaskManager.Web)
How do I get the TaskManager.Data root directory when I'm in a class in the same project?
I've tried with theese methodes and similar ones.
HttpContext.Current.Request.PhysicalApplicationPath;
System.IO.Path.GetFullPath();
AppDomain.CurrentDomain.BaseDirectory;
Thanks in advance!
One possibility is to embed the XML file into the assembly of the class library and then read it as resource in your web application. Remember that when you publish your web application to a web server all that will get into the package will be the files of this web application. There's no physical relation to some projects that might have lived into the Visual Studio solution that this web application was part of.
You may take a look at the GetManifestResourceStream method which will allow you to read the embedded XML from the referenced assembly.
Here's an example:
// you could use any type from the assembly here
var assembly = typeof(TaskManagerHelper).Assembly;
using (var stream = assembly.GetManifestResourceStream("TaskManager.Data.DataBase.TasksDataBase.xml"))
using (var xmlReader = XmlReader.Create(stream))
{
// ... do something with the XML here
}
Bear in mind though that since the file is embedded into the assembly you will not be able to modify it. It is readonly. If you need to modify it then an alternative approach would consist into copying this file to your web application. For example a good place is the App_Data special folder. You could even setup a post compilation step that will copy the XML file in this location.
And then you can reference it easily:
string xmlFile = HostingEnvironment.MapPath("~/App_Data/TasksDataBase.xml");
using (var xmlReader = XmlReader.Create(xmlFile))
{
// ... do something with the XML here
}
In this case since the XML file is now physically part of the web application and lives on the hard drive you could also modify it.
Just because the two projects are located in the same folder tree during development, says nothing about where they'll be located at run time. It's entirely possible that that could be on different machines.
"No," you say. They'll will definitely be on the same machine in the same c:\inetpub tree. That may be true, but that's your policy, not a requirement.
If you are going to establish a hard policy about where they are located, then you can hard-code that into you code.
Right-click the XML file and select properties, then change the Copy to Output Director to one of the other settings than "Do Not Copy". That will place the file into your \bin\ folder alongside the other project output. You can then use AppDomain.CurrentDomain.BaseDirectory as your base path
IF you are running a web project, all the referenced dll files are copied to the bin directory (unless they are in the GAC) and used from there, no matter if you add a reference to another project, Visual Studio first compile it and then copies it to the bin folder of the web project. You can mark your xml file as Content (Compilation Action) and with the copy always option so it always copy it to the bin directory .... the problem is that it sometime look for this files outside of the bin folder but I think that you can handle this.