Я знаю, что могу заставить это работать технически, но я хотел бы реализовать самое чистое возможное решение. Вот такая ситуация:
У меня есть управляемая библиотека, которая обертывает неуправляемую библиотеку в стиле C. Функциональность библиотеки в стиле C, которую я сейчас оборачиваю, выполняет некоторую обработку, включающую список строк. Клиентский код библиотеки может предоставить делегата, так что во время обработки списка, если встречается «недопустимый» сценарий, библиотека может выполнить обратный вызов клиенту через этот делегат и позволить им выбрать стратегию для использования (выбросить исключение, заменить недопустимые символы и т. д.)
В идеале я бы хотел иметь весь управляемый C++, изолированный в одной функции, а затем иметь возможность вызывать отдельную функцию, которая принимает только неуправляемые параметры, чтобы весь собственный C++ и неуправляемый код были изолированы в одной точке. Предоставление механизма обратного вызова этому неуправляемому коду оказалось для меня камнем преткновения.
#pragma managed
public delegate string InvalidStringFilter(int lineNumber, string text);
...
public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter)
{
// Managed code goes here, translate parameters etc.
}
#pragma unmanaged
// This should be the only function that actually touches the C-library directly
std::vector<NativeResult> ProcessList(std::vector<char*> list, ?? callback);
В этом фрагменте я хочу сохранить весь доступ к C-библиотеке в ProcessList, но во время обработки он должен будет выполнять обратные вызовы, и этот обратный вызов предоставляется в форме делегата InvalidStringFilter, который передается от какого-либо клиента моя управляемая библиотека.





.NET может автоматически преобразовать делегат в указатель на функцию, если он правильно объявлен. Есть два предостережения
http://www.codeproject.com/KB/mcpp/FuncPtrDelegate.aspx?display=Print
Мне повезло, что кто-то сказал мне это в первый день работы с C#, поэтому я всегда это знал. Он знал, что я собираюсь написать мост C# / C++.
Если я правильно понимаю проблему, вам необходимо объявить неуправляемую функцию обратного вызова в вашей сборке C++ / CLI, которая действует как мост между вашей библиотекой C и управляемым делегатом.
#pragma managed
public delegate string InvalidStringFilter(int lineNumber, string text);
...
static InvalidStringFilter sFilter;
public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter)
{
// Managed code goes here, translate parameters etc.
SFilter = filter;
}
#pragma unmanaged
void StringCallback(???)
{
sFilter(????);
}
// This should be the only function that actually touches the C-library directly
std::vector<NativeResult> ProcessList(std::vector<char*> list, StringCallback);
В написанном виде этот код явно не является потокобезопасным. Если вам нужна потокобезопасность, потребуется какой-то другой механизм для поиска правильного управляемого делегата в обратном вызове, либо ThreadStatic, либо, возможно, обратный вызов получает переменную, предоставленную пользователем, которую вы могли бы использовать.
Так что, по сути, у меня был бы указатель на неуправляемую функцию, вызывающий неуправляемую функцию, тогда у меня была бы эта неуправляемая функция, вызывающая делегат, который был сохранен как переменная-член в моем управляемом классе?
По сути, да. Вам нужно выяснить, как функция StringCallback обращается к управляемому делегату. Приведенный выше фрагмент просто делает его глобальной переменной. StringCallback также может обрабатывать перевод между типами, например std :: string и String ^
Вы хотите сделать что-то вроде этого:
typedef void (__stdcall *w_InvalidStringFilter) (int lineNumber, string message);
GCHandle handle = GCHandle::Alloc(InvalidStringFilter);
w_InvalidStringFilter callback =
static_cast<w_InvalidStringFilter>(
Marshal::GetFunctionPointerForDelegate(InvalidStringFilter).ToPointer()
);
std::vector<NativeResult> res = ProcessList(list, callback);
Спасибо за второй комментарий. Это, наконец, объясняет, почему я продолжаю получать SEH в случайное время.