DLL not found when running outside debug folder - c#

Application names, paths and urls have been renamed to 'MyApp' and 'example' just for easier reading.
Hello, I currently use 1 dll file for my application and that is log4net in c#. Now I include my dll in references from
C:\Users\ashle\AppData\Roaming\MyApp
Simply because I will be publicly releasing my application. Now it works fine outside debug mode and inside, but when I run the exe outside the /bin/debug folder it throws an error..
Unhandled Exception: System.IO.FileNotFoundException: Could not load
file or assembly 'log4net, Version=1.2.15.0, Culture=neutral,
PublicKeyToken=669e0ddf0bb1aa2a' or one of its dependencies. The
system cannot find the file specified.
at MyApp.Program.Main(String[] args)
I have also put this code in which I thought would stop it happening.. but what am I doing wrong? this code should cover my a**
if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp"))
{
Console.WriteLine("Created directory: " + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp");
Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp");
}
if (!File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp/log4net.dll"))
{
Console.WriteLine("Please wait while we download some files...");
string downloadUrl = "http://example.com";
if (checkWebsiteAvalibility(downloadUrl))
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(downloadUrl + "/downloads/log4net.dll"),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/MyApp/log4net.dll");
Console.Clear();
But when running the exe solo outside /bin/debug it doesn't even display the "Please wait while we download some files..." line

Before publishing your project, delete the dll file from your reference. Add the same dll through Add Reference. Then try to publish it.
It worked for me.

When I ran into this issue, I deployed the .dll to the running location of the executable. Add the dll to the project Resources and set its Build Action to Content.
class Program
{
//Programmatically, the Program() method is called prior to Main() and before any registrations are made.
//This is where we write the dll to the disk.
static Program()
{
//Path.GetDirectoryName() returns the folder path to a particular file.
//Assembly.GetExecutingAssembly().Location returns the path to the current running location of the compiled executable, with the name. E.G. C:\MyProject\MyProgram.exe
//We combine this with Path.GetDirectoryName to get the folder, and then write the dll into this folder. That way, when this method finishes and main is called, it will find the dll in the folder.
File.WriteAllBytes(string.Format("{0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\log4net.dll"), FindResourceByName("log4net"));
}
/// <summary>
/// Returns a byte array of the object searched for
/// </summary>
/// <param name="objectName">Name of the resource object</param>
/// <returns>Byte array of the specified resource object</returns>
private static byte[] FindResourceByName(string objectName)
{
object obj = Properties.Resources.ResourceManager.GetObject(objectName);
return ((byte[])(obj));
}
//Rest of your code goes here...
}
In my situation, my DLL was called "Microsoft.Win32.TaskScheduler.dll" If this is the case, with your application, the periods will be replaced with an underscore when your DLL is added as a resource. Make sure that you reflect this when calling FindResourceByName or your lookup will fail:
File.WriteAllBytes(string.Format("{0}{1}", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\Microsoft.Win32.TaskScheduler.dll"), FindResourceByName("Microsoft_Win32_TaskScheduler"));

Related

Is there an way to add reference(dll) in parent path in C#?

== compile command ==
csc -r:"../Newtonsoft.Json.dll" test.cs
== exec command ==
mono test.exe
== exec result : dependency error ==
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
"Newtonsoft.Json.dll" this file is located in parent path. so I added a reference about dll and compile succeeded, but when I executed the exe file, it failed to get dll reference I added.
And when I put cs file and dll file together in the same directory, it worked very well, but that's not what I wanted.
Is there a solution to add a reference from dll file which is located in parent path using command line interface?
I used csc for compiler and mono for execution.
Thanks.
References are pathless. What that means is that wherever the assembly resides, all your program knows is that it has a reference to Newtonsoft.Json, Version=x.x.x.x, Culture=... and so on. You can do some things with the application configuration (application.config or myprogram.exe.config) to organize things into subfolders (using the probing setting) or specify a URL location for the file (using the codebase setting). You can set up the environment to change the search path and so on.
Or you can add some runtime code that allows you to override the default behavior and the call Assembly.LoadFrom to provide a full path to the file. You can either do it as part of your initialization or in a handler for the AppDomain.AssemblyResolve event - which is generally better method since it will only be called when the assembly is actually needed.
For example:
using System.IO;
using System.Reflection;
static class ParentPathResolver
{
public static void Init()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolve);
}
private static Assembly? Resolve(object? sender, ResolveEventArgs args)
{
var filename = new AssemblyName(args.Name).Name + ".dll";
var fullname = Path.GetFullPath(Path.Combine("..", filename));
if (File.Exists(fullname))
return Assembly.LoadFrom(fullname);
return null;
}
}
Of course you can add your own code to the Resolve method to search pretty much anywhere, just as long as you don't get caught in a resolution loop. I've used the AssemblyResolve event to do fun things like loading assemblies from compressed resources.

Can't find .XSL file embedded in project DLL

Quick question that's wrecked my morning and is driving me nuts.
I have a small project that includes a DLL from another project. The DLL has an XSL file embedded in it that I want to extract and apply to a webbrowser control.
I have no problem with extracting / accessing embedded resources in the main EXE file, but I cannot find the means of accessing it in the DLL!?
I've tried:
"SolutionName.DllName.Resource.xsl"
"ExeName.DllName.Resource.xsl"
"ProjectNamespace.DllNamespace.Resource.xsl"
...and pretty much every permutation thereof, but it's never able to find it.
I don't have a dot-notation reference for it in C# to use with nameof(), and I can't find any apparent reference / access to it with:
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
So, what's the correct naming (or other?) method for retrieving this file?
In case any of this helps, here's some additional details:
Project Name: DataBuilder
Project Namespace: DataBuilder
DLL Name: CobCommon
DLL Namespaces: CobCommon, CobCommon.Classes, CobCommon.Classes.Data, CobCommon.Winforms, CobCommon.WinForms.Controls
XSL Resource Name: XmlFormating.xsl
The specified resource file operation is "Embedded Resource" and it's located in the "root" area of the DLL project.
Accessing global:: gives me CobCommon, and DataBuilder amongst the available choices, but CobCommon doesn't have either a .Properties or a .Resources option, and DataBuilder which does have .Properties.Resources gives "Culture" as the only reference.
The XSL file is listed on the DLL Project's "Properties|Resources|Files" tab.
What am I missing?
Using GetExecutingAssembly() will probably always refer to your assembly. Instead, create an instance of some innocuous, (hopefully) simple object declared in that external DLL, then use that instance object's...
<object-from-DLL>.GetType().Assembly.GetManifestResourceStream("what.youre.looking.for")
to get a stream handle to your embedded object.
So this was the way that I finally managed to put together a generic function to retrieve any text-encoded embedded resource from any project assembly (and that's working as intended in my project):
First, I extended the Assembly class to facilitate grabbing just the relevant leading portion of Assembly.FullName that we need to use to search for the requested resource:
/// <summary>Adds a function to dereference just the base name portion of an Assembly from the FullName.</summary>
/// <returns>The extracted base name if able, otherwise just the FullName.</returns>
public static string ExtractName(this Assembly root)
{
string pattern = #"^(?<assy>(?:[a-zA-Z\d]+[.]?)+)(?>,).*$", work = root.FullName;
MatchCollection matches = Regex.Matches(work, pattern, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
if (matches.Count > 0) return matches[0].Groups["assy"].Value;
if (work.IndexOf(",") > 3) return work.Substring(0, work.IndexOf(','));
return root.FullName;
}
Then I wrote this function to search for the specified assembly + embedded resource file and return its contents as a string, if found:
/// <summary>Searches all loaded assemblies in a project for the specified embedded resource text file.</summary>
/// <returns>If the specified resource is found, its contents as a string, otherwise throws a DllNotFoundException.</returns>
/// <exception cref="DllNotFoundException"></exception>
public static string FetchAssemblyResourceFile(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
int i = -1; while ((++i < assemblies.Length) && !Regex.IsMatch(name, "^(" + assemblies[i].ExtractName() + ")", RegexOptions.IgnoreCase)) ;
if (i < assemblies.Length)
{
try {
using (System.IO.Stream stream = assemblies[i].GetManifestResourceStream(name))
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
return reader.ReadToEnd();
}
catch (Exception innerExc)
{
Exception result = new Exception("The requested resource (\"" + name + "\") was not found.");
result.InnerException = innerExc;
throw result;
}
}
throw new DllNotFoundException("The requested assembly resource (\"" + name + "\") could not be found.");
}

Location of original assembly(not the current executing)

Suppose I have a solution which contains 4 projects, A, A_UnitTests, B, and B_UnitTests.
Project A has a data file, which is added as a link to A_UnitTests and set to copy to the output directory. When unit tests are run or when the code is executed in production, the path to that file is correctly identified using the following code snippet:
public static string GetFullPath(string relativePath)
{
string retVal = string.Empty;
if (System.Web.HttpContext.Current == null)
{
string locationBeforeShadowCopy = typeof(A.SomeClassInA).Assembly.CodeBase;
UriBuilder uri = new UriBuilder(locationBeforeShadowCopy);
string locationWithoutUriPrefixes = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(locationWithoutUriPrefixes);
retVal = Path.Combine(dir, relativePath);
}
else
{
// stuff that doesn't matter
}
return retVal;
}
However, I have a new testcase in B_UnitTests which attempts to use this code path to find the file location. However, even though I call typeof(A.SomeClassInA).Assembly.CodeBase, it is being called from B_UnitTests, using its referenced DLLs. This means the path return is the B_UnitTests output directory + a relative path. So it doesn't find the data file.
Without resorting to hard coded settings and build scripts, what could I use to specify the correct path?
Update (clarification)
The real issue is with typeForClassInA.Assembly.CodeBase returning the path of the executing assembly rather than A itself. It seems very wrong to provide a type that comes from some assembly but instead of returning the original assembly location, it returns the path to the executing assembly which happens to have a reference to it.
If there is a reference to 'typeForClassInA', then its assembly will be being copied into the output directory of B_UnitTests. So when you ask for CodeBase of that type's assembly from a test in B_UnitTests, it is (correctly) pointing at the version of assembly A in the B_UnitTests output folder, because that's where it's being loaded from.
I admit that I avoid using Shadow Copy to avoid exactly these kinds of problems of locating resources which are alongside the assembly, since ShadowCopy doesn't understand that they are needed, and they don't get shadow copied.
Another thing which can help is to build all the projects into the same output folder by changing all the project output folders to be "..\bin". For example, this would mean that A_UnitTests would not need the link to the resource file (once shadow copy is off).
I have a method similar to the one you've shown which goes "up" from the assembly's location (which for me is the shared bin folder) to the solution's location; and my relative paths are 'rooted' at that folder.
If that all sounds too complex, you could just use the same approach that A_UnitTests did, of including a link to it from B_UnitTests.

Resource can not be loaded IF program.exe name changed after compiling

I have an unmanaged dll so i wrote this to save the dll to file once the program is run.
Working code :
public static void ExtractResourceToFile()
{
if (!File.Exists("loader.dll"))
try
{
using (System.IO.FileStream fs = new System.IO.FileStream("loader.dll", System.IO.FileMode.Create))
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Kraken.Resources.loader.dll").CopyTo(fs);
Thread.Sleep(5000);
}
catch ( Exception ex)
{ }
}
Problem:
if the compiled Kraken.exe name changed the DLL is not being saved.
what I've tried :
public static void ExtractResourceToFile()
{
if (!File.Exists("loader.dll"))
try
{
string file = System.Reflection.Assembly.GetExecutingAssembly().Location;;
string app = System.IO.Path.GetFileNameWithoutExtension(file);
using (System.IO.FileStream fs = new System.IO.FileStream(file, System.IO.FileMode.Create))
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(app + ".Resources.loader.dll").CopyTo(fs);
Thread.Sleep(5000);
}
catch ( Exception ex)
{ }
}
I've also tried to get the current process name and use it, but same problem occurred.
See this:
How do I get the name of the current executable in C#?
Particularly these comments are interesting:
Beware of GetExecutingAssembly(): if you call this from a library
assembly, it returns the name of the library assembly, which is
different from the name of the entry assembly (i.e. the original
executable). If you use GetEntryAssembly(), it returns the name of the
actual executable, but it throws an exception if the process is
running under WCF (admittedly a rare situation). For the most robust
code, use Process.GetCurrentProcess().ProcessName. – Contango Nov 3
'10 at 12:01
Hmm, note that the returned string won't change even if you rename
the executable file by hand using the file explorer. While
Environment.GetCommandLineArgs()[0] changes along with the actual
executable filename (of course). Coincidentally, the second method
resulted better for my specific situation as I want the data folder to
be named as the actual executable filename. – Hatoru Hansou Jan 23 '14
at 2:25
Hatoru's comment seems to support Phil1970's comment, that GetExecutingAssembly() probably use the resource properties found in the assembly instead of the actual file name.
So I'd use
Process.GetCurrentProcess().ProcessName
as Contango suggested.
And as zerkms said, don't ignore exceptions.

How to get Current Project Directory path using C#

okay, here is the question. I have two projects one is C# Console and other is Class library.
I am accessing/calling Class library method from the console app.
There is a folder called Files within the class library project.
I need to get the path of the Class library's files folder but whenever I use
System.IO.Directory.GetCurrentDirectory();
and
Environment.CurrentDirectory;
it is giving me path of the Console project which I am using to call the method.
Above methods are giving me path like
C:\\ConsolePro\\bin\\Debug
but I need the path of Class library project
C:\\ClassLibPro\\bin\\Debug
Please advise
Once the code is compiled and running, 'Project Path' has no meaning. All you can determine are the file locations of the compiled assemblies. And you can only do what you are asking if your Console project references the built 'class library' DLL directly, rather than via a Project Reference.
Then, you can make use of Reflection to get Assembly paths like;
string path = Assembly.GetAssembly(typeof (SomeClassInOtherProject)).Location;
You should be able to use Directory.GetParent(Directory.GetCurrentDirectory()) a few times to get higher level directories and then add the path of the lib directory to the end of that.
I believe the problem is:
Since the Console project has the DLL file reference it is using DLL to call any methods.
At this time it is returning the class library projct's DLL location which is located in console project's bin directory and it doesn't know about the physical location of class library project.
so essentially it is returning the same project path. I will have to move both projects in same directory in order to solve this issue.
If you loading the class library from another assembly.
string Path = System.Reflection.Assembly.GetAssembly(typeof({LibraryClassName})).Location;
string PathToClassLibPro = Path.GetDirectoryName( Path);
Replace {LibraryClassName} with the class name of your library.
I hope I understand u corretly:
Path.GetDirectoryName(typeof(Foo.MyFooClass).Assembly.Location);
I would recommend one of two options.
If the files are small include them in the class library and stream them to a temp location when needed
Other option is to copy the files during the build to the output directory and use them that way. In cases of multiple shared projects it is best to have a common bin folder that you copy assemblies to and run from that location.
Despite i cant find a good solution i use this trick :
as long as you want to come back to your ideal path u should add Directory.GetParent() instead of ...
Directory.GetParent(...(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()...).ToString()
I use the following approach to get the current project path at runtime:
public static class ProjectInfo {
public static string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
public static string projectPath = appDirectory.Substring(0, appDirectory.IndexOf("\\bin"));
}
I had this exact issue as well where I couldn't access the file in my namespace's bin/debug folder. My solution was to manipulate the string using Split() then construct a new string which is the absolute path to the json file I have in my namespace.
private static string GetFilePath()
{
const char Escape = '\\'; //can't have '\' by itself, it'll throw the "Newline in constant" error
string directory = Environment.CurrentDirectory;
string[] pathOccurences = directory.Split(Escape);
string pathToReturn = pathOccurences[0] + Escape; //prevents index out of bounds in upcoming loop
for(int i = 1; i < pathOccurences.Length; i++)
{
if (pathOccurences[i] != pathOccurences[i - 1]) //the project file name and the namespace file name are the same
pathToReturn += pathOccurences[i] + Escape;
else
pathToReturn += typeof(thisClass).Namespace + Escape; //In the one occurrence of the duplicate substring, I replace it with my class Namespace name
}
return pathToReturn + "yourFile.json";
}
I personally don't like this solution, but it was the only answer I could think of.

Categories