FileSystemWatcher不适用于从Windows服务创建的文件

时间:2011-01-25 21:12:36

标签: c# windows-7

我正在使用LocalSystem帐户运行的Windows服务创建文件。我有一个Windows应用程序,它监视创建文件的指定文件夹。我正在使用FileSystemWatcher,但它不会触发。 Windows资源管理器中的文件图标是挂锁图标。如何从Windows服务创建此文件,以便可以从Windows用户帐户访问它?

2 个答案:

答案 0 :(得分:1)

FileWatcher很不稳定。如果您正在观看网络驱动器上的文件夹,也会出现问题。我见过十几个或更多使用FileWatcher的应用程序,并且每个人都有多次无法识别文件的创建时间。

我会使用定时事件备份FileWatcher,以检查新文件或修改过的文件。这样,如果FileWatcher无法识别事件,计时器将捕获它。

答案 1 :(得分:0)

我认为最好是建立一个服务来亲自观看该文件夹,从那以后你可以将该服务的帐户设置为LocalSystem,如果它与你的同一台计算机上的一切都很好正在创建文件的其他服务。但是代码应该与应用程序类似,我想,但是有一个额外的层:模仿。如果您使用FileSystemWatcher,它实际上与此无关。权限是权限,也是一个独立的动物。

我对我建立的服务进行类似文件检查的方式,我用计时器做了。您可以结合使用File.Exists()File.GetCreationTime()以及File.GetLastWriteTime()来查找文件属性,以确定文件是否存在,文件是否存在以及文件是否在上次修改时。如果它早于计时器运行的时间间隔,您就会知道是否已经处理过该文件。

参考:http://www.csharp-examples.net/file-creation-modification-time/

对我来说,每当我处理文件时,我都会将我的应用程序删除,所以我所要做的就是File.Exists()检查以确定我是否有另一个。

下面这段代码是我定期查找文件的服务,我还要稍微整合你需要做的下一部分,我没有,冒充,我在下面解释它。您可以根据自己的应用进行调整:

using System;
using System.ServiceProcess;
using System.Threading;
using System.Timers;

namespace MyNamespace
{
    public partial class Service1:  ServiceBase
    {
        Thread syncThread = null;
        System.Timers.Timer timer1;
        string filePath = @"C:\myfile.txt";

        public Service1()
        {
            InitializeComponent();             
        }

        protected override void OnStart(string[] args)
        {
            timer1 = new System.Timers.Timer();
            timer1.Interval = 60000; // 1 min
            timer1.Enabled = true;
            timer1.Elapsed += new ElapsedEventHandler(timer1_Elapsed);
            timer1.Start();
        }
        protected override void OnStop()
        {
            syncThread.Abort();
            timer1.Stop();
        }
        protected void timer1_Elapsed(object sender, ElapsedEventArgs e)
        {
            syncThread = new Thread(new ThreadStart(doThread));
            syncThread.Start();
        }
        protected void doThread()
        {
            // This will run for each timer interval that elapses
            // in its own separate thread, and each thread will
            // end when the processing in this function ends            

            // You'll need to develop a strategy for getting these 
            // into your app
            string username, domainName, password;

            // Log the domain service account in, here... 
            bool returnValue = LogonUser(userName, domainName, password,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            if (!returnValue) return;  // not logged in - report this

            using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
            {
                using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) 
                {                 
                    bool fileAbsent = true;
                    string myFileText = String.Empty;
                    if (File.Exists(filePath))
                    {
                        fileAbsent = false;
                        FileStream fs = new FileStream(filePath, FileMode.Open);
                        StreamReader sr = new StreamReader(fs)
                        myFileText = sr.ReadToEnd();
                        sr.Close();
                        fs.Close();
                        File.Delete(filePath);
                    }
                    else
                    {
                        // report that file does not exist
                    }

                    if (myFileText != String.Empty)  // content found
                    {
                        // do processing here
                    }
                    else
                    {
                        //if (fileAbsent == false)
                        // report file was found, but empty
                    }
                } 
            }    
        }
    }
}

请注意,如果某人无权打开您的应用正在关注的文件,并且他们已登录,并且他们运行您正在构建的此应用程序,则无法打开它,因为您发现。应用程序使用与登录用户相同的文件权限,除非您为具有该文件权限的用户实现代码以使用WindowsImpersonationContext。但是您无法模拟应用程序中的Windows LocalSystem帐户,因为您没有凭据,如果需要额外的权限(如此),最好在域帐户下运行服务。相反,您应该将服务设置为在新的或现有的域服务帐户下运行,该帐户应该具有您知道其密码的文件的权限。然后,您可以在应用程序中模拟该域帐户,就像您实际使用该帐户登录一样。

下面这个类,从the MSDN site复制和格式化,类似于你必须适应你的应用程序 - 你必须将它与上面的计时器代码结合起来。您可能需要提示或存储有权访问该文件的域帐户的凭据。在using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { ... }区域内,您可以将文件读取和处理作为模拟用户。我已经将LogonUser()调用添加到上面的代码中,但是您必须包含该函数,常量以及下面我没有包含的所有其他缺少的引用:

public class ImpersonationDemo
{
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

    // Test harness.
    // If you incorporate this code into a DLL, be sure to demand FullTrust.
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public static void Main(string[] args)
    {
        SafeTokenHandle safeTokenHandle;
        try
        {
            string userName, domainName;
            // Get the user token for the specified user, domain, and password using the
            // unmanaged LogonUser method.
            // The local machine name can be used for the domain name to impersonate a user on this machine.
            Console.Write("Enter the name of the domain on which to log on: ");
            domainName = Console.ReadLine();

            Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);
            userName = Console.ReadLine();

            Console.Write("Enter the password for {0}: ", userName);

            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            Console.WriteLine("LogonUser called.");

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                Console.WriteLine("LogonUser failed with error code : {0}", ret);
                throw new System.ComponentModel.Win32Exception(ret);
            }
            using (safeTokenHandle)
            {
                Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
                Console.WriteLine("Value of Windows NT token: " + safeTokenHandle);

                // Check the identity.
                Console.WriteLine("Before impersonation: "
                    + WindowsIdentity.GetCurrent().Name);
                // Use the token handle returned by LogonUser.
                using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
                {
                    using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
                    {

                        // Check the identity.
                        Console.WriteLine("After impersonation: "
                            + WindowsIdentity.GetCurrent().Name);
                    }
                }
                // Releasing the context object stops the impersonation
                // Check the identity.
                Console.WriteLine("After closing the context: " + WindowsIdentity.GetCurrent().Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occurred. " + ex.Message);
        }

    }
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private SafeTokenHandle()
        : base(true)
    {
    }

    [DllImport("kernel32.dll")]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [SuppressUnmanagedCodeSecurity]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr handle);

    protected override bool ReleaseHandle()
    {
        return CloseHandle(handle);
    }
}

参考:https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx