1

我编写了一个 C# 应用程序,它每 5 分钟截取一次屏幕截图并将其保存到服务器。它是计时器、2 个线程、几个方法(ping srv、检查文件夹、截屏等)。

作为进程(exe)它运行良好,但我需要将它安装为服务。我正在通过 installutil (框架服务安装程序)安装它。

我的问题是,当它作为服务安装时,它不会截图。停止服务时拉出一些。不是正确的分辨率和黑色。

我假设执行代码放错了位置(见 main)。我不知道把它放在哪里,因为我不能有多个主要方法。请帮忙。

主要应用代码:

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");
        }
    }

服务代码:

    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.");
            }

        }
    }

我觉得问题可能出在这里:

    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);
        }
    }
}
4

2 回答 2

3

我认为截图都是黑色的。发生这种情况是因为 Windows 服务在Session 0 Isolation中运行

一种解决方案是每 5 分钟后从服务中启动一个控制台应用程序(隐藏 UI)。控制台应用程序可以截取屏幕截图并退出。

从 Windows 服务启动控制台应用程序的一些代码:

   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);

编辑:控制台应用程序将在会话 0 中启动。解决方法是使用 WIN API CreateProcessAsUser pinvoke 调用并在用户会话中启动控制台应用程序。

有关如何实现此目的的代码示例的一些链接: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/

于 2013-04-02T12:44:31.533 回答
1

问题出在令牌上。我在这里找到了可行的解决方案 how-can-windows-service-execute-gui

感谢大家帮助我摆脱困境。我现在更了解 Windows 服务的工作原理。

于 2013-06-14T09:06:19.230 回答