ToXiC
ToXiC

Reputation: 21

C# windows service issues

I wrote a C# app which takes screenshot every 5 mins a saves it to server. It is timer, 2 threads, few methods(ping srv, check folder, take screenshot etc.).

As process (exe) it runs great, but I need to install it as service. I am installing it trough installutil (Framework service installer).

My problem is, that when it is installed as service, it doesn't take screenshots. Pull out some, when stopping the service. Not the right resolution and black.

I assume, that the executive code is misplaced (see main). I don't know where to put it, since I cannot have more than one main method. Please help.

Code of main application:

I've deleted some not important code. 

    using System;
    using System.Threading;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Windows.Forms;
    //using System.Timers;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Net.NetworkInformation;
    using System.ComponentModel;
    using System.Configuration.Install;

    namespace LogWriterService
    {
        static class Program
        {
            public static int TimeO = 5;  // zpoždění časovače v minutách
            private static bool Online;
            private static bool active = false;

            public static String GetIP()
            {
                // ...  
                // returns IP like xxx.xxx.xxx.xxx
                // ...   
            }

            // Test dostupnosti serveru
            public static bool PingTest()
            {
                // ... 
                // return true if server is reachable
                // ... 
            }


            /*
             * Z Windows.Forms _ screenů získá obrazová data, která uloží na
             * server jako ../[IP]/[současný_systémový_čas].jpg
             */
            public static void  ScreenShot()        //Bitmap
            {
                Int64 CurrTime = Int64.Parse(DateTime.Now.ToString("yyyyMMddhhmmss")); //yyyyMMddhhmmss

                Rectangle bounds = Rectangle.Empty;

                foreach (Screen s in Screen.AllScreens)
                    bounds = Rectangle.Union(bounds, s.Bounds);

                Bitmap screenShotBMP = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);   // PixelFormat.Format32bppArgb

                Graphics screenShotGraphics = Graphics.FromImage(screenShotBMP);

                screenShotGraphics.CopyFromScreen(bounds.X, bounds.Y,
                    0, 0, bounds.Size, CopyPixelOperation.SourceCopy);

                string path = null;  //"D:/TEMP/" + CurrTime + ".jpg";  // GetIP()

                // Ukládání obrázků do dočasné složky a přesun, pokud se připojí
                if (PingTest() == true)
                {
                    path = "//10.0.0.10/Upload/screen/test/" + GetIP() + "/" + CurrTime + ".jpg";
                    string path2 = "//10.0.0.10/Upload/screen/test/" + GetIP() + "/";
                    Online = true;
                    if (Directory.Exists(path2))
                    {
                        MoveCached();
                    }
                    else
                    {
                        Console.WriteLine("Online slozka neni dostupna.");
                    }
                } else {
                    Console.WriteLine("Caching .. ");
                    path = "C:/TEMP/" + CurrTime + ".jpg";  // "C:/TEMP/" + GetIP() + "/" + CurrTime + ".jpg"

                    string LPath = @"c:\TEMP";
                    if (!Directory.Exists(LPath))
                    {
                        DirectoryInfo di = Directory.CreateDirectory(LPath);
                        di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;

                        Console.WriteLine("Lokalni slozka neexistuje. Vytvarim ..");
                    }
                    Online = false;
                }

                screenShotBMP.Save(path, ImageFormat.Jpeg); // C:\\test\\test.jpg        
                screenShotGraphics.Dispose();
                screenShotBMP.Dispose();

                return; //screenShotBMP;
            }


            /*
             * Přesune cache soubory (za dobu offline) na server
             */
            public static void MoveCached()
            {
                // ... 
                // after conect, move localy saved screenshots to server
                // ... 
            }

            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            static void Main()
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] 
                { 
                    new Service1() 
                };
                ServiceBase.Run(ServicesToRun);


                // Vytvoří událost signalizující hranici odpočtu ve  
                // zpětném volání časovače
                AutoResetEvent autoEvent = new AutoResetEvent(false);

                // Počet průchodů timeru
                StatusChecker statusChecker = new StatusChecker(Timeout.Infinite); //  1440

                // Vytvoří odvozeného delegáta, který vyvolá metody pro časovač
                TimerCallback tcb = statusChecker.CheckStatus;

                // Create a timer that signals the delegate to invoke 
                // CheckStatus after one second, and every 1/4 second 
                // thereafter.
                System.Threading.Timer stateTimer = new System.Threading.Timer(tcb, autoEvent, 1000, TimeO * 1000); // TimeO * 1000 * 60 * 12 * 5, 250

                // When autoEvent signals, change the period to every
                // 1/2 second.
                autoEvent.WaitOne(15000, false);
                stateTimer.Change(0, TimeO * 1000 * 60);        // TimeO * 1000 * 60
                Console.WriteLine("menim poprve..");

                // When autoEvent signals the second time, dispose of 
                // the timer.
                autoEvent.WaitOne(Timeout.Infinite, false);
                stateTimer.Change(0, TimeO * 1000 * 60);        // TimeO * 1000 * 60
                Console.WriteLine("menim podruhe..");
                //stateTimer.Dispose();


                // Garbage collector
                GC.Collect();
                GC.WaitForPendingFinalizers();

            }
        }
        }

        class StatusChecker
        {
        private int invokeCount;
        private int maxCount;
        Int64 CurrTime = Int64.Parse(DateTime.Now.ToString("hh"));  //  screeny od 6:00 do 16:00

        public StatusChecker(int count)
        {
            invokeCount = 0;
            maxCount = count;
        }

        // Tato metoda je volána delegátem časovače
        public void CheckStatus(Object stateInfo)
        {
            AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;

            //if ((CurrTime > 6) & (CurrTime < 16))  // 16
            //{
                LogWriterService.Program.ScreenShot();
                Console.WriteLine("ScreenShot ..");
            //}
            Console.WriteLine("{0} Kontroluji stav {1,2}.",
                DateTime.Now.ToString("h:mm:ss.fff"),
                (++invokeCount).ToString());

            if (invokeCount == maxCount)
            {
                // Resetuje čítač a signál Main.
                invokeCount = 0;
                autoEvent.Set();
            }

            // Garbage collector
            GC.Collect();
            Console.WriteLine("Paměť uvolněna .. \n");
        }
    }

Code of service:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;

    namespace LogWriterService
    {
        public partial class Service1 : ServiceBase
        {
            public Service1()
            {
                InitializeComponent();
            }

            protected override void OnStart(string[] args)
            {
                EventLog.WriteEntry("Sluzba screenshot se spustila.");
            }

            protected override void OnStop()
            {
                EventLog.WriteEntry("Sluzba screenshot se zastavila.");
            }

        }
    }

I thing the problem may be here:

    public static void CreateProcessAsUser()
    {
        IntPtr hToken = WindowsIdentity.GetCurrent().Token;
        IntPtr hDupedToken = IntPtr.Zero;

        ProcessUtility.PROCESS_INFORMATION pi = new ProcessUtility.PROCESS_INFORMATION();

        try
        {
            ProcessUtility.SECURITY_ATTRIBUTES sa = new ProcessUtility.SECURITY_ATTRIBUTES(); 
            sa.Length = Marshal.SizeOf(sa);

            bool result = ProcessUtility.DuplicateTokenEx(
                  hToken,
                  ProcessUtility.GENERIC_ALL_ACCESS,
                  ref sa,
                  (int)ProcessUtility.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                  (int)ProcessUtility.TOKEN_TYPE.TokenPrimary,
                  ref hDupedToken
               );

            if (!result)
            {
                throw new ApplicationException("DuplicateTokenEx failed");
            }


            ProcessUtility.STARTUPINFO si = new ProcessUtility.STARTUPINFO();
            si.cb = Marshal.SizeOf(si);
            si.lpDesktop = String.Empty;

            string folder = "D:\\test"; //Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            string path = folder + "\\LogWriter\\LogWriter.exe";  // "C:/TEMP/" + GetIP() + "/" + CurrTime + ".jpg"

             result = ProcessUtility.CreateProcessAsUser(
                                 hDupedToken,
                                 @path,        //   C:\Users\ToXiC\AppData\Roaming\LogWriter\LogWriter.exe
                                 String.Empty,
                                 ref sa, ref sa,
                                 false, 0, IntPtr.Zero,
                                 @"D:\\test", ref si, ref pi
                           );

            if (!result)
            {
                int error = Marshal.GetLastWin32Error();
                string message = String.Format("CreateProcessAsUser Error: {0}", error);
                throw new ApplicationException(message);
            }
        }
        finally
        {
            if (pi.hProcess != IntPtr.Zero)
                ProcessUtility.CloseHandle(pi.hProcess);
            if (pi.hThread != IntPtr.Zero)
                ProcessUtility.CloseHandle(pi.hThread);
            if (hDupedToken != IntPtr.Zero)
                ProcessUtility.CloseHandle(hDupedToken);
        }
    }
}

Upvotes: 1

Views: 1388

Answers (2)

ToXiC
ToXiC

Reputation: 21

Problem was with token. I found working solution here how-can-windows-service-execute-gui

Thank you all for helping me out of it. I understand now much more how windows service works.

Upvotes: 1

A G
A G

Reputation: 22597

The screenshots are all black I think. This happens because a windows service runs in Session 0 Isolation

One solution is to start a console application (with UI hidden) from the service after every 5 mins. The console application can take the screenshot and exit.

Some code to start a console app from windows service:

   string applicationPath = ...;
   private ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(applicationPath);
   //set working directiory
   Directory.SetCurrentDirectory(Path.GetDirectoryName(applicationPath));
   psi.WorkingDirectory = Path.GetDirectoryName(applicationPath);
   //psi.CreateNoWindow = false;
   psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
   //psi.UseShellExecute = false;
   prcs = System.Diagnostics.Process.Start(psi);

Edit: The console application will be started in session 0. Workaround is to use the WIN API CreateProcessAsUser pinvoke call and start the console application in a user session.

Some links with code samples on how to achieve this:
http://odetocode.com/blogs/scott/archive/2004/10/28/createprocessasuser.aspx

http://blogs.msdn.com/b/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx

http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/31bfa13d-982b-4b1a-bff3-2761ade5214f/

Upvotes: 3

Related Questions