I have a web project like:
namespace Web
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lbResult.Text = PathTest.GetBasePath();
}
}
}
The method PathTest.GetBasePath() is defined in another Project like:
namespace TestProject
{
public class PathTest
{
public static string GetBasePath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
}
}
Why it's display ...\Web\ while the TestProject assembly is compiled into bin folder(in other words it should display ...\Web\bin in my thought).
Now I got a troublesome if I modified method into:
namespace TestProject
{
public class FileReader
{
private const string m_filePath = #"\File.config";
public static string Read()
{
FileStream fs = null;
fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + m_filePath,FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
return reader.ReadToEnd();
}
}
}
The File.config is created in TestProject. Now AppDomain.CurrentDomain.BaseDirectory + m_filePath will returen ..\Web\File.config (actually the file was be copied into ..\Web\bin\File.config), an exception will be thrown.
You could say that I should modified m_filePath to #"\bin\File.config". However If I use this method in a Console app in your suggest, AppDomain.CurrentDomain.BaseDirectory + m_filePath will return ..\Console\bin\Debug\bin\File.config (actually the file was copyed into .\Console\bin\Debug\File.config), an exception will be thrown due to surplus bin.
In other words, in web app, AppDomain.CurrentDomain.BaseDirectory is a different path where file be copyed into (lack of /bin), but in console app it's the same one path.
Any one can help me?
Per MSDN, an App Domain "Represents an application domain, which is an isolated environment where applications execute." When you think about an ASP.Net application the root where the app resides is not the bin folder. It is totally possible, and in some cases reasonable, to have no files in your bin folder, and possibly no bin folder at all. Since AppDomain.CurrentDomain refers to the same object regardless of whether you call the code from code behind or from a dll in the bin folder you will end up with the root path to the web site.
When I've written code designed to run under both asp.net and windows apps usually I create a property that looks something like this:
public static string GetBasePath()
{
if(System.Web.HttpContext.Current == null) return AppDomain.CurrentDomain.BaseDirectory;
else return Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"bin");
}
Another (untested) option would be to use:
public static string GetBasePath()
{
return System.Reflection.Assembly.GetExecutingAssembly().Location;
}
In case you want a solution that works for WinForms and Web Apps:
public string ApplicationPath
{
get
{
if (String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
{
//exe folder for WinForms, Consoles, Windows Services
return AppDomain.CurrentDomain.BaseDirectory;
}
else
{
//bin folder for Web Apps
return AppDomain.CurrentDomain.RelativeSearchPath;
}
}
}
The above code snippet is for binaries locations.
The AppDomain.CurrentDomain.BaseDirectory is still a valid path for Web Apps, it's just the root folder where the web.config and Global.asax are, and is same as Server.MapPath(#"~\");
If you use AppDomain.CurrentDomain.SetupInformation.PrivateBinPath instead of BaseDirectory, then you should get the correct path.
When ASP.net builds your site it outputs build assemblies in its special place for them. So getting path in that way is strange.
For asp.net hosted applications you can use:
string path = HttpContext.Current.Server.MapPath("~/App_Data/somedata.xml");
Related
I'm having trouble getting my log4net.config file to load when using Visual Studio in debug mode for an Excel VSTO Plugin. The config file is in the top level directory of my project. I have the property "Copy to Output Directory" set to "Copy Always". This ensures the file is copied to bin/Debug/log4net.config. I can verify this is the case when I build.
However, the file won't load when I run in Debug mode. I gave up on trying to get the file to load automatically and decided to do it by code, as per the OP's code at the bottom of this question.
However, I realised that I needed to use an absolute path to the config file, as relative paths weren't picking it up. On further investigation, I realised that the executing DLL wasn't actually the DLL in the debug/bin folder. It was in the following location:
C:\Users\cbhandal\AppData\Local\assembly\dl3\MO52QQWP.9ZL\K36XZHGN.1PB\230751e6\d09b7fb2_19f6d401
Also the current working directory, as found by System.IO.Directory.GetCurrentDirectory(); was set to "C:\\Users\\cbhandal\\Documents".
Hard-coding the path as an absolute path works as in the following code:
var log4netConfig = "C:\\" + path + "\\Log4net.config";
var log4netInfo = new FileInfo(log4netConfig);
log4net.Config.XmlConfigurator.ConfigureAndWatch(log4netInfo);
But that's not a solution I can deploy. I'm stuck here. Wondering if there's a way to either force Visual studio to copy the .config file to that appdata/temp location, or if there's a way to programatically reference the folder where the original DLL lay- the one that was built. Or if anyone had any other solution?
For me the easiest solution was to use this:
https://stackoverflow.com/a/6963420/4754981
But there are several other solutions on that link for different approaches, each with their caveats.
So mine looks like this:
using System.Reflection;
using System.IO;
using System;
public static class Extensions {
private static string GetDirectory(this Assembly a) {
string codeBase = a.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
private static void AlterLogPath(this log4net.Repository.ILoggerRepository repo, string newPath, string directory="") {
log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy) repo;
foreach (log4net.Appender.IAppender a in h.Root.Appenders) {
if (a is log4net.Appender.FileAppender) {
var fa = (log4net.Appender.FileAppender)a;
var fileName = Path.GetFileName(fa.File);
fa.File = newPath + (String.IsNullOrEmpty(directory)?"":(directory + Path.DirectorySeparatorChar.ToString())); // edit: filename is attached after next line automatically.
fa.ActivateOptions();
break;
}
}
}
}
and in the bootup (via [assembly: System.Web.PreApplicationStartMethod] or otherwise for asp), or main app..
static void Main() {
var PATH = Assembly.GetExecutingAssembly().GetDirectory() + Path.DirectorySeparatorChar.ToString();
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(PATH + "log4net.config"));
log4net.LogManager.GetRepository().AlterLogPath(PATH, "Logs");
}
This question already has answers here:
How do I read a local file in my ASP.NET Core project?
(2 answers)
Closed 4 years ago.
In my solution I have a file called beep.png directly in the root, right next to Startup.cs file. I changed its properties to always copy. I activated UseFileServer and opted in to browse the directory structure to be sure.
However, when I run the code Image.FromFile("beep.png");, I only get the error that the file isn't found.
System.IO.FileNotFoundException
Message=C:\Program Files\IIS Express\beep.png
How can I enable the file to be accessible?
Use IWebHostEnvirounment to get the root content path (technicaly the project folder), or to get the web root path (the wwwroot folder under project folder).
_hostingEnvirounment.ContentRootPath will return:
D:\Hosting\ProjectFolder
_hostingEnvirounment.WebRootPath will rturn:
D:\Hosting\ProjectFolder\wwwroot
So in your case; inject IWebHostEnvirounment to your controller then get the content root folder as below:
public class MyApiController : ControllerBase {
private readonly IWebHostEnvirounment _hostingEnvirounment;
public MyApiController(IWebHostEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
// get image from project root folder \ProjectFolder\
public Image GetImageFromContentRoot(string name) {
// e.g.: imgPath = "D:\\Hosting\\ProjectFolder\\beep.png"
var imgPath = Path.Combine(_hostingEnvirounment.ContentRootPath, name);
return Image.FromFile(imgPath);
}
//get image from projects wwwroot folder
public Image GetImageFromWebRoot(string name) {
// e.g.: imgPath = "D:\\Hosting\\ProjectFolder\\wwwroot\\beep.png"
var imgPath = Path.Combine(_hostingEnvirounment.WebRootPath, name);
return Image.FromFile(imgPath);
}
}
I have problem with my C# app, when is opened via file association, it works in file directory. For example, when I create copy of opened file:
File.Copy("C:\Photo\car.jpg", ".\car2.jpg"); // this is only ilustration code.
It makes new file "C:\Photo\car2.jpg", but I want to make file in my app directory (".\car2.jpg").
So, I think, when app is opened via file association, it run with working folder of that file ("C:\Photo\"). Is there way, how to keep working directory as directory with app.exe?
Edit:
This is not solution, I need to get equals of ".\" and System.AppDomain.CurrentDomain.BaseDirectory:
File.Copy("C:\Photo\car.jpg", Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "car2.jpg"));
I have to use this on many places in application, solution can be sets:
Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
but I prefer set it in startup application via file association and not in running program - it looks cleaner.
Thanks,
Jakub
To get the path of your application, you can use:
System.AppDomain.CurrentDomain.BaseDirectory
Use Path.Combine to build the destination path as follows:
File.Copy("C:\Photo\car.jpg", Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "car2.jpg"));
I'd like to try and offer an alternative. I'm relatively new to this, but I figured out a solution that works for me:
Make 2 static variables in your MainWindow.xaml.cs:
public static string fileOpen;
public static string workingDirectory;
In your app.xaml.cs file, add the following code:
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Count() > 0)
{
var a = File.Exists(e.Args[0]);
var path = Path.GetFullPath(e.Args[0]);
MainICPUI.workingDirectory = Directory.GetCurrentDirectory();
MainICPUI.fileOpen = e.Args[0];
}
base.OnStartup(e);
}
When you open a file associated with your program from any directory, the full file name is added to the StartupEventArgs, including the directory. This code saves the directory.
Back to your MainWindow.xaml.cs file:
public static string fileOpen;
public static string workingDirectory;
public MainWindow()
{
InitializeComponent();
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
// This sets the directory back to the program directory, so you can do what you need
// to with files associated with your program
}
// Make sure your MainWindow has an OnLoaded event assigned to:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
// Now I'm setting the working directory back to the file location
Directory.SetCurrentDirectory(workingDirectory);
if (File.Exists(fileOpen))
{
var path = Path.GetFullPath(fileOpen);
// This should be the full file path of the file you clicked on.
}
}
I have the following code (sample1.evol - file attached to my unit test project):
[Test]
public void LexicalTest1()
{
var codePath = Path.GetFullPath(#"\EvolutionSamples\sample1.evol");
//.....
}
I found that the working directory of test execution is not the assembly directory: (in my case codepath variable assigned to d:\EvolutionSamples\sample1.evol).
So, how can I change the execution working directory (without hardcode)? What will be the best practice to load any files attached to test case?
You can use following to get the directory of assembly running the code something like
var AssemblyDirectory = TestContext.CurrentContext.TestDirectory
I use this for integration tests that need to access data files.
On any machine the test needs to run create a system environment variable named TestDataDirectory that points to the root of where your test data is.
Then have a static method that gets the file path for you..
public static class TestHelper
{
const string EnvironmentVariable = "TestDataDirectory";
static string testDataDir = Environment.GetEnvironmentVariable(EnvironmentVariable);
public static string GetTestFile(string partialPath)
{
return Path.Combine(testDataDir, partialPath);
}
}
...
[Test]
public void LexicalTest1()
{
var codePath = TestHelper.GetTestFile(#"\EvolutionSamples\sample1.evol");
//.....
}
I am using this code:
var str = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
if (str.StartsWith(#"file:\")){
str = str.Substring(6);
}
Getting in str variable the assembly directory.
We were having a problem where tests run using ReSharper and NCrunch would work, but the native VS Test Runner would not be able to find the files, when given just a relative file path for the test to use. I solved it by creating a function that you pass the relative test file path into, and it will give you the absolute file path.
private static string _basePath = Path.GetDirectoryName(typeof(NameOfYourTestClassGoesHere).Assembly.Location);
private string GetAbsoluteTestFilePath(string relativePath) => Path.Combine(_basePath, relativePath);
You would then use the function like so:
var input = File.ReadAllLines(GetAbsoluteTestFilePath(#"TestData/YourTestDataFile.txt"));
Background : Merging dlls into a single .exe with wpf
How shall i merge a .dll reference into the .exe file, i read the above post, got principle behind it, but i am not able to figure out how to do it?( i am newbie, sorry)
The reference file is HtmlagilityPack.dll
Currently my App.xaml.cs contains :
public partial class App : Application
{
public App(){
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
// proceed starting app...
}
static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
//We dont' care about System Assembies and so on...
if (!args.Name.ToLower().StartsWith("Html")) return null;
Assembly thisAssembly = Assembly.GetExecutingAssembly();
//Get the Name of the AssemblyFile
var name = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
//Load form Embedded Resources - This Function is not called if the Assembly is in the Application Folder
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(name));
if (resources.Count() > 0)
{
var resourceName = resources.First();
using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
}
return null;
}
}
Where else am i supposed to make changes?, i have being trying past an hour with an example of http://blog.mahop.net/post/Merge-WPF-Assemblies.aspx But not able to figure out how to do it with HtmlAgilityPack.
Okay, finally had to use the SmartAssembly program.
But still looking for a solution to do it by code.
Your code looks slightly off, it should look more like this:
public class App : Application
{
[STAThreadAttribute()]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
// etc...
}
// etc...
You then also need to change the "Startup object" setting in the properties page of your project to use the App class (i.e. the above code) - you should then see the Main method of this class being the first code executed when you start debugging.