views:

96

answers:

2

I have some commercial equipment that I can connect to with a .Net library supplied by the equipment manufacturer - so I have no control over the library or the equipment, only my code.

The manufacturer has set their system up so that if you are not connected to the equipment via their library then it works fine. However, when you do connect with their library there is an implicit requirement that you service Windows message pump at a rate set by how fast the equipment is running. This is because their library implements an event system that you can subscribe to that tracks operation of the equipment, but they assume that your application code will be WinForms based. (But there is nothing explicitly stating this in their documentation - only that all 2 of their .Net example programs are WinForms based.) I have confirmed with their tech support that the expectation is that you will use a WinForms application.

In my case I am writing a C#, non-WinForms based application (actually a Windows Service, so I do not have a UI thread) and even though I am connecting to the equipment I am not subscribing to any of the events. As a result I have found that I need reference the WinForms assembly and call Application.DoEvents() at a fast enough rate to service all of those events that I do not subscribe to.

So my questions are these:

  1. In this case is calling Application.DoEvents() my only option?
  2. Or is there a more modern way of doing this?
  3. What are the ramifications of calling DoEvents() on a 20mS rate?
  4. Unrelated, but if I wrote a WPF based application would that program be likely to service the message pump?

Edit

I should add that if you connect to the equipment and do not service the windows message pump (even if not subscribed to any of their events), then the equipment falls starts to behave unpredictably.

Edit 2

Also the thread I use to interface to the library is about 2 or 3 generations removed from the initial windows service thread.

+2  A: 

You can have a message pump without forms just call the version of Application.Run() that takes a ApplicationContext or no parameters on a thread.

EDIT: I would recommend the ApplicaitonContext version so you can call ApplicationContext.ExitThread() in the OnStop() of your service.

Scott Chamberlain
Scott - as per my latest edit, I am not servicing this library from the main application thread. Instead it is being done from a dedicated thread I spawn myself. Is the technique you suggest still applicable?
Peter M
Yes, Application.Run() does not need to be run on the main thread, and you can even have more than one per program.
Scott Chamberlain
This is preferable to DoEvents in a loop. The reason being that a "proper" message pump relinquishes control to the OS between messages, so you are not executing code for no reason (there is no message to process). Spawning a thread that does nothing but Application.Run is fine. If you want to be able to terminate that thread non-forcably, you can post a thread message and use a message filter to intercept it and PostQuitMessage.
Tergiver
+3  A: 

This is fine, the usual caveats for DoEvents do not apply here because you don't have a UI. There are no ramifications, the rate is realistic. Application.Run() also pumps the message loop but you'll have a harder time controlling the thread since the call doesn't return. Yes, WPF pumps the message loop too but there's little point in using it since you don't have a UI.

You should initialize the service thread by calling SetApartmentState() to select STA. This ensures that any COM server works properly.

Oh, one caveat that jumps to mind: you do need to do something to prevent the thread from burning 100% core. It is automatic with Application.Run() but not with DoEvents in a 'game loop'. I think you already do since you can specify a 20 msec rate. Otherwise, calling WaitHandle.WaitOne(20) on the service stop request event is the typical approach.

Hans Passant
Hans - as per your caveat I use a custom sleep call that works off a stopwatch (got it off the net somewhere) and sleep for 20ms between DoEvents (as well as a lot of other stuff my code does). My code doesn't need a 20ms response time so I am slightly annoyed that I have to work at this rate.
Peter M
Hans - btw .. the WPF bit was an aside. I was just curious to know how well such a library would interface with more modern systems as some day I may have to build a WPF system using this library
Peter M
Well, if you have no other tasks to perform and *only* need the events then you can stop feeling annoyed and just use Application.Run(). Stopping the service thread is harder, you need a Form object and call its Invoke() method from OnStop() so you can call Application.Exit()
Hans Passant
Oh, and a custom sleep with a stopwatch sounds scarily like a loop that burns core. Just the regular Thread.Sleep() will do the job just fine, especially when you use 15 msec.
Hans Passant
@Hans - don't worry about the stopwatch .. it wraps around thread.sleep as a backup. I have had issues with XP in the past of sleeping for small amounts of time due to the resolution of the XP tick counter only being around 16 ms.
Peter M
That hasn't changed, that's why I recommended 15 msec. You can pinvoke timeSetPeriod() to give it better resolution.
Hans Passant