Обратный вызов из Delphi DLL в C # — работает только в idle winform или с помощью app.doevents?

#c# #delphi #service #callback

#c# #delphi #Обслуживание #обратный вызов

Вопрос:

Я сдаюсь, я должен написать и спросить; Я использую неуправляемую DLL, написанную на Delphi, которая асинхронно вызывает (без параметров) обратный вызов, передаваемый ей всякий раз, когда происходит событие на оборудовании, для мониторинга которого она разработана.

Что я делаю в C #, так это сохраняю статическую ссылку на созданный делегат, который затем передается в качестве параметра методу start на стороне Delphi. Это говорит DLL уведомлять меня с помощью обратного вызова всякий раз, когда появляются новые данные для извлечения, используя метод getData.

Все работает нормально, действительно отлично, пока я не попытаюсь сделать то же самое в консольном приложении или в службе Windows. Или если я создаю и вызываю связанные с DLL методы в отдельном потоке, не позволяя этому потоку вращаться, выполняя приложение.DoEvents(). Обычно у людей с подобными проблемами, похоже, возникали проблемы с GC или соглашениями о вызовах, но, (попытавшись) защититься от проблем с GC, сохранив ссылку делегата, я остаюсь без дальнейших подсказок о том, как это решить.

Я предполагаю, что мне не хватает чего-то важного в том, как среда CLR вызывает мои обратные вызовы после того, как DLL вызывает модуль, предоставленный для подключения к моему обработчику обратного вызова.

Объявления моего импорта DLL выглядят следующим образом:

     [DllImport("TheDelphiApi.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    private static extern UInt32 Start(MulticastDelegate callback);

    [DllImport("TheDelphiApi.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    private static extern void GetData(byte[] recdata);

    public delegate void EngineCallbackHandler();                     

    private static EngineCallbackHandler engineCallback;
  

И вызов, настраивающий обратный вызов:

         UInt32 result = Start(engineCallback);
  

Есть подсказки? Зачем нужны DoEvents? Любая обратная связь действительно ценится, пытаюсь исследовать это в течение нескольких дней, без каких-либо указаний на прогресс.
/J

Комментарии:

1. Никаких подсказок с данным кодом. Вы говорите, что для запуска нужна winform. Может быть, коду нужна очередь сообщений winform для работы на стороне Delphi?

2. Не по словам разработчика Delphi, он использует его в службах (а что нет) без проблем.

3. Служба Delphi имеет активную перекачку сообщений.

4. @Marjan Интересно! Будет ли эта перекачка сообщений иметь решающее значение для способности DLL отвечать на обратные вызовы, выполняемые для нее из другой библиотеки DLL, на которую она, в свою очередь, ссылается?

5. Я бы сказал так. Перекачка сообщений в службе Delphi была бы механизмом, с помощью которого может выполняться код в DLL. Смотрите ответ Торстена о двух способах выполнения кода DLL.

Ответ №1:

Трудно сказать, не видя собственный код, но я бы предположил, что код на собственной стороне зависит от цикла обработки сообщений в потоке, который вызывает Start? например, потому что он использует TTimer или аналогичный?

После возврата вызова Start существует только 2 способа выполнения любого кода внутри этой библиотеки DLL. Либо был запущен новый поток, либо был зарегистрирован таймер или какой-либо другой механизм, который зависит от сообщений окна.

Редактировать:

Другая возможность:

Если собственная DLL использует межпроцессный COM в STA (однопоточный модуль), то COM зависит от рабочего цикла обмена сообщениями.

Комментарии:

1. Я выясню, используется ли такой компонент где-нибудь в Delphi DLL. Конечно, звучит как правдоподобное объяснение. Запуск приложения. DoEvents во внутреннем цикле потока в службе решает проблему. У меня создается общее впечатление, что это не очень хороший способ решить эту проблему, но я думаю, что пока это работает.

2. Что ж, если определенной библиотеке требуется рабочий цикл обмена сообщениями, то ей требуется рабочий цикл обмена сообщениями. Это нередкое требование (например, то же самое применимо, если вы используете COM с STA). Но это требование, которое должно быть четко задокументировано.

3. Я понимаю. Здесь не задействован COM. Мой обратный вызов вызывается из библиотеки DLL как ответ на управляемый прерываниями обратный вызов из коммуникационной библиотеки UDP. Итак, насколько я понимаю, не должно быть зависимости от перекачки сообщений. Но я должен признать, что мое понимание того, как все это «цепляется», очень ограничено.

4. Основной вопрос: вы ожидаете, что engineCallback будет вызван в том же потоке, который вызвал Start? Если да, то перекачка сообщений — это единственный способ, которым эта (или любая другая, если уж на то пошло) встроенная библиотека может «взломать» этот поток для вызова вашего обратного вызова. Кроме того, между вашим управляемым кодом и машинным кодом нет COM, но это не гарантирует, что машинный код не использует COM внутри. Если вы готовы согласиться с тем, что обратные вызовы могут выполняться в контексте другого потока, машинный код может запустить собственный поток и выполнять обратные вызовы в этом контексте.

5. Я вижу! Теперь это все объясняет. Мне все равно, что это может быть любой поток, другой был бы предпочтительнее, чем ссылаться на sys.win.forms и запускать app.doevents просто для того, чтобы все заработало. Миллион благодарностей за волшебные слова. Теперь все кажется ясным.