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

Gon*_*ing 8 project-reference visual-studio-2010 envdte

摘要:

当您添加特定DLL时,我们需要使用DTE复制"添加引用"对话框的行为(它将Hint路径条目添加到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);
Run Code Online (Sandbox Code Playgroud)

其中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>
Run Code Online (Sandbox Code Playgroud)

作为DLL引用(路径完全丢失):

 <ItemGroup>
    <Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <Private>False</Private>
    </Reference>
    ...
  </ItemGroup>
Run Code Online (Sandbox Code Playgroud)

作为手动引用的DLL:

  <ItemGroup>
    <Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\ClassLibrary1\bin\Debug\ClassLibrary1.dll</HintPath>
    </Reference>
    ...
  </ItemGroup>
Run Code Online (Sandbox Code Playgroud)

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

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

来自下面的Muse VSExtensions的建议不会影响有问题的DLL,因为已经从DLL的项目BIN到父项目的BIN文件夹进行了复制.父项目不打算使用引用路径,因为它已在其输出文件夹中具有子DLL.

Reference Paths项目的另外还保存到项目 .csproj.user文件中,而不保存到项目 .csproj文件中.

bdr*_*jer 10

我确信这是VS 2010中的一个新bug /功能,因为我有一个加载项,几天前我从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());
    }
}
Run Code Online (Sandbox Code Playgroud)