Наш пример будет состоять из трех проектов:
- MixedLibrary — C++/CLI
- SimpleLibrary — C#
- Win32App — C++
Начнем с самого простого — SimpleLibrary. Эта библиотека содержит один простой сервис, который складывает два числа и выводит результат в консоль:
public class Service
{
public void Add(Int32 a, Int32 b)
{
Console.WriteLine("Hello from Managed Code!");
Console.WriteLine(String.Format("Result: {0}", a + b));
}
}
Теперь перейдем к библиотеке MixedLibrary. Эта библиотека содержит в себе класс-обертку над нашим SimpleService. Содержимое файла CppService.h:
// Директивы препроцессора нужны, чтобы компилятор сгенерировал записи
// об экспорте класса из библиотеки
#ifdef INSIDE_MANAGED_CODE
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif
namespace MixedLibrary
{
class DECLSPECIFIER CppService
{
public:
CppService();
virtual ~CppService();
public:
void Add(int a, int b);
private:
void * m_impl;
};
}
И содержимое файла CppService.cpp:
#include "CppService.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace SimpleLibrary;
namespace MixedLibrary
{
CppService::CppService()
{
Service^ service = gcnew Service();
m_impl = GCHandle::ToIntPtr(GCHandle::Alloc(service)).ToPointer();
}
CppService::~CppService()
{
GCHandle handle = GCHandle::FromIntPtr(IntPtr(m_impl));
handle.Free();
}
void CppService::Add(int a, int b)
{
GCHandle handle = GCHandle::FromIntPtr(IntPtr(m_impl));
Service^ service = safe_cast<Service^>(handle.Target);
service->Add(a, b);
}
}
Также для компилируемости необходимо добавить директиву препроцессора INSIDE_MANAGED_CODE:
И последний штрих — наше обычное неуправляемое приложение:
#include "stdafx.h"
#pragma comment(lib, "../Debug/MixedLibrary.lib")
#include <iostream>
#include "../MixedLibrary/CppService.h"
using namespace std;
using namespace MixedLibrary;
int main()
{
CppService* service = new CppService();
service->Add(5, 6);
cout << "press any key..." << endl;
getchar();
}
И, конечно же, результат:
Автор: nikitam
Комментарии (44)
Videoman
14.08.2017 23:04+1А можно поинтересоваться, а где удаляется вот этот объект?
CppService* service = new CppService();
nikitam
15.08.2017 09:22-1В данном примере явно нигде. Но это всего лишь пример, а не рабочее приложение. При освобождении ссылку на управляемый объект также освобождаем:
CppService::~CppService()
{
GCHandle handle = GCHandle::FromIntPtr(IntPtr(m_impl));
handle.Free();
}
maisvendoo
15.08.2017 07:13Вот этот кусок
// Директивы препроцессора нужны, чтобы компилятор сгенерировал записи // об экспорте класса из библиотеки #ifdef INSIDE_MANAGED_CODE # define DECLSPECIFIER __declspec(dllexport) # define EXPIMP_TEMPLATE #else # define DECLSPECIFIER __declspec(dllimport) # define EXPIMP_TEMPLATE extern #endif
выглядит не слишком кроссплатформенноmayorovp
15.08.2017 08:40О какой кроссплатформенности может идти речь, когда один из проектов написан на c++/cli?
vasily-v-ryabov
15.08.2017 09:51+1Честно говоря, напоминает студенческое упражнение (даже на лабу не тянет). И ради этого писать статью? Ладно, если бы .NET движок поднять в соседнем процессе, который сугубо нативный, и в нем запустить какой-то код.
nikitam
15.08.2017 10:23+1При написании статьи никто и не претендовал на диссертацию! Это просто отправная точка одного из подходов (о чем и сказано в начале), и зачастую именно простейших примеров и не хватает людям, которые только начали разбираться
Xitsa
15.08.2017 11:35+1Пара советов из моего опыта поддержки большого смешанного проекта:
- в области C++/CLI надо находиться как можно меньше, слой совместимости между нативным и управляемым кодом должен быть как можно тоньше
- если объектная система или процесс взаимодействия не тривиален, то COM — оправданный выбор для организации взаимодействия. Хотя я бы воздержался от автоматической активации через реестр или манифесты, они приносят гораздо больше проблем, чем кажущейся пользы.
- вопрос управления ресурсами должен быть обязательно продуман, чтобы большие куски памяти или того хуже соединения не зависали непонятно где.
nckma
У меня от фразы «неуправляемое приложение» мурашки.
jehy
Аналогично. Managed и unmanaged гораздо понятнее. А если русифицировать, тогда уж вместо
Так же для для преобразования в машинный нулёво-еденичковый код необходимо добавить указание преобработчика В_УПРАВЛЯЕМОМ_КОДЕ.
nckma
Именно это я и хотел сказать.