简体   繁体   English

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

[英]FileSystemWatcher does not work on files created from windows service

I'm creating file from windows service run under LocalSystem account. 我正在从在LocalSystem帐户下运行的Windows服务创建文件。 I have a windows application that monitors the specified folder where the file is created. 我有一个Windows应用程序,该应用程序监视在其中创建文件的指定文件夹。 I'm using FileSystemWatcher but it doesn't fire. 我正在使用FileSystemWatcher,但不会触发。 The file icon in Windows explorer is a padlock icon. Windows资源管理器中的文件图标是一个挂锁图标。 How can I create this file from the windows service, so that it is accessible from the windows user account? 如何从Windows服务创建此文件,以便可以从Windows用户帐户访问该文件?

The FileWatcher is flaky. FileWatcher是片状的。 There are also issues if you are watching a folder on a network drive. 如果您正在观看网络驱动器上的文件夹,也会出现问题。 I have seen a dozen or more applications that use a FileWatcher and everyone of them has more than once failed to recognize when a file was created. 我已经看到有十几个或更多使用FileWatcher的应用程序,而且每个人中的每个人都不止一次无法识别创建文件的时间。

I would backup the FileWatcher with a timed event that checks for new or modified files. 我将使用定时事件来备份FileWatcher,该事件检查新文件或修改过的文件。 That way if the FileWatcher fails to recognize the event, the timer will catch it. 这样,如果FileWatcher无法识别事件,计时器将捕获该事件。

I think it would be better to build a service to watch that folder, personally, since then you could set the account on that service to LocalSystem and it should all be fine if it's on the same computer as your other service that is creating the file. 我认为最好亲自构建一个服务来监视该文件夹,因为这样您就可以将该服务上的帐户设置为LocalSystem,并且如果该帐户与正在创建该文件的其他服务位于同一台计算机上,那应该没问题。 But the code should be similar to do it from an app, I suppose, but with an extra layer: impersonation. 但是,我想代码应该类似于从应用程序执行的代码,但有一个额外的层:模拟。 It really has nothing to do with if you use FileSystemWatcher or not. 是否使用FileSystemWatcher确实与它无关。 Permissions are permissions, and a separate animal unto themselves. 权限是权限,本身就是一个单独的动物。

The way I did a similar file check for a service I built, I did it with a timer. 我对自己构建的服务进行类似文件检查的方式是通过计时器完成的。 You could use that with the combination of looking for file attributes, like File.Exists() and File.GetCreationTime() and File.GetLastWriteTime() to determine if the file is there, when it got there, and when it was last modified. 您可以将其与查找文件属性(例如File.Exists()File.GetCreationTime()以及File.GetLastWriteTime()以确定文件是否存在,何时到达以及何时被最后修改。 You would know if you processed that file already or not if it is older than the interval your timer runs at. 您会知道是否已处理该文件,如果该文件的时间间隔超过了计时器的运行间隔。

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

For me, whenever I process the file, I have my application delete it, so all I have to do is the File.Exists() check to know if I have another one. 对我来说,每当我处理该文件时,我就让我的应用程序将其删除,因此,我要做的就是File.Exists()检查是否还有另一个文件。

This code below was a service where I was looking for a file at regular intervals, and I have also slightly integrated in the next part you'll need to do, that I didn't, for impersonation, which I explain below it. 下面的代码是一种用于定期查找文件的服务,并且在下一部分中,我还稍微集成了您需要做的(出于模拟目的而没有用),我将在下面进行解释。 You might be able to adapt this to your application: 您也许可以使它适应您的应用程序:

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
                    }
                } 
            }    
        }
    }
}

Note that if someone does not have permission to open the file your app is watching for, and they are logged in, and they run this application you're building, it will not be able to open it, as you found out. 请注意,如果有人没有打开您的应用程序正在监视的文件的权限,并且他们已经登录并且运行了您正在构建的该应用程序,那么您将无法打开它。 The application uses the same file permissions as the logged-in user, unless you implement code to do otherwise with a WindowsImpersonationContext for the user that does have permissions to that file. 该应用程序使用与登录用户相同的文件权限,除非您实施代码以其他方式对确实拥有该文件权限的用户使用WindowsImpersonationContext But you would not be able to impersonate the Windows LocalSystem account in your application because you would not have its credentials, and it is a best practice to run a service under a domain account if it needs extra permissions, like this, because of that. 但是您将无法在应用程序中模拟Windows LocalSystem帐户,因为您没有该帐户的凭据,因此,最佳做法是在需要额外权限的域帐户下运行服务,因为这样。 Instead, you should set the service to run under a new or existing domain service account that should have permissions to the file where you know its password. 而是,应将服务设置为在新的或现有的域服务帐户下运行,该帐户应具有您知道其密码的文件的权限。 Then you can impersonate that domain account within your application as if you had actually logged in with that account. 然后,您可以在您的应用程序中模拟该域帐户,就像您实际使用该帐户登录一样。

This class below, copied and formatted from the MSDN site , is something like what you'd have to adapt to your application - you would have to combine it with the code for the timer, above. 下面的类是从MSDN站点复制并格式化 ,类似于您必须适应应用程序的类-您必须将其与上面的计时器代码结合起来。 It would be where you would either have to prompt for, or store, the credentials of your domain account that has access to the file. 这将是您必须提示或存储有权访问该文件的域帐户的凭据的位置。 Inside the area with using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { ... } is where you would do the file reading and processing as the impersonated user. using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { ... }区域内,您将以模拟用户的身份进行文件读取和处理。 I have added that, and the LogonUser() call, to the code above, but you would have to include that function, the constants, and all the other missing references from below that I didn't include: 我已经在上面的代码中添加了LogonUser()并对其进行了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);
    }
}

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM