Как связать сборки C # и C ++ в один исполняемый файл? - PullRequest
10 голосов
/ 09 апреля 2010

У меня есть решение VS2008, связанное с проектом, который генерирует исполняемый файл C #, который ссылается на проект, который генерирует dll, содержащую как C ++ / CLI, так и неуправляемый C ++.

Я хотел бы объединить их в один исполняемый файл, поскольку DLL C ++ содержит код безопасности, который я хочу встроить в основной исполняемый файл.

Я не могу использовать ILMerge, так как DLL содержит как управляемый, так и неуправляемый код. Предлагаемое решение, по-видимому, заключается в использовании link.exe для связи сборки C # с объектными файлами C ++. Это то, что я пытаюсь сделать.

Я вручную отредактировал файл проекта для исполняемого файла c #, чтобы сгенерировать сетевой модуль. Я добавил шаг после сборки в исполняемый проект, чтобы запустить link.exe, чтобы связать сетевой модуль c # и скомпилированные объектные файлы C ++, а затем запустить mt.exe, чтобы объединить манифесты сборки, созданные обоими проектами. Это выполняется успешно, но исполняемый файл все еще содержит ссылку и использует типы c ++, определенные в dll, сгенерированные обычным процессом сборки для проекта C ++.

Затем я указал / NOASSEMBLY в настройках проекта для DLL C ++, поэтому он также генерирует сетевой модуль. В проекте C # я удалил ссылку на проект C ++, но добавил зависимость проекта в решение. Я вручную отредактировал файл проекта C #, добавив в него:

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

т.е. для ссылки на сетевой модуль C ++, который теперь генерируется проектом C ++.

Однако теперь шаг компоновщика в моем событии пост-сборки завершается с помощью:

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

Это вполне понятно, так как я не делаю ссылки в сетевом модуле библиотечного кода; Я ссылаюсь на объектные файлы C ++, используемые для генерации сетевого модуля.

Короче говоря, как объединить исполняемый файл c # и объектные файлы C ++ в одну сборку? Что я пропустил?

До сих пор моим источником информации (кроме ссылки на команду link.exe и т. Д. В MSDN) являются две следующие статьи:

Заранее большое спасибо.


Update1

Я точно следовал примеру в блоге Стива Тейшейры и убедился, что он работает. Используя рефлектор, я вижу, что полученный исполняемый файл содержит два сетевых модуля. Сетевой модуль c # содержит ссылку на другой сетевой модуль, но без имени ?! Если вы переместите сборку в новый каталог, второй сетевой модуль становится не связанным (очевидно), но исполняемый файл по-прежнему будет работать, так как в сетевом модуле c # существуют типы с правильным определением.

Обратите внимание, что оригинальный сетевой модуль c # содержит именованную ссылку на сетевой модуль c ++, поэтому это должен быть шаг компоновщика, который удаляет имя.

Пытаясь следовать этому примеру в моем примере проекта, я добавил аргумент / ASSEMBLYMODULE к моему шагу компоновщика после сборки. Компоновщик теперь не работает с

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

Я думаю, что это магия компоновщика, которая удаляет ссылочное имя модуля, которое я пропускаю.

Любые идеи приветствуются.


Update2

Я сократил свой проект до максимально простого и пытаюсь скомпилировать его из командной строки. Следующий пакетный файл успешно создает пример в блоге Стива Тейшейры:

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

Следующий пакетный файл не может создать мой пример кода с ошибкой компоновщика 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

Время для определения разницы: - (

Ответы [ 3 ]

3 голосов
/ 19 октября 2012

Ниже приведен скрипт сборки Nant, который делает именно то, что вы (и я) хотели (если я прочитал ваше правильное право, то есть xD).

Некоторые из них отсутствуют (например, некоторые переменные, которые на самом деле не нужны), но оказалось, что их действительно довольно легко достичь.

Здесь показаны флаги cl / csc и linker, которые необходимы для объединения смешанной и управляемой сборки. Кроме того, в качестве дополнительного «бонуса» все внутренние классы / методы / поля и т. Д. Видны внутри всей новой сборки, что означает, что они пересекают границу проекта.

    <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>
0 голосов
/ 14 августа 2014

Для правильного объединения модуль program.net должен указываться в компоновщике два раза, в списке ввода и в качестве параметра в опции ASSEMBLYMODULE.

Таким образом, вся командная строка будет выглядеть следующим образом:

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

После этой командной строки типы program.module должны быть объединены в MixedLanguageDemo.exe. Вы всегда можете проверить, что попадает в полученную сборку, с помощью отражателей .NET, например ILSpy или Telerik .

Удачного кодирования.

0 голосов
/ 12 апреля 2010

Ваш бизнес-пример очень похож на SQLite, поэтому тот же подход должен работать для вас. По сути, они вставляют управляемую сборку в неуправляемую dll как отдельный раздел данных. Затем они в состоянии p / вызвать неуправляемую DLL из управляемой DLL обычным способом. Также возможно динамически связываться с неуправляемым кодом в dll.

...