1

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

John B
  • 1,129
  • 14
  • 23
  • 1
    `I've tried wrapping the component in an STA thread` Please show us the code you used to do that. – mjwills Feb 25 '18 at 09:44
  • Please try these articles, [COM Component Compatibility](https://msdn.microsoft.com/en-us/library/zwk9h2kb.aspx), [Creating STA COM compatible ASP.NET Applications](https://weblog.west-wind.com/posts/2012/Sep/18/Creating-STA-COM-compatible-ASPNET-Applications), [ASP.NET and STA COM objects](https://stackoverflow.com/questions/1342160/asp-net-and-sta-com-objects). – kunif Feb 25 '18 at 12:03
  • Probably you are right about the message loop not been process. I don't know if this is good approach, but you can start the exe of the winforms application from your web api, if this is suitable using the Process class from System.Diagnostics – vasil oreshenski Feb 25 '18 at 17:58

1 Answers1

2

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);
John B
  • 1,129
  • 14
  • 23