0

I've created a managing application. On of the things my application does, is register on a plugin which can detect whenever or not a fingerprint reader gets plugged in or plugged out. The class subscribes on the event like such:

//Subscribe to the plug, unplug and imageAcquired events from the GrFingerXCtrlClass library.
FingerXCtrlClass.SensorPlug += ReaderPlug;
FingerXCtrlClass.SensorUnplug += ReaderUnplug;
FingerXCtrlClass.ImageAcquired += ImageAcquired;

First when I was actively working on the program, I've developed a WPF application. Through this application I could see some of the lists and switch some settings so I am sure my service works well. In this WPF application, I've created my service by instantiating it:

ProjectServiceLogic logic = new ProjectServiceLogic();

Now I've created an installer. Thus I had a ProjectService-class, initializing the application. This is being done like so:

protected override void OnStart(string[] args)
{
    log.Debug("Starting service...");
    _worker = new Thread(new ThreadStart(StartService));
    _worker.IsBackground = true;
    _worker.Name = "ServiceThread";
    _worker.SetApartmentState(ApartmentState.STA);
    _worker.Start();
    log.Debug("Successfully started service");
}

void StartService()
{
    serviceLogic = new ProjectServiceLogic();
    while (!_shutdownEvent.WaitOne(0))
    {

    }
}

The program gets installed and the service starts. When debugging the service, I notice the subscribing code gets executed. However, the events are not triggered when I plug in a device while it does trigger when running it locally through the WPF application, instantiating the service logic. Why doesn't it work now?

Joetjah
  • 6,292
  • 8
  • 55
  • 90

1 Answers1

1
_worker.SetApartmentState(ApartmentState.STA);

Selecting a Single Threaded Apartment requires you to implement the contract of an STA thread. Just two basic requirements: you can never block the thread and you must pump a message loop. The message loop is essential to allow COM to provide the guarantee that method calls on the COM object are always made from the thread that created the object, thus ensuring thread-safety. Also the mechanism in .NET that makes Control.BeginInvoke and Dispatcher.BeginInvoke work.

A COM component counts on having that guarantee in place, it often relies on the message dispatcher to take care of its own inter-thread marshaling. Like Dispatcher.BeginInvoke does.

Two things go wrong when you don't in fact pump a message loop as required. First of all, as expected, any calls you make on the object from a worker thread will deadlock. COM will use PostMessage to ask the STA thread to dispatch the call. But that won't happen when the thread isn't retrieving messages from the message queue. Second thing that goes wrong is likely what you see happening here, the component itself uses PostMessage to raise events on the STA thread. With the failure mode that the event is never raised. Also classically the way WebBrowser misbehaves, you never get the DocumentCompleted event.

You'll need to pump a message loop, Application.Run(). Either the Winforms or the WPF version of it will do, take your pick. A Winforms example is here

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Sounds reasonable. I think you are right. My goal is to create a windows service application, so `Application.Run()` doesn't apply for me here. The WPF application was only used to help me programming that service application. There is no GUI involved now. How can I still achieve this? – Joetjah May 08 '13 at 11:35
  • Application.Run() doesn't care that it runs in a service or that you don't actually have a GUI. It dispatches Windows messages, that's all. You really do have to use it to get ahead. – Hans Passant May 08 '13 at 11:39
  • Ah I see, I was missing a reference. Is there a way to check if there is a (correct) message loop pumping? I've adapted my code like suggested in the other question, but with no avail. The events still don't get triggered, even though the application subscribed. I'm starting to think I messed up my installation project... – Joetjah May 08 '13 at 11:56
  • I have to say I find the entire approach mystifying. A service cannot interact with the user, there are few things *more* interactive than scanning a fingerprint. How are you going to tell the user that the scan isn't recognized? How do you distinguish between a cat paw and a thumb? This should be a program you start at login. NotifyIcon is a good way to keep it in the background but still permit interaction. – Hans Passant May 08 '13 at 12:13
  • The users that interact with the readers won't be able to understand the interface. Think of their mental state compared to a 3 years old child. The one thing that they have to know, is that a door opens when they press their finger on the scanner. Nevertheless, I dó have a GUI, a WPF application which can (remotely) connect to this service and configure a thing or two. In case of powerdrop or anything, after the computer reboots, it should work immediately again. That's why I've chosen a service over an after-login-start-application. – Joetjah May 08 '13 at 13:26