10

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 :-(

swingkid
  • 101
  • 1
  • 5
  • possible duplicate of http://stackoverflow.com/questions/72264/how-can-a-c-windows-dll-be-merged-into-a-c-application-exe – spoulson Apr 09 '10 at 16:29
  • just out of curiosity, what is the "business use case" for this? – Marek Apr 10 '10 at 21:00
  • Our customer have requested we implement software protection andlicensing using a third party library, as they already maintain and administer other products using that licensing infrastructure. The third party library provides a C API, but our product is C#. So, I have our C# executable, and a dll containing this C api and a C++/CLI interop interface we have written. It would be more secure if we could link all this into one executable, otherwise you could bypass the security just by substituting the dll. Hope this helps. – swingkid Apr 11 '10 at 12:53
  • To be honest, this is also now out of academic curiosity; it should be possible to link them all together. I would ike to understand how, even if we end up implementing a different solution. – swingkid Apr 11 '10 at 12:54
  • See this [post](http://stackoverflow.com/questions/72264/how-can-a-c-windows-dll-be-merged-into-a-c-application-exe) (duplicate). – GalacticJello Apr 09 '10 at 16:28
  • 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 Apr 10 '10 at 19:53
  • I have to ask; did you ever find it out? – Alxandr Oct 18 '12 at 22:24

3 Answers3

3

The following is a Nant build-script that does exactly what you (and me) wanted (if I read your wanting right that is xD).

Some of it is missing (like some variables, that aren't really needed), but it turned out to actually be fairly easy to achieve.

This shows the cl/csc and linker-flags you need to be able to merge a mixed and a managed assembly. Also, as an added "bonus" all internal classes/methods/fields etc. is visible within the entire new assembly, meaning that they cross the boundary of the project.

    <delete file="${tmp.cpp}" />
    <foreach item="File" property="filename">
        <in>
            <items basedir="${basedir}/SpotiFire.LibSpotify">
                <include name="**.h" />
            </items>
        </in>
        <do>
            <echo message="#include &quot;${filename}&quot;&#10;" append="true" file="${tmp.cpp}" />
        </do>
    </foreach>

    <cl outputdir="${build.obj}" options="/clr /LN">
        <sources basedir="${basedir}/SpotiFire.LibSpotify">
            <include name="*.cpp" />
            <include name="${tmp.cpp}" asis="true" />
            <exclude name="AssemblyInfo.cpp" />
        </sources>
    </cl>

    <csc target="module" output="${build.obj}/SpotiFire.netmodule">
        <modules basedir="${build.obj}">
            <include name="tmp.obj" />
        </modules>
        <references refid="all_refs" />
        <sources basedir="${basedir}/SpotiFire.SpotifyLib">
            <include name="**.cs" />
        </sources>
    </csc>

    <link output="${build.dir}/${name}.dll" options="/LTCG /FIXED /CLRIMAGETYPE:IJW /NOENTRY /DLL">
        <sources basedir="${build.obj}">
            <include name="*.obj" />
            <include name="*.netmodule" />
            <include name="${basedir}/libspotify.lib" asis="true" />
        </sources>
        <arg value="/DEBUG" if="${build.debug == 'true'}" />
    </link>
Alxandr
  • 12,345
  • 10
  • 59
  • 95
1

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
  • 19,370
  • 5
  • 54
  • 56
0

In order to be correctly merged in, program.netmodule should be indicated in linker two times, in input list and as the parameter in ASSEMBLYMODULE option.

So the whole command line will will be as follows:

link /LTCG /CLRIMAGETYPE:IJW /ENTRY:MixedLanguageDemo.Program.Main /SUBSYSTEM:WINDOWS /ASSEMBLYMODULE:program.netmodule /OUT:MixedLanguageDemo.exe managedmessageprovider.obj messageprovider.obj program.netmodule

After this command line, program.module types should be merged into MixedLanguageDemo.exe. You can always check what gets into your resulting assembly with .NET reflectors, like ILSpy or Telerik.

Happy coding.

Anton K
  • 4,658
  • 2
  • 47
  • 60