Как мне вызвать C ++ / CLI из C #? - PullRequest
64 голосов
/ 06 февраля 2010

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

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

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

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

Ответы [ 4 ]

56 голосов
/ 06 февраля 2010

Вы смотрели на C ++ / CLI?

Позвольте привести очень короткий пример. Вот исходный файл из проекта Visual C ++ -> 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!");
            }
        }
    };
}

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

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

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

Здравствуйте, m3rlinez!

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

9 голосов
/ 06 февраля 2010

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

  1. C ++ / CLI
  2. Платформа Invoke
  3. Оберните ваш C ++ в COM-объект

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

4 голосов
/ 06 февраля 2010

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

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

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

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

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

3 голосов
/ 06 февраля 2010

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

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

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

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

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

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

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

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

    [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)] публичная структура DevMode { [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 32)] публичная строка dmDeviceName; }

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

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

...