8

概括:

当您添加特定的 DLL 时,我们需要使用 DTE 复制“添加引用”对话框的行为(它在 CSProj 文件中的引用中添加了一个提示路径条目)。

**注意:我在这里发布了另一个相关但不重复的帖子:https ://stackoverflow.com/questions/6690655/visual-studio-2010-add-in-how-to-get-a-references-提示路径属性有关此问题的更多信息,另请阅读该内容。我现在已经添加了一个体面的赏金来得到这个答案,并且很乐意对任何体面的答案进行投票:)*

到目前为止的故事:

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

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

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,而不需要复制并引用它?

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

更新:我还尝试以编程方式从 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。

项目的 .csproj.user 文件也Reference Paths保存到项目.csproj.user 文件中,而不是项目.csproj 文件中。

4

5 回答 5

10

我确信这是 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());
    }
}
于 2011-07-27T16:32:15.907 回答
2

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

看看: Microsoft.Build.Evaluation Namespace,有一些关于如何加载 csproj 文件以及如何更改它的有用信息。另请参见项目类

于 2011-07-26T17:34:29.803 回答
0

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

在 Visual Studio 中设置引用路径:

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

你必须从自动化对象做同样的事情

让我知道它是否有帮助

于 2011-07-19T20:54:36.593 回答
0

很难拒绝一个好的挑战......我不在那里,但我认为我有一些体面的暗示来推动这一进程。
首先,我创建了一个微小的测试插件来重现您的问题,然后..我失败了!

    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 复制并重命名为完全不同的文件夹并引用新名称 --> 应添加提示路径

好奇你的结果:)

于 2011-07-22T22:24:28.530 回答
0

我最近在 Visual Studio 2010 中添加 dll 引用时遇到问题,我无法让它添加 HintPath,这导致 TFS 构建出现问题。我注意到我几周前安装的 Productivity Power Tools 插件更改了添加参考对话框。我从“工具”菜单中关闭了插件的“可搜索添加引用对话框”,在重新启动 Visual Studio 2010 后,我再次感到高兴 - 我能够添加引用,这一次 HintPath 也在那里。

于 2012-08-07T13:22:04.327 回答