views:

521

answers:

2

I have VS2008 solution containg a project that generates a C# executable that references a project that generates a dll containing both C++/CLI and unmanaged C++.

I would like to merge these into a single executable, as the C++ dll contains security code that I want to embed in the main executable.

I cannot use ILMerge, as the dll contains both managed and unmanaged code. The suggested solution seems to be to use link.exe to link the C# assembly with the C++ object files. This is what I am trying to do.

I manually edited the project file for the c# executable to generate a netmodule. I added a post build step to the executable project to run link.exe to link the c# netmodule and the compiled C++ object files together, then run mt.exe to merge the assembly manifests created by both projects. This runs successfully, but the exe still contains a reference to and uses the c++ types defined in the dll generated by the normal build process for the C++ project.

I then specified /NOASSEMBLY in the project settings for the C++ dll, so it also generates a netmodule. In the C# project, I removed the reference to the C++ project, but added a project dependancy in the solution. I manually edited the C# project file to include similar to:

<ItemGroup>
    <AddModules Include="..\Debug\librarycode.netmodule" />
</ItemGroup>

i.e. to reference the C++ netmodule that is now generated by the C++ project.

However, now the linker step in my post build event fails with:

error LNK2027: unresolved module reference 'librarycode.netmodule'
fatal error LNK1311: 1 unresolved module references: 

This is entirely understandable, as I am not linking in the librarycode netmodule; I am linking in the C++ object files used to generate the netmodule instead.

So in short, how do I merge a c# executable and C++ object files into a single assembly? What have I missed?

My source of reference so far (appart from the link.exe command link reference etc on MSDN) are the two following articles:

Thank you very much in advance.


Update1

I have followed exactly the example in Steve Teixeira's blog, and verified it works. Using reflector, I can see the resulting executable contains two netmodules. The c# netmodule contains a reference to another netmodule but with no name?! If you move the assembly to a new directory, the second netmodule becomes unreferenced (obviously), but the executable still runs, as types with the correct definition exist in the c# netmodule.

Note that the original c# netmodule does contain a named reference to the c++ netmodule, so it must be the linker step that removes the name.

Trying to follow this example in my sample project, I have added an /ASSEMBLYMODULE argument to my post build linker step. The linker now fails with

LNK2022: metadata operation failed (80040427) : Public type 'MixedLanguageLibrary.Class1' is defined in multiple places in this assembly: 'MixedLanguageDemo.exe' and 'mixedlanguagelibrary.netmodule'
LINK : fatal error LNK1255: link failed because of metadata errors

I guess that it is the linker magic that removes the module reference name that I am missing.

Any ideas welcome.


Update2

I have reduced my project to the simplest possible, and am trying to compile is from the command line. The following batch file successfully builds the example in Steve Teixeira's blog:

setlocal    
call "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
if errorlevel 1 goto End
cl /c /MD nativecode.cpp
if errorlevel 1 goto End
cl /clr /LN /MD clrcode.cpp nativecode.obj
if errorlevel 1 goto End
csc /target:module /addmodule:clrcode.netmodule Program.cs
if errorlevel 1 goto End
link /LTCG /CLRIMAGETYPE:IJW /ENTRY:ConsoleApplication1.Program.Main /SUBSYSTEM:CONSOLE /ASSEMBLYMODULE:clrcode.netmodule /OUT:MixedApp.exe clrcode.obj nativecode.obj program.netmodule
:End

The following batch file fails to build my example code with linker error LNK2022:

setlocal
call "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
if errorlevel 1 goto End
cl /c /MD messageprovider.cpp
if errorlevel 1 goto End
cl /clr /LN /MD managedmessageprovider.cpp messageprovider.obj
if errorlevel 1 goto End
csc /target:module /addmodule:managedmessageprovider.netmodule Program.cs Form1.cs Form1.Designer.cs
if errorlevel 1 goto End
link /LTCG /CLRIMAGETYPE:IJW /ENTRY:MixedLanguageDemo.Program.Main /SUBSYSTEM:WINDOWS /ASSEMBLYMODULE:managedmessageprovider.netmodule /OUT:MixedLanguageDemo.exe managedmessageprovider.obj messageprovider.obj program.netmodule
:End

Time for spot the difference :-(

+1  A: 

See this post (duplicate).

GalacticJello
Just comment with a link to the duplicate rather than posting an answer.
ChrisF
The answers to question 72264 involve embedding the dll, then extracting it at runtime. This simplifies installation, but does not improve security. After the first run, the dll is available as a separate assembly. However, this has got me mulling the option to embed the dll and dynamically load it into memory, rather than extract it to file.
swingkid
A: 

Your business case is very similar to SQLite, so the same approach should work for you. Basically they insert the managed assembly into the unmanaged dll as a separate data section. They are then able to p/invoke the unmanaged dll from the managed dll in the normal way. It is also possible to dynamically link to the unmanaged code in the dll.

Anton Tykhyy