Visual Studio 2010 DTE:如何使添加的 DLL 引用绝对且不复制



摘要:

当您添加特定 DLL 时,我们需要使用 DTE 复制"添加引用"对话框的行为(它会将提示路径条目添加到 CSProj 文件中的引用(。

**注意:我在这里还有另一个相关但不重复的帖子:https://stackoverflow.com/questions/6690655/visual-studio-2010-add-in-how-to-get-a-references-hint-path-property 另请阅读该帖子以获取有关此问题的更多信息。我现在增加了一个体面的赏金来获得这个问题的答案,并且很乐意将投票分配给任何体面的答案:)*

到目前为止的故事:

我正在使用 DTE 以编程方式将项目引用转换为直接 DLL 引用。

假设我有一个引用Project1(项目(的Project2(项目(的简单解决方案,我进行如下更改:

project1Reference = FindProjectReference(project2.References, project1);
project1Reference.Remove();
Reference dllReference = project2.References.Add(project1DllPath);

其中 project1DllPath 引用"c:somewhereProject1BinDebugProject1.dll"文件。

我还不能解决的问题是新的参考不是 "c:somewhereProject1BinDebugProject1.dll",而是指向 "c:somewhereProject2BinDebugProject1.dll"(文件被复制到那里(。

如果我使用"添加引用"菜单直接/手动添加 DLL,它不会执行此复制。

如何将 DLL 引用添加到现有项目的 DLL 中,而无需复制并引用它?

我尝试在添加后添加dllReference.CopyLocal = false;但除了设置标志之外,它没有任何区别。创建后似乎没有修改路径的选项。

更新:我还尝试以编程方式从项目1中删除对Project2的任何构建依赖项,但这没有效果。

以下是 csproj 文件之间的区别:

作为一个项目:

  <ItemGroup>
    <ProjectReference Include="..ClassLibrary1ClassLibrary1.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>..ClassLibrary1binDebugClassLibrary1.dll</HintPath>
    </Reference>
    ...
  </ItemGroup>

看起来能够指定 DLL 引用的提示路径是关键。如何在 DLL 引用上设置提示路径(假设您只有 Reference 属性的句柄(?

更多信息(2011年7月20日(:

下面来自 Muse VSExtension的建议不会影响有问题的 DLL,因为已经从 DLL 的项目 BIN 复制到父项目的 BIN 文件夹。父项目不会费心使用引用路径,因为它的输出文件夹中已经有子 DLL。

此外,项目的Reference Paths将保存到project.csproj.user文件,而不是project.csproj文件。

我确信这是 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());
    }
}

这是否必须仅使用 DTE 来解决?您可以使用 MSBuild 自动化来执行此操作...它具有可以解析 CSPROJ 文件内容的类。

看看:Microsoft.构建.评估命名空间,有一些关于如何加载 CSPROJ 文件以及如何更改它的有用信息。另请参阅项目类。

要解决此问题,您需要从 DTE 向项目属性添加引用路径

若要在 Visual Studio 中设置引用路径,请执行以下操作:

  1. 在"解决方案资源管理器"中,选择项目。
  2. 在"项目"菜单上,单击"属性"。
  3. 单击引用路径。
  4. 在"文件夹"文本框中,指定包含程序集的文件夹的路径。若要浏览到该文件夹,请单击省略号 (...(。
  5. 单击添加文件夹。

您必须从自动化对象执行相同的操作

让我知道它是否有帮助

很难拒绝一个好的挑战...我不在那里,但我认为我有一些不错的提示来推动这项工作。
首先,我创建了一个微小的测试插件来重现您的问题和..我失败了!

    foreach (Project project in (object[])_applicationObject.ActiveSolutionProjects)
    {
        ((VSProject)project.Object).References.Add(@"c:temptestFromFolderbindebugKmlLib.dll");
    }

这是来自我磁盘上某处的随机 dll。dll 是未签名的,最终在我的 csproj 中作为(正是您想要完成的(:

   <Reference Include="KmlLib">
      <HintPath>....FromFolderbindebugKmlLib.dll</HintPath>
    </Reference>

然后我注意到你dll被签名了。这也没有任何区别。我做的下一个测试是复制一些标准的MS dll。我从 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 复制并重命名为完全不同的文件夹并引用新名称 --> 应该添加提示路径

对结果感到好奇:)

我最近在Visual Studio 2010中添加dll引用时遇到问题,我无法使其添加HintPath,这导致了TFS构建的问题。我注意到几周前安装的生产力电动工具附加组件更改了添加引用对话框。我从"工具"菜单中关闭了附加组件的"可搜索添加引用对话框",重新启动Visual Studio 2010后,我再次感到高兴 - 我能够添加引用,这次HintPath也在那里。

最新更新