VS 项目可执行文件的最终位置
Final location of VS project executables
我正在编写一个 Visual Studio 扩展,我需要知道最终用户项目的每个项目的可执行文件的最终位置。假设我专门针对 C# 桌面应用程序,因此它们应该使用 MSBuild。这通常相当简单,但一些最终用户项目可能非常复杂。简单的答案是为每个项目查询 DTE
并获得它们的 OutputPath
。有时,事情并不是那么简单。这是一个不起作用的示例:
一些解决方案包含三个项目:Main
、Plugin1
和Plugin2
。
Main
项目使用标准输出路径。
Plugin1
和 Plugin2
项目在构建后通过各自的 Copy AfterBuild
指令复制到 plugins
文件夹项目文件。
用户 运行 启动了 Main
项目并在 运行 时告诉它他们需要哪个插件。
Main
项目使用该信息动态加载所选插件。
请注意,这意味着选定的 DLL 不会在 Main
可执行文件中显示为引用。如果是,我可以找到一种方法来检索该信息,但它们是动态计算的。我需要在执行前知道这些信息。
我遇到的主要问题是我没有可靠的方法来检索插件的 "final output path"(项目文件中 AfterBuild
指令的结果),而且这是我真正需要知道的。不幸的是,我不能只更改项目文件,因为此扩展需要尽可能多地使用 VS 解决方案。
更新:我已经尝试使用 MSBuild API,使用自定义记录器和 FileWrites
变量的组合,但我找不到提取此信息的方法。不幸的是,FileWrites
不包含 Copy
操作的结果。除非有人提出更好的解决方案,否则我将只抓取 "match" 目标(大小、时间戳、内容等)的所有文件的解决方案树。诚然这是一种 hack,但我没有看到更好的方法。
据我所知,您有以下选择:
您可以将 dll 放入 vsix 文件 see here,这是一个 zip 文件。您可以从您的入口点(即 main 方法)使用反射,然后您可以找到您的主要方法可执行位置(即 exe、dll 等)。如果它显示在 vsix 路径中,那么您就知道您的 vsix 位置并可以加载与该位置相关的其他 dll。
您可以根据需要下载 dll 来解决您的问题 see here with a framework like MEF or MAF see here。这样你就不需要知道位置了。您还可以选择将下载的 dll 单独存储或作为资源存储在 dll 的一部分中等(您的选择)
对于这两个选项,您最终会在新的 dll 中创建接口,这些接口将被插件和您的主项目引用。这将使您能够确定不同的项目。没有什么是接口不能实际表示的,因为你甚至可以把函数调用放回那里。
不妨结束这个问题。从来没有找到一个优雅的答案,所以我会勾勒出我的蛮力方法。第一种方法递归地 returns 给定路径中的所有可执行文件(DLL 和 EXE):
IEnumerable<FileInfo> FindBinaries(string path)
{
var dirInfo = new DirectoryInfo(path);
var exeFiles = dirInfo.EnumerateFiles("*.exe", SearchOption.AllDirectories);
var dllFiles = dirInfo.EnumerateFiles("*.dll", SearchOption.AllDirectories);
return exeFiles.Concat(dllFiles);
}
这里有一个计算校验和的方法:
string GetChecksum(string filename)
{
string checksum = null;
using (var md5 = System.Security.Cryptography.MD5.Create())
{
using (var stream = File.OpenRead(filename))
{
checksum = BitConverter.ToString(md5.ComputeHash(stream));
}
}
return checksum;
}
这是执行工作的方法:
IEnumerable<string> FindMatchingBinaries(string path, string filename)
{
var checksum = GetChecksum(filename);
return FindBinaries(path).Select(p => p.FullName).Where(name => GetChecksum(name) == checksum);
}
然后您可以使用适当的参数调用它,如下所示:
var matchingFiles = FindMatchingBinaries(solutionRoot, projectBinaryName);
当然,您需要在其他地方确定解决方案根目录和项目二进制文件是什么。它们有点超出了问题的范围,对于这些特定问题还有很多其他答案。
我正在编写一个 Visual Studio 扩展,我需要知道最终用户项目的每个项目的可执行文件的最终位置。假设我专门针对 C# 桌面应用程序,因此它们应该使用 MSBuild。这通常相当简单,但一些最终用户项目可能非常复杂。简单的答案是为每个项目查询 DTE
并获得它们的 OutputPath
。有时,事情并不是那么简单。这是一个不起作用的示例:
一些解决方案包含三个项目:
Main
、Plugin1
和Plugin2
。Main
项目使用标准输出路径。Plugin1
和Plugin2
项目在构建后通过各自的Copy AfterBuild
指令复制到plugins
文件夹项目文件。用户 运行 启动了
Main
项目并在 运行 时告诉它他们需要哪个插件。Main
项目使用该信息动态加载所选插件。
请注意,这意味着选定的 DLL 不会在 Main
可执行文件中显示为引用。如果是,我可以找到一种方法来检索该信息,但它们是动态计算的。我需要在执行前知道这些信息。
我遇到的主要问题是我没有可靠的方法来检索插件的 "final output path"(项目文件中 AfterBuild
指令的结果),而且这是我真正需要知道的。不幸的是,我不能只更改项目文件,因为此扩展需要尽可能多地使用 VS 解决方案。
更新:我已经尝试使用 MSBuild API,使用自定义记录器和 FileWrites
变量的组合,但我找不到提取此信息的方法。不幸的是,FileWrites
不包含 Copy
操作的结果。除非有人提出更好的解决方案,否则我将只抓取 "match" 目标(大小、时间戳、内容等)的所有文件的解决方案树。诚然这是一种 hack,但我没有看到更好的方法。
据我所知,您有以下选择:
您可以将 dll 放入 vsix 文件 see here,这是一个 zip 文件。您可以从您的入口点(即 main 方法)使用反射,然后您可以找到您的主要方法可执行位置(即 exe、dll 等)。如果它显示在 vsix 路径中,那么您就知道您的 vsix 位置并可以加载与该位置相关的其他 dll。
您可以根据需要下载 dll 来解决您的问题 see here with a framework like MEF or MAF see here。这样你就不需要知道位置了。您还可以选择将下载的 dll 单独存储或作为资源存储在 dll 的一部分中等(您的选择)
对于这两个选项,您最终会在新的 dll 中创建接口,这些接口将被插件和您的主项目引用。这将使您能够确定不同的项目。没有什么是接口不能实际表示的,因为你甚至可以把函数调用放回那里。
不妨结束这个问题。从来没有找到一个优雅的答案,所以我会勾勒出我的蛮力方法。第一种方法递归地 returns 给定路径中的所有可执行文件(DLL 和 EXE):
IEnumerable<FileInfo> FindBinaries(string path)
{
var dirInfo = new DirectoryInfo(path);
var exeFiles = dirInfo.EnumerateFiles("*.exe", SearchOption.AllDirectories);
var dllFiles = dirInfo.EnumerateFiles("*.dll", SearchOption.AllDirectories);
return exeFiles.Concat(dllFiles);
}
这里有一个计算校验和的方法:
string GetChecksum(string filename)
{
string checksum = null;
using (var md5 = System.Security.Cryptography.MD5.Create())
{
using (var stream = File.OpenRead(filename))
{
checksum = BitConverter.ToString(md5.ComputeHash(stream));
}
}
return checksum;
}
这是执行工作的方法:
IEnumerable<string> FindMatchingBinaries(string path, string filename)
{
var checksum = GetChecksum(filename);
return FindBinaries(path).Select(p => p.FullName).Where(name => GetChecksum(name) == checksum);
}
然后您可以使用适当的参数调用它,如下所示:
var matchingFiles = FindMatchingBinaries(solutionRoot, projectBinaryName);
当然,您需要在其他地方确定解决方案根目录和项目二进制文件是什么。它们有点超出了问题的范围,对于这些特定问题还有很多其他答案。