Объединить нативную DLL и сборку в одну DLL - PullRequest
4 голосов
/ 06 декабря 2009

Я сейчас программирую на C ++ и C #. Использование нативного C ++ для числовой вычислительной части.

Изначально я намеревался использовать C ++ / CLI для создания оболочки для собственных классов C ++, но я обнаружил, что это приведет к замедлению в 2–4 раза.

Поэтому я решил скомпилировать свой родной C ++ в DLL и вызвать .NET / C # через P / Invoke . Я сделаю предварительную обработку данных в C # и сжатие чисел в нативной DLL.

Проблема в том, что когда я передаю свою работу другим, я бы хотел, чтобы это была одна DLL.

Возможно ли это? Кстати, у меня есть весь исходный код на родной C ++.

Ответы [ 5 ]

6 голосов
/ 06 декабря 2009

Вы можете сделать это довольно легко и полностью поддерживая, создавая сетевые модули. Скомпилируйте ваш комбинированный код C ++ и C ++ / CLI в .obj (сетевые модули C ++ имеют расширение .obj, сетевые модули C # имеют расширение .netmodule), а затем свяжите это с вашим проектом C #.

Детали: http://blogs.msdn.com/junfeng/archive/2005/05/19/420186.aspx. Проработанный пример: http://blogs.msdn.com/b/junfeng/archive/2006/05/20/599434.aspx.

2 голосов
/ 18 сентября 2013

Еще одна опция, которую я использовал в прошлом, - это чтобы ваша программа / dll действовала как самораспаковывающийся архив. Загрузите управляемый dll как сжатый ресурс и распакуйте его во временную папку любой машины, на которой вы работаете.

У меня есть приложение, которое использует AlphaVSS , в которое встроено несколько встроенных DLL. Я хотел, чтобы приложение было полностью переносимым как единый исполняемый файл и не требовало установки. Вот как я с этим справился.

public static void ExtractResources()
{
    Directory.CreateDirectory(Path.Combine(Program.DataPath, FolderName));

    //Extract the runtime in case it is not installed on the destination computer
    if (NativeMethods.Is64BitOperatingSystem)
    {
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\Microsoft.VC90.CRT.manifest"),
                       Resources.x64.X64Resources.Microsoft_VC90_CRT_manifest);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcm90.dll"), Resources.x64.X64Resources.msvcm90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcp90.dll"), Resources.x64.X64Resources.msvcp90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcr90.dll"), Resources.x64.X64Resources.msvcr90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.52.x64.dll"), Resources.x64.X64Resources.AlphaVSS_52_x64_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.60.x64.dll"), Resources.x64.X64Resources.AlphaVSS_60_x64_dll);
    }
    else
    {
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\Microsoft.VC90.CRT.manifest"),
                       Resources.x86.X86Resources.Microsoft_VC90_CRT_manifest);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcm90.dll"), Resources.x86.X86Resources.msvcm90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcp90.dll"), Resources.x86.X86Resources.msvcp90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcr90.dll"), Resources.x86.X86Resources.msvcr90_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.51.x86.dll"), Resources.x86.X86Resources.AlphaVSS_51_x86_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.52.x86.dll"), Resources.x86.X86Resources.AlphaVSS_52_x86_dll);
        FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.60.x86.dll"), Resources.x86.X86Resources.AlphaVSS_60_x86_dll);
    }

    FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.Common.dll"), Resources.VssResourcesCommon.AlphaVSS_Common_dll);
    FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\ClientDataSnapshotVssAgent.exe"), Resources.VssResourcesCommon.ClientDataSnapshotVssAgent_exe);

}

Теперь в моем коде у меня был исполняемый файл агента, работающий во временной папке, которая ссылалась на эти извлеченные dll, однако вы могли легко использовать ваш P / Invoke и указать полный путь, к которому вы извлекли его, в сигнатуре P / Invoke.


В качестве дополнительного совета я написал небольшую программу, которая просто скопирует любой файл, который вы передадите ему, а затем вставил следующее в событие Post-build для программы агента.

"$(SolutionDir)\DataCompressor\DataCompressor.exe" "$(TargetPath)" "$(SolutionDir)\ClientDataSnapshot\VSS\Resources\$(TargetFileName).gz"

Вы могли бы сделать что-то похожее для вашего события посткомпиляции для вашей родной dll, просто обновите файл gz в вашем управляемом проекте каждый раз, когда он завершает сборку, и убедитесь, что родная dll предшествует управляемой dll в порядке сборки, и у вас всегда будет самая последняя копия в качестве ресурса в вашем управляемом проекте.

using System;
using System.IO;
using System.IO.Compression;

namespace DataCompressor
{
    class Program
    {
        //This program is used to compress the compiled VssAgent for storage in the snapshot program
        static void Main(string[] args)
        {
            //Will quit directly if any args are invalid.
            ValidateArgs(args);

            using (var sourceFile = File.OpenRead(args[0]))
            using (var destFile = new FileStream(args[1], FileMode.Create))
            using (var gz = new GZipStream(destFile, CompressionMode.Compress))
            {
                sourceFile.CopyTo(gz);
            }
        }

        private static void ValidateArgs(string[] args)
        {
            if (args.Length == 2)
            {
                if (File.Exists(args[0]) == false)
                {
                    Console.Error.WriteLine("The source file did not exist.");
                    Environment.Exit(-2);
                }
                if (Directory.Exists(Path.GetDirectoryName(args[1])) == false)
                {
                    Console.Error.WriteLine("The destination directory did not exist.");
                    Environment.Exit(-3);
                }
            }
            else
            {
                Console.Error.WriteLine("You must pass two arguments.");
                Environment.Exit(-1);
            }
        }
    }
}
0 голосов
/ 18 сентября 2013

Недавно я столкнулся с подобной проблемой, когда собственный код C внутри DLL C ++ / CLI работал очень медленно. Оказывается, Visual Studio компилировал мои функции C как управляемые.

Чтобы решить эту проблему, мы использовали Pragma, неуправляемую для принудительной компиляции. Это восстановило скорость без необходимости перемещать нативный код в собственную DLL без CLI

// Force native compilation
#pragma managed(push, off)  

// Your native code
void Foo()
{
}

// Restore managed compilation
#pragma managed(push, on)   
0 голосов
/ 06 декабря 2009

Я не знаю, каким образом вы можете объединить управляемый код, написанный на C #, и нативный код, написанный на C ++ (или любом другом языке). Самое близкое, что вы можете сделать, - это использовать нативный C ++ с управляемым C ++ в одной сборке, что, по вашему мнению, привело к существенному замедлению в вашем коде.

0 голосов
/ 06 декабря 2009

Вы можете смешивать языки в одной сборке в .NET, но не естественно. Я бы избежал этого, если бы в этом не было крайней необходимости.

См:

http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx

Я держу сборки на разных языках отдельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...