C++ Question C++ / CLI linker problem LNK2005

smarly

New member
Joined
Jul 27, 2008
Messages
40
Reaction score
0
Points
0
Hi everyone,

once again I am stuck with a problem and so I humbly ask for the wisdom of the community... this time, the problem is related to Orbiter SDK and C++ /cli.

I am trying to write an Orbiter module using c++ /cli in MS VS 2012.
Whenever I try to compile the code using /clr, I do get the following linker error:

Orbitersdk.lib(Orbitersdk.obj) : error LNK2005: _DllMain@12 is already defined in MSVCRT.lib(dllmain.obj).

Following the MSDN article http://support.microsoft.com/kb/148652/EN-US
I tried to switch the order in which Orbitersdk.lib and msvcrt.lib are compiled. Didn't really do much.

So, I eventuelly got rid of the problem by adding /FORCE:MULTIPLE to the linker command. It compiled, but now it looks like Orbiter is bypassing the InitModule callback of my module, which makes it effectively impossible to use on instance of a class derived from oapi::Module. (However, the callback opcPreStep in the module is still called correctly.)
Well, I figured that /FOCRE:MULTIPLE would break the code and would not be the solution to the initial problem, but it was worth a try.

Long story short, has anyone an idea how I can get rid of the problem between orbiterskd.lib and msvcrt.lib? Any trick to it?

Quite frankly, I have never worked with c++ /cli before, so it's all more or less just try-and-error with a lot of reading and the help of Mister Google. I am not even sure if what I am trying to do is technically feasible...

So, as usual, any help you can provide will be greatly appreciated. :thumbup:

Best regards,

smarly
 

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
Would a CLI/CLR module work properly when it's using OrbiterSDK's DllMain entry? It may require some special initialization, and that's why not the OrbiterSDK's DllMain may have been chosen to be linked.

So, I eventuelly got rid of the problem by adding /FORCE:MULTIPLE to the linker command. It compiled, but now it looks like Orbiter is bypassing the InitModule callback of my module, which makes it effectively impossible to use on instance of a class derived from oapi::Module. (However, the callback opcPreStep in the module is still called correctly.)
That's because InitModule is called from the module's own DllMain, which is (normally) linked from the OrbiterSDK. It is called when Orbiter loads the library (with LoadLibrary), and not explicitly from Orbiter.

Forcing multiple linking will choose only one DllMain, which the linker sees as more appropriate, and not both, and it links the DllMain from MSVCRT.lib instead, and that's the reason the InitModule isn't called at all.

The opcPreStep on the other hand is called explicitly from within Orbiter (if GetProcAddress returned its address) and that's why it's being executed even if the module wasn't properly initialized with OrbiterSDK's DllMain, but only when the module was properly loaded.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,404
Reaction score
581
Points
153
Location
Vienna
OMP and Orbiter.NET use a special technique to get managed initializations working during module loading. The tricky part is getting around a loader-lock.

It is possible to get around the linker troubles with leaving out the STRICT macro and only having the ORBITER_MODULE one. In addition, I had to exclude the following default libs to circumvent multiple definitions: "msvcirt.lib"

regards,
Face
 
Last edited:

smarly

New member
Joined
Jul 27, 2008
Messages
40
Reaction score
0
Points
0
That's because InitModule is called from the module's own DllMain, which is (normally) linked from the OrbiterSDK. It is called when Orbiter loads the library (with LoadLibrary), and not explicitly from Orbiter.

Forcing multiple linking will choose only one DllMain, which the linker sees as more appropriate, and not both, and it links the DllMain from MSVCRT.lib instead, and that's the reason the InitModule isn't called at all.

The opcPreStep on the other hand is called explicitly from within Orbiter (if GetProcAddress returned its address) and that's why it's being executed even if the module wasn't properly initialized with OrbiterSDK's DllMain, but only when the module was properly loaded.

Ok, that explains quite a bit. Thanks for the hint, at least now I know why it behaves the way it does. I'll see if I can find a way to work around this ...

---------- Post added at 02:04 PM ---------- Previous post was at 01:47 PM ----------

OMP and Orbiter.NET use a special technique to get managed initializations working during module loading. The tricky part is getting around a loader-lock.

It is possible to get around the linker troubles with leaving out the STRICT macro and only having the ORBITER_MODULE one. In addition, I had to exclude the following default libs to circumvent multiple definitions: "msvcirt.lib"

I tried to compile the code without the STRICT macro and without ORBITER_MODULE. As soon as I remove the ORBITER_MODULE macro, the code compiles and links correctly. Of course, than the module doesn't work within Orbiter.

Anything else that you did "special" for ORBITER.NET, etc? I had a look at the source files, but since I didn't know what exactly to look for, I couldn't find anything that I could apply to my problem.

I'll be away for the week-end, but I'll start working on this again next week. So if there is anything else that comes to your mind regarding this problem, please let me know. :tiphat:
 

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
I tried to compile the code without the STRICT macro and without ORBITER_MODULE. As soon as I remove the ORBITER_MODULE macro, the code compiles and links correctly.
Is it the same linker error with only ORBITER_MODULE macro defined?
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,404
Reaction score
581
Points
153
Location
Vienna
I tried to compile the code without the STRICT macro and without ORBITER_MODULE. As soon as I remove the ORBITER_MODULE macro, the code compiles and links correctly. Of course, than the module doesn't work within Orbiter.

And did you also configure the default lib ignoring? My code compiles and links just fine with ORBITER_MODULE in it. Of course it is not calling the InitModule method on its own, so you have to implement a DLLMain yourself for it.

Anything else that you did "special" for ORBITER.NET, etc? I had a look at the source files, but since I didn't know what exactly to look for, I couldn't find anything that I could apply to my problem.

So you did not see the DLLMain, the loader-lock thread and the static managed object that performs managed operations during DLL loading? That's really all that is special to it.
 

smarly

New member
Joined
Jul 27, 2008
Messages
40
Reaction score
0
Points
0
Is it the same linker error with only ORBITER_MODULE macro defined?

Yes, error message is the same regardless whether I add the STRICT macro or not.

---------- Post added at 08:00 AM ---------- Previous post was at 07:32 AM ----------

And did you also configure the default lib ignoring? My code compiles and links just fine with ORBITER_MODULE in it. Of course it is not calling the InitModule method on its own, so you have to implement a DLLMain yourself for it.

Yes, I did add the msvcirt.lib to the ignore lib section. Same problem. If I also add msvcrt.lib to ignore, I get a lot of "unresolved external symbols" - errors.

Well, here is the linker output ( no STRICT macro, ORBITER_MODULE defined, msvcirt.lib added to ignore):

Code:
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppBuild.targets(1137,5): warning MSB8012: TargetPath(..\..\Debug\MyModule.dll) does not match the Linker's OutputFile property value (..\..\..\Modules\Plugin\MyModule.dll). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Link.OutputFile).
1>Link:
1>  All outputs are up-to-date.
1>     Bibliothek "..\..\..\Modules\Plugin\MyModule.lib" und Objekt "..\..\..\Modules\Plugin\MyModule.exp" werden erstellt.
1>Orbitersdk.lib(Orbitersdk.obj) : error LNK2005: _DllMain@12 ist bereits in MyModule.obj definiert.
1>..\..\..\Modules\Plugin\MyModule.dll : fatal error LNK1169: Mindestens ein mehrfach definiertes Symbol gefunden.
1>
1>Build FAILED.
Unfortunately, the linker output is in my native language and even though I spent a couple of hours trying to fix this, I failed miserably. Hope it is still at least partially understandable.
At the moment, the only way I see to get the linker to link at all is by adding the /FORCE:MULTIPLE argument.

So you did not see the DLLMain, the loader-lock thread and the static managed object that performs managed operations during DLL loading? That's really all that is special to it.

Frankly, I missed that part. Thanks for pointing it out! I tried to load my plugin like this:
Code:
#pragma unmanaged

==============================================================
// Global variables and launcher
// ==============================================================

MyModule *g_mymod = 0;   // instance pointer

HINSTANCE dllHandle;

DLLCLBK void InitModule (HINSTANCE hDLL)
{

    g_mymod = new MyModule(hDLL);
    //Register plugin
    oapiRegisterModule(g_mymod);

}

void WINAPI clrInitialize(DWORD sd)
{
    InitModule(dllHandle);
}

BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,
  DWORD fdwReason,
  LPVOID lpvReserved
)
{
    DWORD threadID;
    switch(fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        dllHandle=hinstDLL;        
        CreateThread(0, 0, (LPTHREAD_START_ROUTINE) clrInitialize, 0, 0, &threadID);
        break;
    }
    return TRUE;
}
Where MyModule is a class derived from oapi::Module(). However, the module is not loading correctly. I guess I have to add the static managed "launcher^" object from your code as well, but I think I will need some time to figure out what exactly that static object does and how it works.

Thanks for your help so far! :tiphat:
 
Last edited:
Top