Я загружаю сборку C# через COM из неуправляемого C ++, сборка обращается обратно в неуправляемый C ++ через интерфейс обратного вызова. Приложение C ++ использует сообщение Windows l oop и STA.
Интерфейсы определены в C#:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("...")]
public interface IMyInterface
{
void Start(ICallback callback);
void Stop();
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("...")]
public interface ICallback
{
void Message(string message);
}
В C ++ предоставляется реализация ICallback
, а IMyInterface
реализован в C#. Он запускает фон l oop, который периодически обращается к C ++ через обратный вызов, затем возвращает, оставляя l oop работающим:
[Guid("...")]
public class MyInterface : IMyInterface
{
ICallback callback;
public void Start(ICallback callback)
{
this.callback = callback;
CallCallback("Test 1");
StartAsyncMethod(); //deliberately doesn't await.
}
void CallCallback(string message)
{
Thread thread = Thread.CurrentThread;
Console.WriteLine($"'{message}'. Thread - Background: {thread.IsBackground}, Thread Pool: {thread.IsThreadPoolThread}, Thread ID: {thread.ManagedThreadId}");
callback.Message(message);
}
async Task StartAsyncMethod()
{
var i=2;
while(true)
{
await Task.Delay(5000);
CallCallback($"Test {i++}");
}
}
Все работает, за исключением того, что иногда вызывает метод обратного вызова stall - код C# остается в этой строке навсегда, метод C ++ никогда не запускается. Похоже, что виноваты вызовы метода async
, поэтому я добавил запись в журнал деталей потока, мой вывод программы выглядит следующим образом:
'Test 1'. Тема - Фон: True, Пул потоков: False, ID потока: 3
'Test 2'. Тема - Фон: True, Пул потоков: True, ID потока: 6
Вызов callback.Message(message);
останавливается, связанный со второй строкой выше, при вызове из фона l oop. Мой реальный код более сложен, но все обратные вызовы, сделанные непосредственно из Start
, работают нормально; в тот момент, когда фон l oop попытался вызвать остановку кода C# (и код C ++ продолжает работать нормально, он просто никогда не получает обратный вызов).
Возможно ли, что поток пула потоков является как-то это вызывает? Ясно, что я использую его, чтобы получить доступ к асинхронной парадигме C#, но это долгоживущий Task
, выполняющий программную жизнь l oop (которую я, очевидно, значительно упростил для этого вопроса). Что я мог попытаться решить, потому что я застрял на этом этапе!
Finer Detail
Приложение C ++ общается с другими приложениями C ++ через COM. Пока он этого не сделает - или если я не буду этого делать, мои обратные вызовы будут успешными . Сообщение oop довольно стандартно: MSG msg;
while (MsgWaitForMultipleObjects(1, &sm_hShutdown, FALSE, INFINITE, QS_ALLEVENTS) > WAIT_OBJECT_0)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
C ++ использует CGlobalInterfaceTable
и basi c COM, например, CoCreateInstance(ClassGUID, nullptr, CLSCTX_SERVER, GUIDIMyInterface, &pI);
- ничего особенного.
Вдобавок , другая сборка C# COM использует тот же принцип c, но использует устаревшие потоки и не ожидающий TPL, и в ней нет этой проблемы.
** Обновление **
Итак, я только что попробовал это без каких-либо материалов типа TPL
public void Start(ICallback callback)
{
this.callback = callback;
CallCallback("Test 1");
var thread = new Thread(() =>
{
while (true)
{
Thread.Sleep(5000);
CallCallback("Thread Test");
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Точно такая же проблема ... CallCallback
зависает в потоке STA.
Гипотеза заключается в том, что я звоню обратно в COM из другого потока, который не владеет объектом, но создание потока STA означает, что я просто использую поток different
STA. Поскольку это библиотека без точки входа, как может Я использую вызывающий поток для обратного вызова ... этот поток запускает Start
, а затем исчезает.