views:

65

answers:

2

I'm developing a VB.NET Windows service which is costantly checking a BMS database for new alarms and making phone calls to warn operators remotely. I'm currently using a .NET TAPI wrapper and the SAPI 5.3 interop assembly, and the steps I'm taking to speak over the phone are:

  1. Initialize TAPI interface;
  2. Make the call to the operator's number;
  3. Take the voice modem waveOut device id
  4. Set the voice output to that device id;
  5. Let SAPI do the magic.

This is all working fine if I compile the code as a Windows console or forms application, but as soon as I compile and run it as a Windows Service, step 5 never occurs - voice modem calls but stays silent; everything else works, even digit monitoring. Now I know windows services are not supposed to interact with the desktop, so things like UI elements and playing sounds are a no-no, but I'm struggling to find an alternative which doesn't involve using the wave API and can be developed in a reasonable timeframe. Any ideas?

A: 

SAPI typically needs some sort of message pump and a window handle, which Windows services typically don't have. You could create a message-only window to handle the messages in a slightly safer manner (note that any hwnd in a service will open you up to a shatter attack, so make sure you're running your service as a user, and not as SYSTEM or LocalMachine to reduce the attack surface). You'll also need to pump the messages (via Application.Run or Application.DoEvents or the like) as well.

Eric Brown
The service now creates a message-only window at startup, as you suggested. What do I do now? How do I tell SAPI to use that window's handle? Do I start the message loop as soon as the window is created or at a later time? Thanks for your help.
Samuele
Typically you need to start a thread that does nothing but pump messages (and has an event to know when to shut down). SAPI is free-threaded, so you can pass SAPI interface pointers across threads without problems.
Eric Brown
A: 

I ended up referencing the windows forms project in my Windows Service project, and creating the form in the OnStart sub:

Protected Overrides Sub OnStart(ByVal args() As String)
        HelperThread = New Threading.Thread(AddressOf CreateMOWindow)
        HelperThread.TrySetApartmentState(Threading.ApartmentState.STA)
        HelperThread.Start()
End Sub 

Private Sub CreateMOWindow()
        Dim frm As New AlarmVoiceTest.AlarmVoiceTest
        Windows.Forms.Application.Run(frm)
End Sub

The form isn't showing up of course (but I don't need it to), and SAPI is working as expected. I reckon this is not the cleanest way to accomplish that, but I'm kinda running out of time :)

Samuele