簡體   English   中英

如何將C#和C ++程序集鏈接到單個可執行文件中?

[英]How to link C# and C++ assemblies into a single executable?

我有VS2008解決方案包含一個項目,該項目生成一個C#可執行文件,該項目引用一個生成包含C ++ / CLI和非托管C ++的DLL的項目。

我想將這些合並到一個可執行文件中,因為C ++ DLL包含我想要嵌入主可執行文件的安全代碼。

我不能使用ILMerge,因為dll包含托管代碼和非托管代碼。 建議的解決方案似乎是使用link.exe將C#程序集與C ++目標文件鏈接起來。 這就是我想要做的。

我手動編輯了c#可執行文件的項目文件以生成netmodule。 我在可執行項目中添加了一個構建后步驟,以運行link.exe將c#netmodule和已編譯的C ++目標文件鏈接在一起,然后運行mt.exe來合並兩個項目創建的程序集清單。 這運行成功,但是exe仍然包含對C ++項目的正常構建過程生成的dll中定義的c ++類型的引用和使用。

然后我在C ++ dll的項目設置中指定了/ NOASSEMBLY,因此它還生成了一個netmodule。 在C#項目中,我刪除了對C ++項目的引用,但在解決方案中添加了項目依賴性。 我手動編輯了C#項目文件,包含類似於:

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

即引用現在由C ++項目生成的C ++ netmodule。

但是,現在我的post build事件中的鏈接器步驟失敗了:

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

這完全可以理解,因為我沒有鏈接到librarycode netmodule; 我鏈接在用於生成netmodule的C ++目標文件中。

簡而言之,如何將ac#executable和C ++目標文件合並到一個程序集中? 我錯過了什么?

到目前為止我的引用源(來自MSDN上的link.exe命令鏈接引用等)是以下兩篇文章:

非常感謝你提前。


UPDATE1

我在Steve Teixeira的博客中完全遵循了這個例子,並證實它有效。 使用反射器,我可以看到生成的可執行文件包含兩個netmodule。 c#netmodule包含對另一個netmodule的引用但沒有名稱?! 如果將程序集移動到新目錄,則第二個netmodule變為未引用(顯然),但可執行文件仍然運行,因為c#netmodule中存在具有正確定義的類型。

請注意,原始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

我猜這是鏈接器魔術刪除我缺少的模塊引用名稱。

歡迎任何想法。


UPDATE2

我已經盡可能簡化了我的項目,我試圖從命令行編譯。 以下批處理文件在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>

為了正確合並,program.netmodule應該在鏈接器中指示兩次,在輸入列表中,並作為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反射器(如ILSpyTelerik)檢查生成的程序集中的內容

快樂的編碼。

您的業​​務案例與SQLite非常相似,因此相同的方法應該適合您。 基本上,他們將托管程序集作為單獨的數據部分插入到非托管dll中。 然后,他們能夠以正常方式從托管dll調用非托管dll。 也可以動態鏈接到dll中的非托管代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM