Visual Studio 2010 DTE:如何使添加的 DLL 引用成为绝对引用而不是复制
Posted
技术标签:
【中文标题】Visual Studio 2010 DTE:如何使添加的 DLL 引用成为绝对引用而不是复制【英文标题】:Visual Studio 2010 DTE: How to make added DLL reference absolute and not copied 【发布时间】:2011-10-04 14:35:41 【问题描述】:总结:
当您添加特定 DLL 时,我们需要使用 DTE 复制“添加引用”对话框的行为(它向 CSProj 文件中的引用添加提示路径条目)。
**注意:我在此处发布了另一个相关但不重复的帖子:https://***.com/questions/6690655/visual-studio-2010-add-in-how-to-get-a-references-hint-path-property 请同时阅读该帖子以获取有关此问题的更多信息。我现在已经添加了一个体面的赏金来获得这个答案,并且很乐意对任何体面的答案进行投票:)*
到目前为止的故事:
我正在使用 DTE 以编程方式将项目引用转换为直接 DLL 引用。
假设我有一个简单的解决方案,其中有一个 Project2
(parent 项目),它引用了一个 Project1
(child 项目),我做出如下更改这个:
project1Reference = FindProjectReference(project2.References, project1);
project1Reference.Remove();
Reference dllReference = project2.References.Add(project1DllPath);
project1DllPath 指的是"c:\somewhere\Project1\Bin\Debug\Project1.dll"
文件。
我还不能解决的问题是新的参考不是
"c:\somewhere\Project1\Bin\Debug\Project1.dll"
而是指向
"c:\somewhere\Project2\Bin\Debug\Project1.dll"
(文件被复制到那里)。
如果我使用“添加引用”菜单直接/手动添加 DLL,它不会进行此复制。
如何将 DLL 引用添加到现有项目的 DLL,而不需要复制并引用它?
我尝试在 Add 之后添加 dllReference.CopyLocal = false;
,但除了设置标志之外,它没有任何区别。创建后似乎没有修改路径的选项。
更新:我还尝试以编程方式从 Project2 中删除对 Project1 的任何 Build 依赖,但这没有效果。
下面是csproj文件的区别:
作为一个项目:
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
<Project>86B3E118-2CD1-49E7-A180-C1346EC223B9</Project>
<Name>ClassLibrary1</Name>
</ProjectReference>
</ItemGroup>
作为 DLL 引用(路径完全丢失):
<ItemGroup>
<Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
...
</ItemGroup>
作为手动引用的 DLL:
<ItemGroup>
<Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\ClassLibrary1\bin\Debug\ClassLibrary1.dll</HintPath>
</Reference>
...
</ItemGroup>
看起来能够为 DLL 引用指定提示路径是关键。 如何在 DLL 引用上设置提示路径(假设您只有 Reference 属性的句柄)?
更多信息(2011 年 7 月 20 日):
下面Muse VSExtensions 的建议不会影响有问题的 DLL,因为已经从 DLL 的项目 BIN 复制到父项目的 BIN 文件夹。父项目不需要使用引用路径,因为它的输出文件夹中已经有子 DLL。
项目的Reference Paths
也被保存到project.csproj.user 文件而不是project.csproj 文件。
【问题讨论】:
【参考方案1】:我最近在 Visual Studio 2010 中添加 dll 引用时遇到问题,我无法让它添加 HintPath,这导致 TFS 构建出现问题。我注意到我几周前安装的 Productivity Power Tools 插件更改了添加参考对话框。我从“工具”菜单中关闭了插件的“可搜索添加引用对话框”,在重新启动 Visual Studio 2010 后,我再次感到高兴 - 我能够添加引用,这一次 HintPath 也在那里。
【讨论】:
【参考方案2】:是否必须仅使用 DTE 来解决这个问题?您可以使用 MSBuild 自动化来执行此操作...它具有可以解析 csproj 文件内容的类。
看看: Microsoft.Build.Evaluation Namespace, 有一些关于如何加载 csproj 文件以及如何更改它的有用信息。 另请参阅Project Class。
【讨论】:
这是在我们构建的 Visual Studio 插件中,以使超大型解决方案的本地开发能够以超快的速度构建。它已经将 DTE 用于大多数功能,因此将 MSBuild 添加到组合中并不是一个好的选择。无论如何感谢您的建议。【参考方案3】:我确信这是 VS 2010 中的一个新错误/功能,因为我有一个加载项在我从 VS 2008 迁移后几天前开始显示类似的行为......基本上,如果你添加对 VS 程序集搜索路径中的任何内容的引用,将在没有路径提示的情况下添加。
我设法找到了解决此问题的其他 VS 插件(Power Tools、NuGet 等),它们似乎都使用了 MsBuild。我不知道 MsBuild 是否会大大提高资源使用率 - 我自己并没有看到太大的减速,可能是因为 References.Add() 开始时非常慢。但请注意,要获取 MsBuild 项目的实例,使用了一个名为“GetLoadedProjects”的方法,这可能意味着它适用于内存中已经存在的数据。
下面是我用来修复我的外接程序的代码,是我在网上找到的简化版...本质上,想法是照常添加引用,然后使用MsBuild设置路径提示.设置提示是一项微不足道的操作,但要找到要添加提示的 MsBuild 项目项的实例却非常复杂。我试图破解一个只使用 MsBuild 的替代方案,但遇到了其他问题......这个似乎有效。
这里可能会感兴趣的另一件事:代码包含一种优化 - 如果引用的路径等于我们想要添加的路径,它不会向新引用添加提示。这对于有问题的情况来说已经足够了,并且当 VS 决定使用输出文件夹中的 dll 而不是我们告诉它的内容时,它可以正确检测到。但是当我尝试在输出文件夹中添加对 dll 的引用时(我对许多相关项目使用单个输出文件夹),加载项没有设置提示路径,项目似乎切换到使用其他一些 dll在路径中(在我的情况下是它的 PublicAssemblies 文件夹中的那个)......所以完全删除“if(!newRef.Path.Equals(......”行并始终添加提示可能很有用。我是仍在调查此案例,因此欢迎对代码进行任何额外的 - 嗯、提示或改进。
string newFileName = "the path to your.dll";
VSLangProj.VSProject containingProject = yourProject;
VSLangProj.Reference newRef;
newRef = containingProject.References.Add(newFileName);
if (!newRef.Path.Equals(newFileName, StringComparison.OrdinalIgnoreCase))
Microsoft.Build.Evaluation.Project msBuildProj = Microsoft.Build.Evaluation.ProjectCollection.GlobalProjectCollection.GetLoadedProjects(containingProject.Project.FullName).First();
Microsoft.Build.Evaluation.ProjectItem msBuildRef = null;
AssemblyName newFileAssemblyName = AssemblyName.GetAssemblyName(newFileName);
foreach(var item in msBuildProj.GetItems("Reference"))
AssemblyName refAssemblyName = null;
try
refAssemblyName = new AssemblyName(item.EvaluatedInclude);
catch
if (refAssemblyName != null)
var refToken = refAssemblyName.GetPublicKeyToken();
var newToken = newFileAssemblyName.GetPublicKeyToken();
if
(
refAssemblyName.Name.Equals(newFileAssemblyName.Name, StringComparison.OrdinalIgnoreCase)
&& ((refAssemblyName.Version != null && refAssemblyName.Version.Equals(newFileAssemblyName.Version))
|| (refAssemblyName.Version == null && newFileAssemblyName.Version == null))
&& (refAssemblyName.CultureInfo != null && (refAssemblyName.CultureInfo.Equals(newFileAssemblyName.CultureInfo))
|| (refAssemblyName.CultureInfo == null && newFileAssemblyName.CultureInfo == null))
&& ((refToken != null && newToken != null && Enumerable.SequenceEqual(refToken, newToken))
|| (refToken == null && newToken == null))
)
msBuildRef = item;
break;
if (msBuildRef != null)
Uri newFileUri = new Uri(newFileName);
Uri projectUri = new Uri(Path.GetDirectoryName(containingProject.Project.FullName).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar);
Uri relativeUri = projectUri.MakeRelativeUri(newFileUri);
msBuildRef.SetMetadataValue("HintPath", relativeUri.ToString());
【讨论】:
如果您愿意:您可以为我的匹配问题提供您的答案(或简化):***.com/questions/6690655/…【参考方案4】:很难拒绝一个好的挑战...... 我不在那里,但我认为我有一些不错的提示可以推动这一进程。 首先,我创建了一个微小的测试插件来重现您的问题,然后.. 我失败了!
foreach (Project project in (object[])_applicationObject.ActiveSolutionProjects)
((VSProject)project.Object).References.Add(@"c:\temp\test\FromFolder\bin\debug\KmlLib.dll");
这是我磁盘上某处的随机 dll。该 dll 未签名并最终出现在我的 csproj 中(正是您想要完成的):
<Reference Include="KmlLib">
<HintPath>..\..\FromFolder\bin\debug\KmlLib.dll</HintPath>
</Reference>
然后我注意到您的 dll 已签名。这也没有任何区别。我做的下一个测试是复制一些标准的 MS dll。我从 \Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\ 中选择了 VsWebSite.Interop.dll 并将其复制到我的 FromFolder\bin\debug\ 添加它作为参考突然重现了您的场景> 我得到了包含但没有提示路径。 然后是最后的测试:我将 VsWebSite.Interop.dll 重命名为 xxVsWebSite.Interop.dll 并包含该 dll。突然又添加了提示路径! 将所有这些和您的描述结合在一起,我的猜测是,在添加引用时,VS 首先查看是否可以在其当前搜索位置(GAC、项目文件夹、路径(?)、..)中找到引用的 dll,如果是,则没有提示路径添加。如果找不到,则需要提示路径并将添加。 要看看这个理论是否成立,你可以做两个测试:
将引用的 dll 复制到完全不同的文件夹,然后从那里引用 --> 仍然不会包含提示路径,因为具有相同签名的 dll 仍在“路径”中 复制并重命名引用的 dll 到一个完全不同的文件夹并引用新名称 --> 应该添加提示路径很好奇你的结果:)
【讨论】:
关于 dll 搜索顺序的信息(一般)可以在msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx找到 如果你直接使用Add Reference dialog
添加一个DLL,它是解决方案中另一个项目的输出,它会添加我们想要的提示路径(所以它必须是可能的)。我们无法移动或复制文件,因为我们工具的全部目的是加快构建时间,同时保留对这些库的全面调试。不过感谢您的努力:)【参考方案5】:
要解决此问题,您需要从 DTE 向 项目属性 添加一个引用路径
在 Visual Studio 中设置引用路径:
-
在解决方案资源管理器中,选择项目。
在“项目”菜单上,单击“属性”。
点击参考路径。
在“文件夹”文本框中,指定包含程序集的文件夹的路径。要浏览到该文件夹,请单击省略号 (...)。
点击添加文件夹。
你必须从自动化对象做同样的事情
如果有帮助请告诉我
【讨论】:
不幸的是,它做了一些不同的事情(向父项目添加了搜索丢失 DLL 的位置)。它不会阻止我们添加到本地复制的 DLL(而不是原始的)的现有引用。我们需要为每个单独的 DLL 子引用在添加时专门添加一个提示路径。如果您从Add Reference
对话框添加它,就会出现这种情况,但我们找不到如何在 DTE 中复制它。您提到的引用路径不会传播到 csproj 文件中的各个引用,因为它们已经在本地复制到该项目的 bin。
我们注意到,对项目的Reference Paths
所做的任何更改实际上都会保存到 project.csproj.user 文件中,而不是保存在 project中>.csproj 本身对这个问题没有用处。我已根据您的 cmets 更新了问题。还是谢谢。以上是关于Visual Studio 2010 DTE:如何使添加的 DLL 引用成为绝对引用而不是复制的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio - 如何在运行宏时禁用自动格式化/更正?
如何在 Visual Studio 2008 或 Visual Studio 2010 中设置 JavaScript 断点
如何在 Visual Studio 2010 中使用 Visual Studio 2008 创建的 DLL?