С задачей вызова неуправляемого кода из управляемого мы сталкиваемся довольно часто, и эта задача имеет простое решение в виде одного атрибута [DllImport] и небольшого набора дополнительных правил, которые хорошо изложены в MSDN. Обратная же задача встречается гораздо реже. В данной статье мы и рассмотрим небольшой пример, как это можно сделать. Его не стоит рассматривать как исчерпывающий, скорее лишь, как направление хода мыслей и концепцию. Итак, начнем.
Наш пример будет состоять из трех проектов:
- 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
Автор: ICL Services