I need to control a camera from within an ASP.NET core api and all communication is via a pInvoke dll. In the docs it explicitly states
To create a user thread and access the camera from that thread, be sure to execute CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) at the start of the thread and CoUnInitialize() at the end.
e.g
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
CoUninitialize()
My camera service works from a winforms application (STA) however when I move it over to my API, the callback does not fire when events happen. I've tried wrapping the component in an STA thread and setting up an execution loop but callbacks still do not fire.
I think I might need a message pump but am unsure exactly how this should work.
Non working code:
Thread handlerThread;
handlerThread = new Thread(STAThreadLoop);
handlerThread.SetApartmentState(ApartmentState.STA);
handlerThread.Start();
and in the thread loop
void STAThreadLoop()
{
logger.LogInformation("Starting STAThreadLoop...");
lock (handlerThreadLock)
{
handlerSignal.Set();
while (!exitHandlerThreadLoop)
{
Thread.Yield();
Monitor.Wait(handlerThreadLock);
if (handlerThreadAction != null)
{
try
{
handlerThreadAction();
}
catch (Exception ex)
{
logger.LogError(ex, "Error executing action on STA thread: {ThreadName}", Thread.CurrentThread.Name);
}
}
Monitor.Pulse(handlerThreadLock);
}
}
}
and then to create the component
RunSTAAction(() =>
{
handler = new SDKHandler(loggerFactory.CreateLogger<SDKHandler>());
});
and the method to transition to the STA thread
void RunSTAAction(Action action)
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
lock (handlerThreadLock)
{
handlerThreadAction = action;
Monitor.Pulse(handlerThreadLock);
Monitor.Wait(handlerThreadLock);
}
}
else
{
action();
}
}
Update: This is actually fixed, see answer below
I found a way to do this using the excellent answer by Noseratio in this question: StaTaskScheduler and STA thread message pumping
Effectively, we create an instance of the ThreadAffinityTaskScheduler and pass the WaitHelpers.WaitWithMessageLoop as a wait function.
ThreadAffinityTaskScheduler messageScheduler;
messageScheduler = new ThreadAffinityTaskScheduler(3, staThreads: true, waitHelper: WaitHelpers.WaitWithMessageLoop);
messageScheduler.Run(new Action(STAThreadLoop), CancellationToken.None);