28

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):
alt text

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?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
demoncodemonkey
  • 11,730
  • 10
  • 61
  • 103
  • By the way, getting rid of the #using and adding a reference to the Shared project, still produces the exact same linker error :( – demoncodemonkey May 01 '09 at 10:00

3 Answers3

25

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.

Community
  • 1
  • 1
Nick Desjardins
  • 12,814
  • 3
  • 18
  • 10
  • 1
    This works for me as well. FYI I only had to disable it for the xxx project, not the shared project. – Steven Richards May 06 '09 at 08:45
  • Thanks, this worked for me. When an unsignificant change is made to Shared, the xxx project is rebuilt - which saves me from having to do it myself :o) I will submit this as a bug in VS2008. – demoncodemonkey May 06 '09 at 09:08
  • https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=442615 – demoncodemonkey May 06 '09 at 09:51
  • Thanks for logging the bug with Microsoft. This problem slows down my builds as well. Grr. – Nick Desjardins May 06 '09 at 14:24
  • See my other answer for the real accepted answer :) http://stackoverflow.com/questions/810827/lnk2022-metadata-operation-failed-driving-me-insane/1091175#1091175 – demoncodemonkey Mar 02 '10 at 08:13
  • 1
    Included demoncodemonkey's answer in mine to give a more complete answer. – Nick Desjardins May 12 '10 at 15:33
  • For anyone on VS 2010, you need SP1 for the "Enable Managed Incremental Build" option to appear. Based on this http://blogs.msdn.com/b/vcblog/archive/2011/04/05/10149742.aspx – bigh_29 Apr 04 '13 at 16:32
  • Neither of these approaches work for me, unfortunately. However it is VS2010 without SP1 in my case. – IgorStack May 06 '15 at 20:46
  • Doesn't work for me, alas. Using VS2013 configured to build with VS2010. – chtenb Nov 12 '15 at 15:12
  • I always get it to work again by using the 'clean project/solution' command in the solution explorer context window. – A.R. Nov 08 '17 at 19:40
12

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 :(
Edit: I managed to unaccept my answer and mark Nick's answer as the accepted answer :)

demoncodemonkey
  • 11,730
  • 10
  • 61
  • 103
1

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.

Steven Richards
  • 2,802
  • 19
  • 18
  • Good thinking but I already tried exactly that, and unfortunately it doesn't stop the link error. Bizarre... – demoncodemonkey May 01 '09 at 11:02
  • There definitely seems to be some funky behavior. Like you said, adding this block doesn't seem to fix it, but if I also add additional #defines (just random stuff) after that block in both xxx.cpp and xxx2.cpp, it seems to build correctly. – Steven Richards May 01 '09 at 23:26
  • No I can't get it to work. If you save both CPP files then yes it will fix it. But if you only save one of them after the DLL is insignificantly modified, then you'll get that error again. I will edit the question a little bit with the latest info. – demoncodemonkey May 05 '09 at 09:27