c# - 如何将 C# 和 C++ 程序集链接到单个可执行文件中?

标签 c# visual-c++ linker c++-cli

我的 VS2008 解决方案包含一个生成 C# 可执行文件的项目,该项目引用一个生成包含 C++/CLI 和非托管 C++ 的 dll 的项目。

我想将它们合并到一个可执行文件中,因为 C++ dll 包含我想嵌入到主可执行文件中的安全代码。

我不能使用 ILMerge,因为 dll 包含托管和非托管代码。建议的解决方案似乎是使用 link.exe 将 C# 程序集与 C++ 目标文件链接起来。这就是我想要做的。

我手动编辑了 c# 可执行文件的项目文件以生成网络模块。我向可执行项目添加了构建后步骤以运行 link.exe 将 c# netmodule 和编译的 C++ 目标文件链接在一起,然后运行 ​​mt.exe 合并两个项目创建的程序集 list 。这运行成功,但 exe 仍然包含对 C++ 项目的正常构建过程生成的 dll 中定义的 C++ 类型的引用和使用。

然后我在 C++ dll 的项目设置中指定了/NOASSEMBLY,因此它也生成了一个网络模块。在 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: 

这是完全可以理解的,因为我没有在 librarycode netmodule 中链接;我正在链接用于生成网络模块的 C++ 目标文件。

简而言之,如何将 C# 可执行文件和 C++ 目标文件合并到一个程序集中?我错过了什么?

到目前为止,我的引用来源(来自 MSDN 上的 link.exe 命令链接引用等)是以下两篇文章:

非常感谢您。


更新1

我完全遵循了 Steve Teixeira 博客中的示例,并验证了它的有效性。使用反射器,我可以看到生成的可执行文件包含两个网络模块。 c# 网络模块包含对另一个网络模块的引用,但没有名称?!如果将程序集移动到新目录,第二个网络模块将变为未引用(很明显),但可执行文件仍会运行,因为具有正确定义的类型存在于 c# 网络模块中。

请注意,原始的 c# netmodule 确实包含对 c++ netmodule 的命名引用,因此它必须是删除名称的链接器步骤。

尝试在我的示例项目中遵循此示例,我在构建后链接器步骤中添加了一个/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

我猜是链接器魔术删除了我丢失的模块引用名称。

欢迎任何想法。


更新2

我已将我的项目缩减到尽可能简单的程度,并且正在尝试从命令行进行编译。以下批处理文件成功构建了 Steve Teixeira 博客中的示例:

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

是时候发现差异了:-(

最佳答案

以下是一个 Nant 构建脚本,它完全可以满足您(和我)的需求(如果我正确理解了您的需求,那就是 xD)。

其中一些缺失(比如一些变量,并不是真正需要的),但事实证明它实际上很容易实现。

这显示了您需要能够合并混合程序集和托管程序集的 cl/csc 和链接器标志。此外,作为额外的“奖励”,所有内部类/方法/字段等在整个新程序集中都是可见的,这意味着它们跨越了项目的边界。

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

关于c# - 如何将 C# 和 C++ 程序集链接到单个可执行文件中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2609056/

相关文章:

c# - protobuf-net - 列出支持的类型

c++ - 将元素替换为 vector 的特定位置

visual-c++ - C++ 预处理器中的奇怪定义

ios - 找不到适用于 Parse 框架的 iOS 模拟器的体系结构 i386 的符号

c - 在c中链接文件(...的多个定义)

c - 在 Unix 上查找已编译库的依赖项

c# - FK 约束可能会导致循环或多个级联路径

c# - 如何将字段绑定(bind)到用户控件

c# - 具有分组依据的 Linq 查询

c++ - 为什么移动一个值比它的大小更远不会导致 0?