В данной статье рассматривается вызов управляемого кода (managed) из неуправлявляемого (unmanaged) и их взаимодействие (без использования COM и PInvoke).
Приведенный ниже способ можно рассматривать как альтернативу методу, описанному пользователем klsaymon.
Ситуация взята из личного опыта. Существует некое приложение (ArchiCAD), взаимодействие с которым осуществляется благодаря библиотекам (plugin'ам). Библиотеку (plugin) можно реализовать только на C\C++. Есть собственное приложение (WindowForms) с бизнес логикой и UI, написанное на C# (managed). Необходимо реализовать возможность запуска приложения (WindowsForms) из библиотеки (plugin'а) и их взаимодейсвия. В данном случае, библиотека на C\C++ (plugin) выступает в роли моста между сторонним приложением (ArchiCAD) и нашим (WindowsForms).
Для простоты, отбросим стороннее приложение и предположим, что вызов осуществляется прямо из unmanaged кода. Т.е. заменим связку приложение-plugin одним unmanaged приложением.
Итоговая схема взаимодействия представлена на данной схеме:

Начнем с WindowsForms приложения.
Предположим, что от стороннего приложения нам нужно получить структуру с данными. Создадим данную структуру, назовем ее Entity. Выставим ComVisible и StructLayout аттрибуты.
С помощью команды "%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\regasm.exe" "$(TargetPath)" /tlb:$(TargetName).tlb /codebase (можно добавить в PostBuild Events) создадим type library (*.tlb) и заимпортим ее в unmanaged C++ приложение.
Создадим интерфейс для взаимодействия со сторонним приложением.
Добавим класс с методом SetExternalApplication с помощью которого можно установить созданный интерфейс для дальнейшей работы с программой (и тут же реализуем небольшой тест результата).
В unmanages/managed C++ библиотеке (bridge) добавим ссылку (reference) на WindowsForms приложение.

Теперь в нашей bridge библиотеке необходимо реализовать интерфейс IExternalApplication. Для этого создадим класс ExternalApplication и реализуем интерфейс.
В конструктор передаем handle unmanaged C\C++ библиотеки (plugin'a).
Реализовываем метод, необходимый для запуска managed приложения и экспортируем функцию StartApplication.
В plugin'е реализовываем функицю для получения структуры Entity и экспортируем ее.
Загружаем bridge библиотеку и запускаем приложение.
Запускаем и проверяем результат.

Результат достигнул.
Исходники на Coogle Code
p.s. исходный код далек от красоты и идеала и лишь отражает логику взаимодействия
все это чудо вертится в одном процессе со всеми вытекающими плюсами
Приведенный ниже способ можно рассматривать как альтернативу методу, описанному пользователем klsaymon.
Ситуация взята из личного опыта. Существует некое приложение (ArchiCAD), взаимодействие с которым осуществляется благодаря библиотекам (plugin'ам). Библиотеку (plugin) можно реализовать только на C\C++. Есть собственное приложение (WindowForms) с бизнес логикой и UI, написанное на C# (managed). Необходимо реализовать возможность запуска приложения (WindowsForms) из библиотеки (plugin'а) и их взаимодейсвия. В данном случае, библиотека на C\C++ (plugin) выступает в роли моста между сторонним приложением (ArchiCAD) и нашим (WindowsForms).
Для простоты, отбросим стороннее приложение и предположим, что вызов осуществляется прямо из unmanaged кода. Т.е. заменим связку приложение-plugin одним unmanaged приложением.
Итоговая схема взаимодействия представлена на данной схеме:

Начнем с WindowsForms приложения.
Предположим, что от стороннего приложения нам нужно получить структуру с данными. Создадим данную структуру, назовем ее Entity. Выставим ComVisible и StructLayout аттрибуты.
[ComVisible(true)] [StructLayout(LayoutKind.Sequential)] public struct Entity { // entity external id public int id; [MarshalAs(UnmanagedType.BStr)] // entity name public string name; }
С помощью команды "%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\regasm.exe" "$(TargetPath)" /tlb:$(TargetName).tlb /codebase (можно добавить в PostBuild Events) создадим type library (*.tlb) и заимпортим ее в unmanaged C++ приложение.
#import ".\\..\\Managed.Csharp\\bin\\Debug\\Managed.Csharp.tlb" no_namespace
Создадим интерфейс для взаимодействия со сторонним приложением.
public interface IExternalApplication { /// <summary> /// Returns the <see cref="Entity"/> instance. /// </summary> /// <returns></returns> Entity GetEntity(int id); }
Добавим класс с методом SetExternalApplication с помощью которого можно установить созданный интерфейс для дальнейшей работы с программой (и тут же реализуем небольшой тест результата).
public class Program { private static IExternalApplication _externalApplication; /// <summary> /// Sets the <see cref="IExternalApplication"/> interface. /// </summary> /// <param name="externalApplication"></param> /// <returns></returns> public static void SetExternalApplication(IExternalApplication externalApplication) { _externalApplication = externalApplication; // test var entity = _externalApplication.GetEntity(1234); MessageBox.Show(string.Format("Id={0}; Name={1}", entity.id, entity.name)); } }
В unmanages/managed C++ библиотеке (bridge) добавим ссылку (reference) на WindowsForms приложение.

Теперь в нашей bridge библиотеке необходимо реализовать интерфейс IExternalApplication. Для этого создадим класс ExternalApplication и реализуем интерфейс.
В конструктор передаем handle unmanaged C\C++ библиотеки (plugin'a).
typedef void* (__cdecl *GetEntityFunc)(int); public ref class ExternalApplication: public IExternalApplication { public: // constructor ExternalApplication(HMODULE win32Handle); virtual Entity GetEntity(int id); private: GetEntityFunc m_GetEntity; }; ExternalApplication::ExternalApplication(HMODULE win32Handle) { // initialize functions m_GetEntity = (GetEntityFunc)GetProcAddress(win32Handle, "GetEntity"); } Entity ExternalApplication::GetEntity(int id) { void *entityPtr = m_GetEntity(id); Entity entity = (Entity)Marshal::PtrToStructure((IntPtr)entityPtr, Entity::typeid); return entity; }
Реализовываем метод, необходимый для запуска managed приложения и экспортируем функцию StartApplication.
// exported functions extern "C" __declspec(dllexport) void StartApplication(HMODULE); void StartApplication(HMODULE win32Handle) { Program::SetExternalApplication(gcnew ExternalApplication(win32Handle)); }
В plugin'е реализовываем функицю для получения структуры Entity и экспортируем ее.
// exported functions extern "C" __declspec(dllexport) void* GetEntity(int); void* GetEntity(int id) { Entity *entity = new Entity(); entity->id = id; entity->name = _bstr_t("I'm entity!").copy(); return entity; }
Загружаем bridge библиотеку и запускаем приложение.
typedef void (__cdecl *StartApplicationFunc)(HMODULE); int _tmain(int argc, _TCHAR* argv[]) { HMODULE bridgeHandle = LoadLibrary(L"UnmanagedManaged.C++.dll"); StartApplicationFunc startApplication = (StartApplicationFunc)GetProcAddress(bridgeHandle, "StartApplication"); HMODULE handle = GetCurrentModule(); startApplication(handle); return 0; }
Запускаем и проверяем результат.

Результат достигнул.
Исходники на Coogle Code
p.s. исходный код далек от красоты и идеала и лишь отражает логику взаимодействия
все это чудо вертится в одном процессе со всеми вытекающими плюсами