Обработка управляемых делегатов в неуправляемом коде

Я знаю, что могу заставить это работать технически, но я хотел бы реализовать самое чистое возможное решение. Вот такая ситуация:

У меня есть управляемая библиотека, которая обертывает неуправляемую библиотеку в стиле 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, который передается от какого-либо клиента моя управляемая библиотека.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
4 388
3

Ответы 3

.NET может автоматически преобразовать делегат в указатель на функцию, если он правильно объявлен. Есть два предостережения

  1. Функция C должна быть построена STDCALL
  2. Указатель на функцию не считается ссылкой на объект, поэтому необходимо организовать сохранение ссылки таким образом, чтобы базовый объект не собирался сборщиком мусора.

http://www.codeproject.com/KB/mcpp/FuncPtrDelegate.aspx?display=Print

Спасибо за второй комментарий. Это, наконец, объясняет, почему я продолжаю получать SEH в случайное время.

galets 25.05.2012 21:35

Мне повезло, что кто-то сказал мне это в первый день работы с C#, поэтому я всегда это знал. Он знал, что я собираюсь написать мост C# / C++.

Lou Franco 25.05.2012 22:06

Если я правильно понимаю проблему, вам необходимо объявить неуправляемую функцию обратного вызова в вашей сборке 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, либо, возможно, обратный вызов получает переменную, предоставленную пользователем, которую вы могли бы использовать.

Так что, по сути, у меня был бы указатель на неуправляемую функцию, вызывающий неуправляемую функцию, тогда у меня была бы эта неуправляемая функция, вызывающая делегат, который был сохранен как переменная-член в моем управляемом классе?

Reddog 04.10.2008 01:41

По сути, да. Вам нужно выяснить, как функция StringCallback обращается к управляемому делегату. Приведенный выше фрагмент просто делает его глобальной переменной. StringCallback также может обрабатывать перевод между типами, например std :: string и String ^

Rob Walker 04.10.2008 03:12

Вы хотите сделать что-то вроде этого:

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);

Другие вопросы по теме