2016-03-04 2 views
3

Итак, я пишу плагин DLL, который является чистым C (и кучей иностранных включает), но большая часть реального кода находится в существующей библиотеке классов C#. Я ищу кратчайший путь от C (не C++) до C#. Иностранные объекты не являются безопасными для C++.Является ли смешанный режим gcroot и/clr и C++-оболочкой кратчайший путь от чистого C до C#?

Есть куча образцов вокруг для C++, не так много для чистого C.

Кажется, я должен быть в состоянии собрать весь DLL в/CLR, но скомпилировать C не; затем включить в ту же DLL C++-оболочку, которая представляет C API, но содержит управляемый код для вызова класса C#.

Итак, создайте экземпляр класса C# и вставьте его в gcroot в классе C++ и передайте указатель класса C++ в виде void * back для кода C, который нужно сохранить для будущих вызовов.

Достаточно немного деталей, чтобы получить право, но не так много кода. Есть ли способ лучше?


Думал, что пришло время добавить немного кода.

// Wrapper.h 
#pragma once 
// API for call by C 
#ifdef __cplusplus 
extern "C" { 
#endif 
    void* wrap_create(); 
    void wrap_doit(void* wrapper, char* input, char* output, int maxlen); 
#ifdef __cplusplus 
} 
#endif 

// Wrapper.cpp 

#define _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h> 
#include <vcclr.h> 
#include "Wrapper.h" 
using namespace System; 

class Wrapper { 
public: 
    gcroot<Wrappee::Evaluator^> eval; 
    Wrapper() {} 
}; 

void* wrap_create() { 
    Wrapper* w = new Wrapper(); 
    w->eval = gcnew Wrappee::Evaluator(); 
    return w; 
} 

void wrap_doit(void* wrapper, char* input, char* output, int maxlen) { 
    Wrapper* w = (Wrapper*)wrapper; 
    String^ s = w->eval->Doit(gcnew String(input)); 
    pin_ptr<const wchar_t> wch = PtrToStringChars(s); 
    wcstombs(output, wch, maxlen); 
} 

// Wrappee.cs 
using System; 
namespace Wrappee { 
    public class Evaluator { 
    string _s; 
    public static Evaluator Create() { 
     return new Evaluator { 
     _s = "wrapped evaluator" 
     }; 
    } 

    public string Doit(string s) { 
     return _s + ":" + s; 
    } 
    } 
} 

Почему бы не так работать? Код базируется на этой ссылке: https://msdn.microsoft.com/EN-US/library/481fa11f%28v=VS.140,d=hv.2%29.aspx.


+0

Прямой PInvoke нативного кода может быть проще, если интерфейс библиотеки C прост. –

+0

@AlexeiLevenkov: Первое, о чем я думал. Но код C пугающе сложный, полный макросов, бит-масок, глобальных переменных. Абсолютно никаких шансов. –

+0

Вам нужно погладить мысль, что это будет легко. Это «обратный pinvoke», собственный код, который вызывает код C#, должен иметь дело с деталями, которые CLR необходимо загрузить и инициализировать. И самое главное, что управляемое исключение диагностируется и не разбивает хост-модуль. Только COM может выполнить эту работу правильно. Либо написав адаптер на C#, который использует [ComVisible (true)], либо используя пользовательские интерфейсы хоста CLR, ICLRMetaHost является основным. Это хорошо работает. –

ответ

2

Ответ нет, это не сработает. Управляемый класс зависит от среды выполнения CLR, которая должна быть размещена приложением. Для управляемого приложения, которое происходит автоматически (mscoree.dll во время запуска), но для собственного приложения нет хоста, поэтому нет CLR.

Поэтому мы должны предоставить один. Как пояснил @hanspassant, это «Reverse P/Invoke», и это действительно так. Вы должны попасть туда через интерфейсы хоста COM, в частности ICLRMetaHost.

И хорошая новость заключается в том, что здесь есть образец, чтобы показать, как это делается: https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0.

Другие образцы тоже: поиск CppHostCLR.