繁体   English   中英

创建自定义MSBuild任务时如何从C#代码获取当前项目目录?

[英]How do you get the current project directory from C# code when creating a custom MSBuild task?

我不想运行带有硬编码路径的外部程序,而是想获取当前的项目目录。 我正在使用自定义任务中的进程调用外部程序。

我该怎么做? AppDomain.CurrentDomain.BaseDirectory 只是给我 VS 2008 的位置。

using System;
using System.IO;

// This will get the current WORKING directory (i.e. \bin\Debug)
string workingDirectory = Environment.CurrentDirectory;
// or: Directory.GetCurrentDirectory() gives the same result

// This will get the current PROJECT bin directory (ie ../bin/)
string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;

// This will get the current PROJECT directory
string projectDirectory = Directory.GetParent(workingDirectory).Parent.Parent.FullName;

您可以尝试这两种方法之一。

string startupPath = System.IO.Directory.GetCurrentDirectory();

string startupPath = Environment.CurrentDirectory;

告诉我,你觉得哪个更好

如果项目在 IIS Express 上运行,则Environment.CurrentDirectory可能指向 IIS Express 所在的位置(默认路径为C:\Program Files (x86)\IIS Express ),而不是项目所在的位置。


这可能是各种项目最合适的目录路径。

AppDomain.CurrentDomain.BaseDirectory

这是 MSDN 的定义。

获取程序集解析器用于探测程序集的基目录。

到目前为止,我对所有发布的解决方案的随意性感到惊讶,简直目瞪口呆。

获取 C# 项目的根文件夹的正确方法之一是利用[CallerFilePath]属性获取源文件的完整路径名,然后从中减去文件名和扩展名,留下项目的路径.

以下是实际操作方法:

在项目的根文件夹中,添加具有以下内容的文件ProjectSourcePath.cs

internal static class ProjectSourcePath
{
    private const  string  myRelativePath = nameof(ProjectSourcePath) + ".cs";
    private static string? lazyValue;
    public  static string  Value => lazyValue ??= calculatePath();

    private static string calculatePath()
    {
        string pathName = GetSourceFilePathName();
        Assert( pathName.EndsWith( myRelativePath, StringComparison.Ordinal ) );
        return pathName.Substring( 0, pathName.Length - myRelativePath.Length );
    }
}

string? 需要带有#nullable enable的相当晚的 C# 版本; 如果你没有它,那么只需删除? .

Assert()函数是我自己的; 如果你喜欢危险地生活,你可以用你自己的代替它,或者省略它。

函数GetSourceFilePathName()定义如下:

using System.Runtime.CompilerServices

    public static string GetSourceFilePathName( [CallerFilePath] string? callerFilePath = null ) //
        => callerFilePath ?? "";

有了以上内容后,您可以按如下方式使用它:

string projectSourcePath = ProjectSourcePath.Value;

1 “正确”如:万无一失; 万无一失; 没有假设; 不被鞋带绑在一起; 不一定要为某些项目工作,但不能为其他项目工作; 当您更改不相关的内容时,不太可能在没有警告的情况下可怕地中断; 等等

这还将通过从当前执行目录向上导航两个级别来为您提供项目目录(这不会为每个构建返回项目目录,但这是最常见的)。

System.IO.Path.GetFullPath(@"..\..\")

当然,您希望将其包含在某种验证/错误处理逻辑中。

如果你想知道你的解决方案所在的目录是什么,你需要这样做:

 var parent = Directory.GetParent(Directory.GetCurrentDirectory()).Parent;
            if (parent != null)
            {
                var directoryInfo = parent.Parent;
                string startDirectory = null;
                if (directoryInfo != null)
                {
                    startDirectory = directoryInfo.FullName;
                }
                if (startDirectory != null)
                { /*Do whatever you want "startDirectory" variable*/}
            }

如果您只使用GetCurrrentDirectory()方法,无论您是在调试还是发布,您都会获得构建文件夹。 我希望这有帮助! 如果您忘记了验证,它将是这样的:

var startDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName;

根据Gucu112 的回答,但对于 .NET Core Console/Window 应用程序,它应该是:

string projectDir = 
    Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\.."));

我在 .NET Core Window 应用程序的 xUnit 项目中使用它。

这个解决方案对我很有效,在开发服务器上以及通过 C# 使用 ASP.NET MVC5的 TEST 和 PROD 服务器上:

var projectDir = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);

如果您需要项目配置文件中的项目目录,请使用:

$(ProjectDir)
using System;
using System.IO;

// Get the current directory and make it a DirectoryInfo object.
// Do not use Environment.CurrentDirectory, vistual studio 
// and visual studio code will return different result:
// Visual studio will return @"projectDir\bin\Release\netcoreapp2.0\", yet 
// vs code will return @"projectDir\"
var currentDirectory = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);

// On windows, the current directory is the compiled binary sits,
// so string like @"bin\Release\netcoreapp2.0\" will follow the project directory. 
// Hense, the project directory is the great grand-father of the current directory.
string projectDirectory = currentDirectory.Parent.Parent.Parent.FullName;

我也在找这个。 我有一个运行 HWC 的项目,我想将网站保留在应用程序树之外,但我不想将其保留在调试(或发布)目录中。 FWIW,公认的解决方案(以及这个解决方案)仅标识可执行文件正在运行的目录。

为了找到那个目录,我一直在使用

string startupPath = System.IO.Path.GetFullPath(".\\").

另一种方法来做到这一点

string startupPath = System.IO.Directory.GetParent(@"./").FullName;

如果要获取 bin 文件夹的路径

string startupPath = System.IO.Directory.GetParent(@"../").FullName;

也许有更好的方法=)

另一个不完美的解决方案(但可能比其他一些更接近完美):

    protected static string GetSolutionFSPath() {
        return System.IO.Directory.GetParent(System.IO.Directory.GetCurrentDirectory()).Parent.Parent.FullName;
    }
    protected static string GetProjectFSPath() {
        return String.Format("{0}\\{1}", GetSolutionFSPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
    }

即使当前项目不是解决方案的Startup Project ,此版本也会返回当前项目的文件夹。

第一个缺陷是我跳过了所有的错误检查。 这可以很容易地解决,但只有当您将项目存储在驱动器的根目录中或在路径中使用结点(并且该结点是解决方案文件夹的后代)时才会出现问题,因此这种情况不太可能出现. 我不完全确定 Visual Studio 是否可以处理这些设置中的任何一个。

您可能遇到的另一个(更有可能)问题是项目名称必须与项目的文件夹名称匹配才能找到它。

您可能遇到的另一个问题是项目必须位于解决方案文件夹中。 这通常不是问题,但如果您使用“ Add Existing Project to Solution ”选项将项目添加到解决方案,那么这可能不是您的解决方案的组织方式。

最后,如果您的应用程序将修改工作目录,您应该在这样做之前存储此值,因为此值是相对于当前工作目录确定的。

当然,这也意味着您不能在项目属性对话框中更改项目的Build -> Output pathDebug -> Working directory选项的默认值。

试试这个,很简单

HttpContext.Current.Server.MapPath("~/FolderName/");

如果您真的想确保获得源项目目录,则无论 bin 输出路径设置为:

  1. 添加预构建事件命令行(Visual Studio:项目属性 -> 构建事件):

    echo $(MSBuildProjectDirectory) > $(MSBuildProjectDirectory)\Resources\ProjectDirectory.txt

  2. ProjectDirectory.txt文件添加到项目的 Resources.resx 中(如果还不存在,请右键单击项目 -> 添加新项目 -> 资源文件)

  3. 使用Resources.ProjectDirectory从代码访问。

我也遇到过类似的情况,在谷歌无果之后,我声明了一个公共字符串,它修改了调试/发布路径的字符串值来获取项目路径。 使用此方法的一个好处是,由于它使用当前项目的目录,因此无论您是从调试目录还是发布目录工作都无关紧要:

public string DirProject()
{
    string DirDebug = System.IO.Directory.GetCurrentDirectory();
    string DirProject = DirDebug;

    for (int counter_slash = 0; counter_slash < 4; counter_slash++)
    {
        DirProject = DirProject.Substring(0, DirProject.LastIndexOf(@"\"));
    }

    return DirProject;
}

然后,您可以随时调用它,只需使用一行:

string MyProjectDir = DirProject();

这在大多数情况下应该有效。

使用它来获取项目目录(为我工作):

string projectPath = 
    Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;

我使用以下解决方案来完成工作:

string projectDir =
    Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\.."));

尝试:

var pathRegex = new Regex(@"\\bin(\\x86|\\x64)?\\(Debug|Release)$", RegexOptions.Compiled);
var directory = pathRegex.Replace(Directory.GetCurrentDirectory(), String.Empty);

这是与其他解决方案不同的解决方案,它也考虑了可能的 x86 或 x64 构建。

在我终于完成了关于公共字符串的我们的第一个答案以得出答案之后,我突然意识到您可能可以从注册表中读取一个值以获得您想要的结果。 事实证明,这条路线甚至更短:

首先,您必须包含 Microsoft.Win32 命名空间,以便您可以使用注册表:

using Microsoft.Win32;    // required for reading and / or writing the registry

这是主要代码:

RegistryKey Projects_Key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\9.0", false);
string DirProject = (string)Projects_Key.GetValue(@"DefaultNewProjectLocation");

关于这个答案的注释:

我正在使用 Visual Studio 2008 专业版。 如果您使用的是其他版本(即 2003、2005、2010 等),那么您可能不必修改 SubKey 字符串的“版本”部分(即 8.0、7.0 等)。

如果您使用我的答案之一,并且问的不是太多,那么我想知道您使用了我的哪种方法以及为什么。 祝你好运。

  • 分米
string projPath = Path.GetFullPath(@"..\..\..\");
Console.WriteLine(projPath);

这对我来说一直很有效。 搏一搏。

(因为 22 个答案还不够……这里还有一个……)

Mike Nakis 发布了一个很好的答案,我添加了一些增强功能。 这只是他非常好的代码的一个稍微改进的版本。

正如 Mike 指出的,这个类文件必须在项目的根目录中。

我没有遇到以下任何问题,但也许有一些我不知道的细微差别。 YMMV。

using System.IO;
using System.Runtime.CompilerServices;

namespace Whatever
{
  internal static class ProjectPathInfo
  {
    public static string CSharpClassFileName = nameof(ProjectPathInfo) + ".cs";
    public static string CSharpClassPath;
    public static string ProjectPath;
    public static string SolutionPath;

    static ProjectPathInfo() {
      CSharpClassPath = GetSourceFilePathName();
      ProjectPath = Directory.GetParent(CSharpClassPath)!.FullName;
      SolutionPath = Directory.GetParent(ProjectPath)!.FullName;
    }

    private static string GetSourceFilePathName( [CallerFilePath] string? callerFilePath = null ) => callerFilePath ?? "";
  }
}

好的,2021 年,派对有点晚了……但我对我在许多项目中发现的所有可能性感到非常恼火:

  • 垃圾箱/调试
  • bin/x86/调试
  • bin/Debug/net5.0-windows
  • ...

来吧...我只需要一个(或几乎)一行来解决测试单元中的一些文件; 我需要在所有过去、现在、(也许将来)的项目中使用它。

因此,如果项目名称与其所在的相对文件夹相同

  1. 使用程序集名称选择项目根文件夹名称;
  2. 返回直到找到该名称。

代码示例:

string appName = Assembly.GetExecutingAssembly().GetName().Name;
var dir = new DirectoryInfo(Environment.CurrentDirectory);
while (dir.Name != appName) {
  dir = Directory.GetParent(dir.FullName);
}
return dir.FullName;

最佳解决方案

string PjFolder1 =
    Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).
        Parent.Parent.FullName;

其他解决方案

string pjFolder2 = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(
                System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)));

测试它,AppDomain.CurrentDomain.BaseDirectory 在过去的项目中为我工作,现在我得到调试文件夹....所选的好答案只是不工作!。

//Project DEBUG folder, but STILL PROJECT FOLDER
string pjDebugFolder = AppDomain.CurrentDomain.BaseDirectory;

//Visual studio folder, NOT PROJECT FOLDER
//This solutions just not work
string vsFolder = Directory.GetCurrentDirectory();
string vsFolder2 = Environment.CurrentDirectory;
string vsFolder3 = Path.GetFullPath(".\\");   

//Current PROJECT FOLDER
string ProjectFolder = 
    //Get Debug Folder object from BaseDirectory ( the same with end slash)
    Directory.GetParent(pjDebugFolder).
    Parent.//Bin Folder object
    Parent. //Project Folder object
    FullName;//Project Folder complete path

这适用于带有 SDK Core MSBuild 配置的 VS2017。

您需要在 EnvDTE / EnvDTE80 包中进行 NuGet。

不要使用 COM 或互操作。 什么……垃圾!!

 internal class Program {
    private static readonly DTE2 _dte2;

    // Static Constructor
    static Program() {
      _dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.15.0");
    }


    private static void FindProjectsIn(ProjectItem item, List<Project> results) {
      if (item.Object is Project) {
        var proj = (Project) item.Object;
        if (new Guid(proj.Kind) != new Guid(Constants.vsProjectItemKindPhysicalFolder))
          results.Add((Project) item.Object);
        else
          foreach (ProjectItem innerItem in proj.ProjectItems)
            FindProjectsIn(innerItem, results);
      }

      if (item.ProjectItems != null)
        foreach (ProjectItem innerItem in item.ProjectItems)
          FindProjectsIn(innerItem, results);
    }


    private static void FindProjectsIn(UIHierarchyItem item, List<Project> results) {
      if (item.Object is Project) {
        var proj = (Project) item.Object;
        if (new Guid(proj.Kind) != new Guid(Constants.vsProjectItemKindPhysicalFolder))
          results.Add((Project) item.Object);
        else
          foreach (ProjectItem innerItem in proj.ProjectItems)
            FindProjectsIn(innerItem, results);
      }

      foreach (UIHierarchyItem innerItem in item.UIHierarchyItems)
        FindProjectsIn(innerItem, results);
    }


    private static IEnumerable<Project> GetEnvDTEProjectsInSolution() {
      var ret = new List<Project>();
      var hierarchy = _dte2.ToolWindows.SolutionExplorer;
      foreach (UIHierarchyItem innerItem in hierarchy.UIHierarchyItems)
        FindProjectsIn(innerItem, ret);
      return ret;
    }


    private static void Main() {
      var projects = GetEnvDTEProjectsInSolution();
      var solutiondir = Path.GetDirectoryName(_dte2.Solution.FullName);

      // TODO
      ...

      var project = projects.FirstOrDefault(p => p.Name == <current project>);
      Console.WriteLine(project.FullName);
    }
  }

我没有看到使用 string.Join 和 string.Split + SkipLast 4 个元素的解决方案,所以在这里。

            string projectDir = 
            string.Join('/', AppDomain.CurrentDomain.BaseDirectory
                .Split(new char[] { '/' })
                .SkipLast(4));

我想运行当前的Project Dir,而不是运行路径经过硬编码的外部程序。 我正在使用自定义任务中的进程来调用外部程序。

我该怎么做? AppDomain.CurrentDomain.BaseDirectory只是给了我VS 2008的位置。

尝试:

            {
                OpenFileDialog fd = new OpenFileDialog();
                fd.Multiselect = false;
                fd.Filter = "Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*";
                if (fd.ShowDialog() == true)
                {
                    if (fd.CheckFileExists)
                    {
                        var fileNameToSave = GetTimestamp(DateTime.Now) + Path.GetExtension(fd.FileName);
                        var pathRegex = new Regex(@"\\bin(\\x86|\\x64)?\\(Debug|Release)$", RegexOptions.Compiled);
                        var directory = pathRegex.Replace(Directory.GetCurrentDirectory(), String.Empty);
                        var imagePath = Path.Combine(directory + @"\Uploads\" + fileNameToSave);
                        File.Copy(fd.FileName, imagePath);

                    }
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }

这是将图像上传到 wpf 上传目录的代码

Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.Parent.FullName

会给你项目目录。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM