“LNK2022: metadata operation failed” driving me in

2020-01-31 03:00发布

问题:

I have a big solution with lots of projects, using VS2008 SP1, and at least once a day I encounter the LNK2022 error. If I do a full rebuild of the solution it builds fine, but this is not fun.

It happens when a dependent DLL is changed 'insignificantly' (i.e. without changing any methods or classes), and the referencing project is later built. It fails when merging the metadata - whatever that means.

First thing to note is that the shared DLL is referenced with #using from multiple .CPP files.
Second thing is that if I delete the AssemblyInfo.cpp from the shared DLL then the problem goes away (but I'm not sure if this is a sensible fix?).

I've narrowed it down as far as possible into the following solution containing 2 CLR Class Library projects (the xxx project depends on Shared):

Here are the contents of each file:

Shared.cpp:

public ref class Shared
{
};

inc.h:

#pragma once
#using "Shared.dll"
public ref class Common
{
private:
    Shared^ m_fred;
};

xxx.cpp and xxx2.cpp:

#include "inc.h"

To reproduce, first rebuild the solution. It will build OK.
Now save Shared.cpp and build the solution, it will build fine and show:

...
2>------ Build started: Project: xxx, Configuration: Debug Win32 ------
2>Inspecting 'd:\xxx\xxx\Debug\Shared.dll' changes ...
2>No significant changes found in 'd:\xxx\xxx\Debug\Shared.dll'.
2>xxx - 0 error(s), 0 warning(s)
========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Now save xxx.cpp and build the solution, it fails with the following message:

1>------ Build started: Project: xxx, Configuration: Debug Win32 ------
1>Compiling...
1>xxx.cpp
1>Linking...
1>xxx2.obj : error LNK2022: metadata operation failed (80131188) : Inconsistent field declarations in duplicated types (types: Common; fields: m_fred): (0x04000001).
1>LINK : fatal error LNK1255: link failed because of metadata errors
1>xxx - 2 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

EDIT:
The differences between the IL for xxx.obj and xxx2.obj are as follows:

(for xxx.obj)
// AssemblyRef #2 (23000002)
// -------------------------------------------------------
// Token: 0x23000002
// Public Key or Token:
// Name: Shared
// Version: 1.0.3412.16606
// Major Version: 0x00000001
// Minor Version: 0x00000000
// Build Number: 0x00000d54
// Revision Number: 0x000040de
// Locale:
// HashValue Blob: 1c bb 8f 13 7e ba 0a c7 26 c6 fc cb f9 ed 71 bf 5d ab b0 c0
// Flags: [none] (00000000)

(for xxx2.obj)
// AssemblyRef #2 (23000002)
// -------------------------------------------------------
// Token: 0x23000002
// Public Key or Token:
// Name: Shared
// Version: 1.0.3412.16585
// Major Version: 0x00000001
// Minor Version: 0x00000000
// Build Number: 0x00000d54
// Revision Number: 0x000040c9
// Locale:
// HashValue Blob: 64 af d3 12 9d e3 f6 2b 59 ac ff e5 3b 38 f8 fc 6d f4 d8 b5
// Flags: [none] (00000000)

This implies to me that xxx2.obj is still using the old version of Shared.dll, and that is conflicting with xxx.obj which is using the updated Shared.dll. So how can I workaround that then?

回答1:

This problem is caused by the new Managed Incremental Build feature in Visual Studio 2008. As you spotted, the metadata did change, but not in a way that the managed incremental build feature considers signifcant. However, if you force a recompile of one of the cpp files, it grabs the new metadata, embeds it in the obj, and then the linker sees a conflict.

There are two ways to resolve this problem. A simple way that seems to work, from demoncodemonkey's answer below is to specify an explicit version number in the referenced assembly metadata to instruct the compiler that the referenced assembly is in fact at the same version:

Replace

[assembly:AssemblyVersionAttribute("1.0.*")];

with

[assembly:AssemblyVersionAttribute("1.0.0.1")];

in AssemblyInfo.cpp. This will ensure that the version does not change between incremental builds.

The alternative way to avoid this problem is by disabling the feature. We may recompile some cpp files unnecessarily, but it's better than having the linker fail.

In the project properties, under Configuration Properties > General, set "Enable Managed Incremental Build" to No.



回答2:

Microsoft replied to my Connect post, with a much better workaround:

It looks like the problem is caused by the mismatch in version between the two .objs. A better workaround is to replace

[assembly:AssemblyVersionAttribute("1.0.*")];

with

[assembly:AssemblyVersionAttribute("1.0.0.1")];

in AssemblyInfo.cpp. This will ensure that the version does not change between incremental builds.

This works for me and obviously this is preferable to disabling the feature.
Anyway the accepted answer has been chosen and cannot be changed now :(



回答3:

Try this in xxx.cpp and xxx2.cpp:

#ifndef _PROTECT_MY_HEADER
#define _PROTECT_MY_HEADER
#include  "inc.h"
#endif

#pragma once isn't enough to protect the header in this case.