C++/CLI .obj 和 C# .netmodule 之间的 LNK2022 部分 class

LNK2022 between C++/CLI .obj and C# .netmodule with partial class

在尝试使用混合语言程序集(例如 here or here)时,我遇到了一些奇怪的 link 错误。我不是在寻找 "don't do that" 建议或替代方案,而是想了解这些错误的含义,因为从表面上看它们都没有多大意义。

C#代码:

// csppStub.cs
public static partial class To
{
    static partial void Do();
    extern public static bool Done();
}

C++代码:

// cspp.cpp
public ref class To abstract sealed
{
    // lnk2022 metadata operation failed (801311D6):
    // Differing number of methods in duplicated types (To): (0x02000002).
    static void Do() { }

    // lnk2022 metadata operation failed (801311D8):
    // Differing number of parameters in duplicated method (types: To; methods: Done): (0x06000001).
    public: static bool Done() { return true; }
};

VS 2015 项目文件:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <ProjectName>cspp</ProjectName>
    <ProjectGuid>{A6FF913B-C27A-4CBC-A847-B15CFA7AE80F}</ProjectGuid>
    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <PlatformToolset>v140</PlatformToolset>
    <CLRSupport>true</CLRSupport>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup>
    <_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Link>
      <AdditionalDependencies>$(OutDir)\csppStub.netmodule;%(AdditionalDependencies)</AdditionalDependencies>
      <SubSystem>Windows</SubSystem>
      <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
    </Link>
    <ClCompile>
      <CompileAsManaged>true</CompileAsManaged>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="cspp.cpp">
      <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</CompileAsManaged>
      <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Async</ExceptionHandling>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <CustomBuild Include="csppStub.cs">
      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">csc /target:module /out:$(OutDir)\csppStub.netmodule csppStub.cs</Command>
      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkObjects>
      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(OutDir)\csppStub.netmodule</Outputs>
    </CustomBuild>
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

构建输出:

Error LNK2022 metadata operation failed (801311D8): Differing number of parameters in duplicated method (types: To; methods: Done): (0x06000001).

Error LNK2022 metadata operation failed (801311D6): Differing number of methods in duplicated types (To): (0x02000002).

链接器被您尝试做的事情弄糊涂了,所以它的诊断不是很好。 C++/CLI 代码中的一个常见问题是它可能会在多个目标文件中遇到相同的 ref class 声明。由#include 指令引起。在 C# 中从未发生过的事情,partial 关键字解析为 compile-time。

链接器必须对此做一些事情,它需要将这些重复项过滤成一个它可以在元数据中发出的定义。它执行测试以确保 class 定义完全相同。 LNK2022 当它们不存在时。常见的原因是宏或不同的编译选项。

但在这种情况下,重复项来自 C# 和 C++/CLI 声明。放弃所有的希望,那永远不会是一场比赛。 extern 关键字并没有像你想的那样做,它是抖动的一个指令,告诉它需要在别处找到函数。它知道的其他地方不多,也不是可扩展的,仅限于 CLR 实现的功能、[DllImport] 声明和 COM 互操作。

元数据中根本没有类似于 partial 的机制。您将不得不考虑采用您在 .NET 中执行此操作的常规无聊方式、继承和多态性。 C++/CLI class 可以使用 C# class 作为基础,C# class 可以是 abstract.