相关文章推荐
追风的显示器  ·  C#时区逻辑·  1 年前    · 
一身肌肉的荒野  ·  7. Validation, Data ...·  2 年前    · 
痴情的帽子  ·  Unity对于VR的支持 - 简书·  2 年前    · 
冷静的楼房  ·  Qt使用QNetworkAssessMana ...·  2 年前    · 
I am building 9 32bit Dlls under a Visual Studio solution.
For the sake of argument each one of them is dependent on all the others. In other words each one imports symbols from the other 8 and exports symbols imported by each of the other 8.
At the moment I do this by having 2 build configurations. The first tries to compile and link all the dlls.
All the compilations succeed but of course all the linking fails. The build however outputs .exp files and .lib files for each DLL
The second configuration is then run which compiles everything again and then ignores most of the files it's just made and links the libs and export files from the first configuration to build the actually Dlls.
This works but it is a horrible hack and takes twice as long as necessary.
What I need is a way to either get Visual Studio to sort this out for me or to stop the first configuration from trying to link and the second configuration form wasting its time compiling everything again.
I asked a similar question in the VS forum about a year ago and was told that this could be fixed by using the project dependency settings. It can't, as you can't create circular dependencies.
What I'm doing is the same as described here [ ^ ] but with Visual Studio.
Normally I would too but there is a good reason for structuring things this way and it does 'work' in the sense that it eventually builds correct code.
I'm looking at whether I can continue to use Visual Studio for the build or I'll have to resort to custom make files which will be very costly in terms of time and effort.

I want to post this as article code here on CP but I don't want bad feedback because the 2 stage build process confuses people and produces thousands of spurious errors in the first stage which are not really a problem. That and the time it takes to build everything twice which is slowing down development.
What you are using is definitely an antipattern. Allowing cyclic dependency between DLLs is a bad idea not only in case of DLLs but also in case of static libs. If 2 DLLs reference each other then they keep each others reference count on 1 btw, that isn't the end of the world because you probably end their life with ExitProcess at the end.
Change your project this way: You have lib1 and lib2. Let only lib1 to depend on lib2. To communicate back from lib2 to lib1 do the following: Define an interface in your source code that is visible to both libs. In lib1 create an implementation of this interface. When lib 1 communicates to lib2 for example by calling lib2::Init() you pass an interface pointer to lib2 so lib2 will be able to call code inside lib1.
Are there so many connections between the DLLs? I think wiping out some dirt is sometimes fruitful. Check out the number of calls between DLLs and find the direction in the bidirectional connection that contains the least calls - I would replace these with the interface. Then decide. :-) In my opinion this is a fairly easy refactorization without serious code modification. Creating an interface + interface implementation that calls the original exported functions inside lib1 is trivial. Besides this all you need is putting an Init(Interface*) method in the DLLs that is used by lib1 and you have to call the Init method of these DLLs at startup to store the interface pointer to a global variable inside other libs that want to call lib1. From now you call the interface pointer methods in other libs. If you are lazy and there are too many calls to the original lib1 functions in lib2 then you can create helper functions inside lib2 that have the same signature as the originally imported lib1 functions but these helpers call the methods of the interface pointer that is stored in lib2 as a global variable. Its work only around the interface without modification to the cores.
These are the link summaries from the Dlls in the first pass build:

1>D:\...\PosumQOR.dll : fatal error LNK1120: 257 unresolved externals
3>D:\...\stdQOR.dll : fatal error LNK1120: 125 unresolved externals
4>D:\...\SystemQOR.dll : fatal error LNK1120: 258 unresolved externals
5>D:\...\WinQL.dll : fatal error LNK1120: 503 unresolved externals
6>D:\...\WinQAPI.dll : fatal error LNK1120: 659 unresolved externals
7>D:\...\CQOR.dll : fatal error LNK1120: 218 unresolved externals
8>D:\...\ArchQOR.dll : fatal error LNK1120: 65 unresolved externals
9>D:\...\CompilerQOR.dll : fatal error LNK1120: 122 unresolved externals
10>D:\...\CodeQOR.dll : fatal error LNK1120: 119 unresolved externals

So yes I don't really fancy adding 2326 stubs or helper functions to get a cleaner build. This is a miniature version of the project, the full project figures would be much higher.

I like the idea though and I may do exactly as you've suggested for some of the ~70 interfaces.
I would put the interface into the central DLL you were talking about, this only one interface is needed. With this solution the central DLL would depend on all other DLLs but the other DLLs wouldn't depend on the central, they would just use the interface of it. This interface would contain all exported functions of the central DLL. With a lot of functions it can be boring work to do this but its a relatively low risk task. I would share a static lib between the non-central DLLs: This static lib would contain the Init(Interface*) exported symbol and the global variable, plus the helper functions that mimic the exported functions of the central DLL and call the methods of the stored global interface pointer. This way you have to write it only once for all non-central DLLs.

And don't misunderstand, I'm not forcing you to do this. :-) You have to weigh up the costs of your future actions with this codebase yourself. I'm just giving tips.
I'd essentially be creating a wrapper that exported the entire interface of my Dll network but only contained stubs. I have considered doing that despite it being a ridiculous amount of work. I already have static libraries that link into every Dll but I hadn't considered trying to but any import interface there because up till now no import interface was common between everything the lib got linked into.
I will think about it. Thank you.
I have done this type of thing before. It is a bit of a pain but it can be done. I will describe the linkage between 2 DLLs, which I think you can generalize. Set up the linkage so that A.dll compiles and links properly to B.dll. In B.dll, use function pointers to the A.dll APIs, and when you need to call A.dll from B.dll, have stub code for each API call do a LoadLibrary on A.dll and lookup the API(s) with GetProcAddress(). This bookkeeping only needs to be done once and can be cached in a static table. Use of extern "C" linkage in A.dll is not mandatory but will simplify the name lookup mess.
A good idea and I've done this before as well. I'm really not looking however for a way to rewrite the code though just to stop VS running off and wasting 10 minutes spewing spurious errors. One reason I can't adopt this strategy is that 7 of the 9 Dlls can't contain calls to LoadLibrary or GetProcAddress as they have to be source compatible with Linux, i.e. they also build as Linux shared objects
  • Read the question carefully.
  • Understand that English isn't everyone's first language so be lenient of bad spelling and grammar.
  • If a question is poorly phrased then either ask for clarification, ignore it, or edit the question and fix the problem. Insults are not welcome.
  • Don't tell someone to read the manual. Chances are they have and don't get it. Provide an answer or move on to the next question. Let's work to help developers, not make them feel stupid.
  •