gushie Posted December 24, 2004 Posted December 24, 2004 I have a C# library which calls TAPI2 Win32 methods, declared using DllImport (tapi32.dll). One of these routines is passed a delegate, which should then be frequently called by Tapi. If I use my library from a Windows Form application, then all works well and my callback is successfully called. However, if I use it from a Windows service, then although everything else works, my delegate never gets called. I have searched for reasons why this may be the case, and found that the ApartmentState may have something to do with it, but adding a [sTAThread] before the Main() function in the service hasn't made any difference. Does anyone know anything else I may need to do? Thanks, Jonathan Corwin. Quote
Administrators PlausiblyDamp Posted December 24, 2004 Administrators Posted December 24, 2004 Could you post the relevant code? Or at least give afew more details about what the callback function does. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
gushie Posted December 24, 2004 Author Posted December 24, 2004 Could you post the relevant code? Or at least give afew more details about what the callback function does. Yes, here is the code. I was just initially checking there wasn't anything "obvious" that I may not have done. (I'm relatively new to .Net) Relevant bits from the service: namespace CtiServerCS { public class Service1 : System.ServiceProcess.ServiceBase { BMSTapi.Tapi t; BMSTapi.NetworkUserCollection u; BMSTapi.NetworkListener nl; private System.ComponentModel.Container components = null; public Service1() { InitializeComponent(); } [sTAThread] static void Main() { System.ServiceProcess.ServiceBase[] ServicesToRun; ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service1() }; System.ServiceProcess.ServiceBase.Run(ServicesToRun); } protected override void OnStart(string[] args) { t = new BMSTapi.Tapi(); u = new BMSTapi.NetworkUserCollection(); nl = new BMSTapi.NetworkListener(t, u); nl.Start(); } ... } } And relevant bits from the BMSTapi library: namespace BMSTapi { public class Tapi { [structLayout(LayoutKind.Sequential)] public struct LineInitializeExParams { public uint dwTotalSize; public uint dwNeededSize; public uint dwUsedSize; public uint dwOptions; public System.IntPtr hEvent; public System.IntPtr hCompletionPort; public uint dwCompletionKey; } private System.IntPtr _hTapi; private LineCallBackFunc _hCallBackFunc; public delegate void LineCallBackFunc(uint hDevice, uint dwMsg, uint dwCallbackInstance, uint dwParam1, uint dwParam2, uint dwParam3); [DllImport("Tapi32.dll", EntryPoint="lineInitializeExW", CharSet=CharSet.Auto, SetLastError=true)] internal static extern LineErrReturn lineInitializeEx( out IntPtr hLineApp, IntPtr hAppHandle, LineCallBackFunc lCallBack, string FriendlyAppName, out uint NumDevices, ref uint APIVersion, ref LineInitializeExParams lineExInitParams); public TapiApi.LineCallBackFunc CallBackFunc { get { return _hCallBackFunc; } } public Tapi() { InitialiseTapi(); } public void LineEventHandler(uint dwDevice, uint dwMsg, uint dwCallbackInstance, uint dwParam1, uint dwParam2, uint dwParam3) { // Code that never ever gets reached from the service, but does from the form } private void InitialiseTapi() { LineErrReturn ret; uint apiVers = TapiConstants.ApiHighVers; LineInitializeExParams tapiParams = new LineInitializeExParams(); tapiParams.dwTotalSize = (uint) Marshal.SizeOf(tapiParams); tapiParams.dwNeededSize = tapiParams.dwTotalSize; tapiParams.dwUsedSize = tapiParams.dwUsedSize; tapiParams.dwOptions = (uint)LineInitializeExOptions.UseHiddenWindow; tapiParams.hEvent = System.IntPtr.Zero; tapiParams.hEvent = System.IntPtr.Zero; tapiParams.hCompletionPort = System.IntPtr.Zero; _hCallBackFunc = new TapiApi.LineCallBackFunc(LineEventHandler); ret = lineInitializeEx(out _hTapi, System.IntPtr.Zero, _hCallBackFunc, "BMSTAPI", out _lineCnt, ref apiVers, ref tapiParams); } } public class NetworkListener { private TcpListener _netListen; private System.Timers.Timer _netClientTimer; private Tapi _tapi; private NetworkUserCollection _users; private NetworkClientCollection _netClients = new NetworkClientCollection(); public NetworkListener(Tapi tapi, NetworkUserCollection users) { _netListen = new TcpListener(IPAddress.Parse("0.0.0.0"), 33334); _netClientTimer = new System.Timers.Timer(); _netClientTimer.Interval = 500; _netClientTimer.Elapsed+=new ElapsedEventHandler(netClientTimer_Elapsed); } public void Start() { _netListen.Start(); _netClientTimer.Start(); _tapi.EventMessage("Started"); } private void netClientTimer_Elapsed(object sender, ElapsedEventArgs e) { TcpClient client; NetworkClient netClient; if(_netListen.Pending()) { client = _netListen.AcceptTcpClient(); netClient = new NetworkClient(client, _tapi, _users); _netClients.Add(netClient); Thread t = new Thread(new ThreadStart(netClient.Start)); t.ApartmentState = ApartmentState.STA; //Added to see if this made a difference t.Start(); } } } } Quote
*Gurus* Derek Stone Posted December 24, 2004 *Gurus* Posted December 24, 2004 Make sure that the garbage collector isn't disposing your delegate prior to the callback function being invoked by TAPI. You might need to call [msdn=System.GC.KeepAlive]GC.KeepAlive()[/msdn] on the object. Quote Posting Guidelines
gushie Posted January 4, 2005 Author Posted January 4, 2005 The problem ended up being down to me not reading the Tapi documentation properly, so apologies for taking up your time. For anyone who may be interested: I was calling lineInitializeEx with a parameter USEHIDDENWINDOW, which tells the routine to use a hidden window for its event handling. As no message queue exists in a windows service, the messages didn't get very far. Using the USEEVENT parameter instead, and using the lineGetMessage routine to get the messages rather than a delegate solved the problem. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.