Как вызвать С++/CLI из С#?

У меня есть класс, реализованный на С++, который отвечает за арифметическое вычисление программы и интерфейс с использованием WPF. Я обрабатываю вход с помощью С#, но тогда как я могу использовать свой класс С++?

Я видел несколько комментариев о том, как создать управляемый класс оболочки С++ для взаимодействия с ним, но я не знаю, с чего начать. Я также не знаю, как я собираюсь скомпилировать его вместе со всем другим кодом. Я не могу найти учебник по этому вопросу, а показания google на управляемом С++ действительно не кажутся полезными.

Что-нибудь там, чтобы помочь мне? Мне это не кажется необоснованным.

EDIT Исправлено решение m3rLinEz, но это дало мне исключение BadImageFormatException, я думаю, это потому, что DLL не генерируется. Я сделал все, что сказал, не знаю, что случилось. Любые идеи?

Ответ 1

Посмотрите ли вы на С++/CLI?

Позвольте мне привести очень короткий пример. Вот исходный файл из проекта Visual С++ → CLR → Class Library. Он в основном получает имя пользователя Windows и возвращает его.

Обратите внимание, что для того, чтобы скомпилировать этот файл, вам нужно перейти в настройки проекта и пометить "Дополнительные зависимости" как "Наследовать от родителя", потому что мы используем эти библиотеки Windows (kernel32.lib, user32.lib,..)

// CSCPP.h

#pragma once

#include "windows.h"

using namespace System;

namespace CSCPP {

    public ref class Class1
    {
        // TODO: Add your methods for this class here.
    public:
        String^ GetText(){
            WCHAR acUserName[100];
            DWORD nUserName = sizeof(acUserName);
            if (GetUserName(acUserName, &nUserName)) {
                String^ name = gcnew String(acUserName);
                return String::Format("Hello {0} !", name);
            }else{
                return gcnew String("Error!");
            }
        }
    };
}

Теперь создайте новый проект С# и добавьте ссылку на наш первый проект библиотеки классов С++/CLI. Затем вызовите метод экземпляра.

namespace CSTester
{
    class Program
    {
        static void Main(string[] args)
        {
            CSCPP.Class1 instance = new CSCPP.Class1();
            Console.WriteLine(instance.GetText());
        }
    }
}

Это дало следующий результат на моей машине:

Привет, m3rlinez!

С++/CLI - это, в основном, управляемое расширение по стандарту С++. Он позволяет использовать классы CLR и типы данных в вашем проекте С++/CLI, а также показывать это на управляемом языке. Вы можете создать управляемую оболочку для вашей старой библиотеки С++, используя это. Есть некоторые странные синтаксисы, такие как String^, чтобы определить тип ссылки для CLR String. Я найду "Quick С++/CLI - Learn С++/CLI менее чем за 10 минут" , чтобы быть полезен здесь.

Ответ 2

Существует как минимум три способа вызова неуправляемого кода из управляемого в том же процессе:

  • С++/CLI
  • Вызов платформы
  • Оберните свой С++ в COM-объект

На работе мы используем для этого С++/CLI, он работает.

Ответ 3

Я бы создал стандартную (не COM/управляемую) динамическую библиотеку ссылок как описанную здесь, а затем используйте атрибут DllImport (вызывать платформу) в коде С# для доступа к экспортированным функциям.

Ключевой момент из этой статьи:

Обратите внимание на __declspec (dllexport) модификатора в объявлениях метода в этот код. Эти модификаторы позволяют метод, который будет экспортироваться DLL что он может использоваться другими Приложения. Чтобы получить больше информации, см. dllexport, dllimport.

Это более легкая альтернатива реальной оболочке COM-взаимодействия и позволяет избежать таких проблем, как регистрация и т.д. (DLL можно просто поместить в каталог приложения) .

Другой альтернативой является It Just Works (IJW). Вероятно, это лучший выбор, если вы управляете кодом на С++ и должны обращаться к нему с других языков .NET. Но это только вариант, если вы можете/счастливы преобразовать ваш неуправляемый С++ в управляемый С++, хотя.

Ответ 4

Я бы держался подальше от P/Invoke, поскольку он довольно медленный по сравнению с IJW (It Just Works). Последнее позволяет вам легко переплетать управляемый и неуправляемый С++. Все, что вам нужно сделать, это создать управляемую сборку С++, написать управляемый класс, видимый из С#, и вызвать неуправляемый код из этого.

Эмм... Хорошо. У меня создалось впечатление, что вызовы P/Invoke медленнее, чем они наследуются. Однако, имея явный контроль над сортировкой, вы можете сделать свою версию С++/CLI более эффективной во многих случаях.

Вот статья Microsoft обо всех механизмах:

http://msdn.microsoft.com/en-us/library/ms235282.aspx

Преимущества IJW

  • Нет необходимости писать декларации атрибутов DLLImport для неуправляемые API, используемые программой. Просто включить заголовочный файл и ссылку с библиотеки импорта.
  • Механизм IJW немного быстрее (например, заглушки IJW не работают необходимо проверить необходимость подключения или копировать данные, потому что это сделано явно разработчиком).
  • Это ясно иллюстрирует проблемы с производительностью. В этом случае тот факт, что вы переводите с Unicode string в строку ANSI и что вы иметь соответствующее распределение памяти и освобождение. В этом случае разработчик, записывающий код с использованием IJW будет понимать, что вызов _putws и использование PtrToStringChars было бы лучше для производительности.
  • Если вы вызываете много неуправляемых API-интерфейсов, используя одни и те же данные, один раз и передача маршалированной копии гораздо эффективнее, чем повторное маршалинг каждый раз.

Есть и эстетические преимущества:

  • Код С# выглядит как код С# без какой-либо странной странности.
  • Вам не нужно определять атрибут DLLImport, вам не нужно определять какую-либо структуру данных (также с помощью p/invoke конкретных атрибутов), которая может выглядеть так:

    [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode {   [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 32)]   public string dmDeviceName; }

  • Вам не нужно преобразовывать все примитивы параметров в свои .NET-копии (есть таблица на на этой странице, в которой перечислены, как управляемые типы карта для неуправляемых типов).
  • Вы можете работать с С++/CLI, что очень интересно учиться и действительно отполировано. Он прошел долгий путь со времен VS 2003 и теперь является полнофункциональным языком .NET. Документация Microsoft для этого довольно хороша, как и вся информация IJW.
  • Выполнение С++-взаимодействия в С++/CLI кажется очень естественным, в отличие от С#. Это полностью субъективно, но я бы предпочел сделать строковое сортирование на С++, которые делают Marshal.PtrToString(ptr).
  • Если вы выставляете API, вы, вероятно, захотите обернуть все материалы P/Invoke на другом уровне, так что вам не придется иметь дело с P/Invoke уродством. Таким образом, у вас есть накладные расходы на все маршаллинг и слой С# вокруг него. С С++/CLI маршаллинг и абстракция interop находятся в одном месте, и вы можете выбрать, сколько маршаллинга вам нужно.

IMHO, если вы вызываете нечетную функцию в SDK Windows, переходите к P/Invoke. Если вы подвергли умеренно сложный С++ API управляемому миру, определенно С++/CLI.