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 引用。

假设我有一个简单的解决方案,其中有一个 Project2parent 项目),它引用了一个 Project1child 项目),我做出如下更改这个:

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?

Visual Studio - 以编程方式将调试器附加到远程进程

电脑里的visual studio 2010怎么打开

visual studio 2010不能正确安装,急求