围绕非托管DLL的C#包装器库要求在构建期间非托管DLL必须位于同一目录中

时间:2018-10-05 22:42:58

标签: c# .net msbuild pinvoke

通常,当使用PInvoke引用封装了非托管DLL的托管DLL时,您必须分别引用这两个DLL-csproj中的托管DLL作为标准<Reference/>,非托管的DLL链接为链接{{1} }(如here所述)。但是,我最近遇到了一个托管包装器库,它不仅在构建过程中自动复制了非托管DLL,而且在同一目录中不存在非托管DLL时,实际上会产生一个构建错误!这是Microsoft.Z3 library,它具有一个托管DLL(Microsoft.Z3.dll),该托管DLL用PInvoke包裹了一个非托管DLL(libz3.dll),因此您可以在C#中使用该库。

如果将两个Z3 DLL放在一个目录中,仅引用Microsoft.Z3.dll,然后使用msbuild编译项目,则将在输出目录中获得这两个DLL,而根本不引用libz3.dll!在<content/>产生的输出中,我看到以下对libz3.dll的引用:

msbuild /verbosity:diag

...

Primary reference "Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2". (TaskId:9)
      Resolved file path is "C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll". (TaskId:9)
      Reference found at search path location "{HintPathFromItem}". (TaskId:9)
      Found embedded scatter file "libz3.dll". (TaskId:9)
      The ImageRuntimeVersion for this reference is "v4.0.30319". (TaskId:9)

以某种方式导致其被复制:

Output Item(s): 
      _ReferenceScatterPaths=
          C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll
                  CopyLocal=true
                  FusionName=
                  HintPath=lib\z3\Microsoft.Z3.dll
                  OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
                  ResolvedFrom={HintPathFromItem}
                  Version=4.7.1.0 (TaskId:9)

它变得更加神秘,因为如果我将libz3.dll移出目录,构建将失败并显示以下错误:

Task "Copy" (TaskId:22)
  Task Parameter:
      SourceFiles=
          C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
                  CopyLocal=true
                  FusionName=Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2
                  HintPath=lib\z3\Microsoft.Z3.dll
                  ImageRuntime=v4.0.30319
                  OriginalItemSpec=Microsoft.Z3
                  ReferenceSourceTarget=ResolveAssemblyReference
                  ResolvedFrom={HintPathFromItem}
                  Version=4.7.1.0
          C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll
                  CopyLocal=true
                  FusionName=
                  HintPath=lib\z3\Microsoft.Z3.dll
                  OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
                  ResolvedFrom={HintPathFromItem}
                  Version=4.7.1.0 (TaskId:22)
  Task Parameter:
      DestinationFiles=
          bin\Debug\Microsoft.Z3.dll
                  CopyLocal=true
                  FusionName=Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2
                  HintPath=lib\z3\Microsoft.Z3.dll
                  ImageRuntime=v4.0.30319
                  OriginalItemSpec=Microsoft.Z3
                  ReferenceSourceTarget=ResolveAssemblyReference
                  ResolvedFrom={HintPathFromItem}
                  Version=4.7.1.0
          bin\Debug\libz3.dll
                  CopyLocal=true
                  FusionName=
                  HintPath=lib\z3\Microsoft.Z3.dll
                  OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
                  ResolvedFrom={HintPathFromItem}
                  Version=4.7.1.0 (TaskId:22)

即使我在csproj中使用"C:\Users\ahelwer\source\test\Framework\FrameworkTest.csproj" (default target) (1) -> (_CopyFilesMarkedCopyLocal target) -> C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\bin\Microsoft.Common.CurrentVersion.targe ts(4358,5): error MSB3030: Could not copy the file "C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll" because it was not found. [C:\Users\ahelwer\source\test\Framework\FrameworkTest.csproj] 以标准方式引用libz3.dll!

问题:

  1. Microsoft.Z3.dll有什么特别之处,使它可以在构建过程中要求libz3.dll位于同一目录中?它带有某些标志吗?
  2. 如何将这种效果添加到我自己的托管包装器库中?
  3. 是否可以从Microsoft.Z3.dll中删除此效果,还是必须以其他方式重新编译它?

1 个答案:

答案 0 :(得分:1)

这就是CSC(C#编译器)所称的"link resource"。它适用于任何类型的文件。

例如,如果您在DLL项目中有这种代码:

using System.Runtime.InteropServices;

namespace Microsoft.Z3
{
    public static class DoSomething
    {
        [DllImport("libz3.dll")]
        public static extern int ReturnValue(int value);
    }
}

这是从Windows DLL导出的C代码:

#include "stdafx.h"

STDAPI ReturnValue(HRESULT value)
{
    return value;
}

您可以这样构建.NET DLL:

"<path to csc.exe>\csc.exe" DoSomething.cs -out:Microsoft.Z3.dll -target:library -linkresource:<path to libz3.dll>\libz3.dll

现在,当您引用此新的Microsoft.Z3.dll时,它的行为与真实的Z3事物相同,它将自动将libz3.dll复制到一边。

请注意AFAIK,Visual Studio不支持此链接资源。

另外一个缺点是,如果要支持多个位,则必须附带两个.NET DLL,一个用于x64,一个用于x86,每个嵌入其本机副本(否则您必须复制所有DllImport等等。