3

I did write a windows service that can connect to a network device using a dll. so everything works fine, but The event handler does not work in win service! here is my code :

My Custom Class Code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyNewService
{
    public class zkemkeeperHandler
    {
        public event EventHandler OnFinger;
        public event EventHandler<VerifyEventArgs> OnVerify;
        private System.Diagnostics.EventLog eventLog1 = new System.Diagnostics.EventLog();
        public zkemkeeper.CZKEMClass axCZKEM1 = new zkemkeeper.CZKEMClass();
        private bool bIsConnected = false;
        private int iMachineNumber = 1;

        public zkemkeeperHandler()
        {
            ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
            this.eventLog1.Log = "DoDyLog";
            this.eventLog1.Source = "DoDyLogSource";
            ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();

            eventLog1.WriteEntry("zkemkeeperHandler constructor");
        }

        public void startService()
        {
            eventLog1.WriteEntry("start service for (192.168.0.77:4370)");
            bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
            if (bIsConnected == true)
            {
                eventLog1.WriteEntry("bIsConnected == true !");
                iMachineNumber = 1;
                if (axCZKEM1.RegEvent(iMachineNumber, 65535))
                {
                    this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
                    this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
                    //This Log Appears in Event Viewer
                    eventLog1.WriteEntry("Define events (OnFingers and OnVerify) !");
                    //This Line Fires Event in Service1.cs for testing event handler
                    Finger(EventArgs.Empty);
                }
            }
            else
            {
                eventLog1.WriteEntry("Unable to connect the device");
            }
        }

        public void stopService()
        {
            if (bIsConnected) {axCZKEM1.Disconnect(); bIsConnected = false;}
        }

        //This method doesn't run :(
        private void axCZKEM1_OnFinger()
        {
            Finger(EventArgs.Empty);
        }

        //This method doesn't run too :(
        private void axCZKEM1_OnVerify(int iUserID)
        {
            VerifyEventArgs args = new VerifyEventArgs();
            args.UserID = iUserID;
            Verify(args);
        }

        public class VerifyEventArgs : EventArgs
        {
            public int UserID { get; set; }
        }

        protected virtual void Finger(EventArgs e)
        {
            EventHandler handler = OnFinger;
            if (handler != null)
                handler(this, e);
        }

        protected virtual void Verify(VerifyEventArgs e)
        {
            EventHandler<VerifyEventArgs> handler = OnVerify;
            if (handler != null)
                handler(this, e);
        }
    }
}

My Main Service Class Code :

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Linq;
using System.Threading;

namespace MyNewService
{
    public class Service1 : System.ServiceProcess.ServiceBase
    {
        private System.Diagnostics.EventLog eventLog1;
        private System.ComponentModel.Container components = null;
        zkemkeeperHandler zkh;
        public Service1()
        {
            InitializeComponent();

            if (!System.Diagnostics.EventLog.SourceExists("DoDyLogSource"))
            {
                System.Diagnostics.EventLog.CreateEventSource("DoDyLogSource", "DoDyLog");
            } 
            eventLog1.Source = "DoDyLogSource";
            eventLog1.Log = "DoDyLog";

            eventLog1.WriteEntry("Preparing to start service");         
            try
            {
                startZKHandler();
            }
            catch (Exception ex)
            {
                eventLog1.WriteEntry(ex.InnerException.Message);
            }
        }

        private void startZKHandler()
        {
            eventLog1.WriteEntry("creating zkemkeeper handler class");
            zkh = new zkemkeeperHandler();
            zkh.OnFinger += OnFinger;
            zkh.OnVerify += OnVerify;
            zkh.startService();
        }

        private void stopZKHandler()
        {
            eventLog1.WriteEntry("Disconnecting from device (192.168.0.77)...");
            zkh.stopService();
        }

        private void writeLog2DB(string message)
        {
            try
            {
                eventLog1.WriteEntry("writing to database");
                DB.DBase.LogTable.AddObject(new LogTable
                {
                    ID = ++DB.IDCounter,
                    deviceLog = message
                });
                DB.DBase.SaveChanges();
            }
            catch (Exception ex)
            {
                eventLog1.WriteEntry(ex.Message + " - " + ex.InnerException.Message);
            }
            this.EventLog.Log = "Event Stored in DB.";
        }

        // The main entry point for the process
        static void Main()
        {
            System.ServiceProcess.ServiceBase[] ServicesToRun;

            ServicesToRun = new System.ServiceProcess.ServiceBase[] { new MyNewService.Service1()};

            System.ServiceProcess.ServiceBase.Run(ServicesToRun);   
        }

        private void InitializeComponent()
        {
            this.eventLog1 = new System.Diagnostics.EventLog();
            ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();

            this.eventLog1.Log = "DoDyLog";
            this.eventLog1.Source = "DoDyLogSource";

            this.ServiceName = "MyNewService";
            ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();

        }

        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        protected override void OnStart(string[] args)
        {
            // TODO: Add code here to start your service.
            eventLog1.WriteEntry("my service started");
        }

        protected override void OnStop()
        {
            // TODO: Add code here to perform any tear-down necessary to stop your service.
            eventLog1.WriteEntry("my service stoped");
            stopZKHandler();
        }

        protected override void OnContinue()
        {
            eventLog1.WriteEntry("my service is continuing in working");
        }

        private void OnFinger(object sender, EventArgs e)
        {
            eventLog1.WriteEntry("Finger Event Raised");
        }

        private void OnVerify(object sender, zkemkeeperHandler.VerifyEventArgs e)
        {
            eventLog1.WriteEntry("Verify Event Raised");
        }

    }
}

What is my mistake? please help me!

The Windows Service that I wrote, can raise custom events but cannot raise my dll events!

Younes Jafari
  • 251
  • 1
  • 5
  • 17
  • which event? on verify? – Ehsan Aug 18 '13 at 08:27
  • Both of them (OnFinger and OnVerify) – Younes Jafari Aug 18 '13 at 08:33
  • you are not firing the events anywhere in your code – Ehsan Aug 18 '13 at 08:58
  • when do you want your events to fire. in which method of your class zkemkeeperHandler – Ehsan Aug 18 '13 at 09:01
  • axCZKEM1_OnFinger or axCZKEM1_OnVerify functions will launch automatic by external device (When finger placed on finger print device)! This code works fine in WinForm Project! but there is WinService... – Younes Jafari Aug 18 '13 at 09:10
  • What do you mean by "does not work"? Errors, exceptions, nothing happens? – Peter Ritchie Aug 18 '13 at 14:41
  • Services run as a different user--has the software to use this device been installed for use by a specific user? You may have to configure the service to run as that user. – Peter Ritchie Aug 18 '13 at 14:43
  • No Peter! No Errors and Exceptions! I'd use this code in windows form project and events works well! but in the windows service project finger print event does not work! (with no errors or exceptions). for example the axCZKEM1_OnFinger function must fires when a finger placed on FP device. I am admin of my PC and it would not use for specific user! – Younes Jafari Aug 18 '13 at 18:54
  • Hello.... I have also programming a ZK device and facing the same problem. Did you found the solution to this? – jstuardo Aug 28 '13 at 21:25
  • Please check this link : http://social.msdn.microsoft.com/Forums/vstudio/en-US/fcc40890-4955-4328-ace6-08e9ccbabeb8/using-dll-event-handler-in-windows-service-c – Younes Jafari Aug 31 '13 at 06:30

3 Answers3

3

I know this thread is old, but I had this problem yesterday, and now I have finally found a solution, after many hours wasted. The problem is that, the COM object must be created from an STA Thread, and then, for the events to be dispatched correctly, the same STA thread (exactly the same) must be pumping COM messages. This can be done calling Application.DoEvents() in a loop or Application.Run().

So here is my working code (it works, even as a non-interactive Windows Service in Vista+, I am using Windows 8.1)

Thread createComAndMessagePumpThread = new Thread(() =>
{
    this.Device = new CZKEMClass(); //Here create COM object
    Application.Run();
});
createComAndMessagePumpThread.SetApartmentState(ApartmentState.STA);
createComAndMessagePumpThread.Start();

After the device gets created you can register the events from any thread, and they get dispatched by the STA thread, that created the COM object.

In Windows Forms application, this worked without doing this, because the STA main thread run the form calling Application.Run(Form). Application.Run() then dispatch events like COM events and Windows GUI events, so there is no need to to the trick above.

Darxis
  • 1,482
  • 1
  • 17
  • 37
  • Your answer saved my life! I am leaving work now at 3 in the morning! My 2 cents on this though, I added the event registration and the event code within the thread to avoid the hassle of waiting on the thread to finish connecting. Also please note that this device (specially the old ones) are cheap or of low quality so you will many times think that your code is failing however it's just the connection is not going through which can be fixed with a quick power off and on for the device. – Mazen Elkashef Dec 13 '16 at 00:59
  • Your best buddies are "TCPView" to monitor port 4370 and a working win form application like the attendance application or any working windows form application. They helped me narrow down the ambiguity when I was sure that my code was supposed to work. I always check the port 4370 if it's not responding I try connecting using the ZKTeco attendance application if I see no connection on port 4370 (which takes much less time, than the actual connection you see on the UI) then this means that your device needs a quick restart. email mazen.elkashef at gmail [dot] com .. I know it's a hard! – Mazen Elkashef Dec 13 '16 at 01:01
2

Reviving this question as I've just been dealing with a related one. Apparently, the OP is using some COM STA objects which need an STA thread and a functional message pump loop to operate properly. The Windows Service execution model doesn't have that by default. Visit the linked answer for more details.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0

You cannot use events in Windows Service. Exists several causes why not but I would like to offer a solution just for zkemkeeper:
ZK released a zkemkeeper.dll as COM object for working with Windows Application. All device events will fired and not raised in your application when you run it as windows service. Try to add a reference System.Windows.Forms to the project and after successfully connect add row:

Application.Run();
HaGever
  • 91
  • 5