Как использовать класс C ++ в приложении C # без учета платформы? - PullRequest
4 голосов
/ 16 сентября 2010

У меня есть собственная / неуправляемая библиотека C ++ с несколькими классами, которые я хотел бы использовать из C #.Большинство решений, которые я прочитал (например, это и это ), предлагают мне создать оболочку C ++ / CLI и использовать оболочку в моем проекте C #.Большинство из этих предложений, однако, игнорируют платформу.Насколько мне известно, если неуправляемая DLL является 32-разрядной, моя DLL-оболочка должна быть 32-разрядной, и это заставит мой проект C # использовать платформу x86, даже если у меня есть как 32-, так и 64-Доступны битовые версии неуправляемой DLL.

Ранее я решал эту проблему с помощью API C, используя P / Invoke с LoadLibrary() и Marshal.GetDelegateForFunctionPointer(), но я думаю, что перенос каждого вызова метода объектов C ++ будетбыть подверженным ошибкам и трудным в обслуживании.Я также не думаю, что мне следует пытаться полагаться на обнаружение искаженного имени экспортов в DLL C ++.

Кстати, библиотека C ++, которую я пытаюсь использовать, - это Google V8 JavaScript VM (http://code.google.com/p/v8/), который может быть скомпилирован для x86 или x64, поэтому о переносе исходного кода C ++ на прямой C # не может быть и речи. И да, я знаю о нескольких существующих проектах, которые обертывают V8 для использования с управляемым кодом, напримерas v8sharp (http://v8sharp.codeplex.com/) и Javascript .NET (http://javascriptdotnet.codeplex.com/).). Однако, насколько мне известно, все они используют оболочку C ++ / CLI, специфичную для платформы. Для взаимодействия с другими библиотеками управляемого кода янужен компонент управляемого кода для использования AnyCPU.

Есть ли хороший способ сделать это?

Ответы [ 2 ]

3 голосов
/ 18 сентября 2010

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

Он основан на создании нового домена приложения с путями приватных бинов для конкретной платформы, откуда загружать сборки. Затем вы прячете свой собственный код в 32- или 64-битные каталоги, и он загружает то, что когда-либо будет наиболее подходящим.

Итак, ради аргумента у вас есть проект C ++ CLR с:

#pragma once

using namespace System;

namespace NativeLib {

    public ref class NativeClass
    {
    public:
        static void DoSomething()
        {
            Console::WriteLine("IntPtr.Size = {0}", IntPtr::Size);
        }
    };
}

Создайте как 32, так и 64 бит. Ссылка на приложение C # для использования библиотеки.

Теперь вам нужно изменить код, чтобы он создавал новый домен приложения, и запускать весь ваш код в нем (вы также можете создавать типы по умолчанию, но это делает его немного более сложным и потенциально медленным).

Итак, определите класс начальной загрузки для запуска приложения:

using NativeLib;

namespace BitnessTest
{
    class StartClass
    {
        public static void Start()
        {
            NativeClass.DoSomething();
        }
    }
}

Наконец, измените вашу функцию Main на что-то вроде:

using System;
using System.Reflection;

namespace BitnessTest
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;

            if (IntPtr.Size > 4)
            {
                setup.PrivateBinPath = "x64";
            }
            else
            {
                setup.PrivateBinPath = "x86";
            }            

            AppDomain appDomain = AppDomain.CreateDomain("Real Domain", null, setup);
            appDomain.DoCallBack(StartClass.Start);
        }
    }
}

Теперь убедитесь, что вы удалили NativeLib.dll из текущего каталога приложения, создали каталог x86 и x64 и поместили соответствующие версии собственного lib в каждый из них. Запустите его, и теперь он должен работать на 32 и 64 бит.

Если вам не нужен другой домен приложения и вы хотите жить с устаревшим кодом (который может уйти, но все еще находится в .net 4), вы можете сделать:

if (IntPtr.Size > 4)
{
    AppDomain.CurrentDomain.AppendPrivatePath("x64");
}
else
{
    AppDomain.CurrentDomain.AppendPrivatePath("x86");                
}

StartClass.Start();

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

Конечно, если вы хотите, чтобы это была библиотека, вам, возможно, придется пойти со сборкой обвязки загрузки, путаницей с закрытым путем приложения, скажем, статическим конструктором, что может быть не очень вежливым делом;)

0 голосов
/ 16 сентября 2010

Скомпилируйте версии x64 и x86, создайте для них сигнатуры PInvoke отдельно и просто создайте метод для каждого сигма, который вы хотите использовать, который проверяет IntPtr.Size и вызывает правильный pinvoke для текущей битности.

Если я не ошибаюсь, но я верю, что именно так это и можно сделать, я не могу вспомнить, нужен ли вам дополнительный уровень косвенности, когда вы делаете взаимодействие, 32- и 64-битное, с соответствующими сигналами пинвока и отражением загрузите правильный в зависимости от IntPtr.Size вместо того, чтобы соединять сигнаты пинвока в один и тот же двоичный файл.

...