【问题标题】:.NET 4.5 File.Copy ok, but file does not exist.NET 4.5 File.Copy 可以,但文件不存在
【发布时间】:2016-06-30 08:38:00
【问题描述】:

这些天来,我们的生产环境中出现了奇怪的行为。 我有以下内容:

try {
    var sourcePath = ... // my source path
    var destinationPath = ... // guess what? You're right, my destination path

    File.Copy(sourcePath, destinationPath);
    Log.Debug(string.Format("Image {0} copied successfully", imagename));
}
catch(Exception e) {
    // exception handling
}

源路径和目标路径都在网络共享上,即另一个(虚拟)机器上的文件夹,其中包含大量文件(> 500k)。 从最近 2 天开始,上面的代码运行,记录最后一行(说明图像已被复制的那一行),但如果我检查目标文件夹,则假定的目标文件不存在。

我认为对于任何 I/O 错误 File.Copy 都会引发异常,所以这件事让我发疯了。 请注意,在该文件夹中写入文件的其他代码部分工作正常。另外,请注意所有文件名都是唯一的(为了简洁起见,未包含业务代码以确保这一点),我认为在这种情况下会引发异常或文件至少会被覆盖。

有人遇到过同样的问题吗?可能的原因?有什么解决办法吗?

编辑 2016-07-01 15:12 (GMT+0200)

好吧,伙计们,显然文件根本没有被删除......显然根本没有任何理由,在它们被复制后,它们在客户端连接用户的读+写模式下保持打开状态。 我发现这试图在我的计算机上以调试模式运行阅读器应用程序,并试图打开我知道最近复制的文件之一。 我收到一个异常,指出该文件是由其他人打开的,这对我来说似乎很奇怪。 在远程服务器(存储文件的服务器)中打开计算机管理,然后转到共享文件夹 > 打开文件,我发现文件在复制文件的 Web 应用程序的模拟用户中以读+写模式保持打开状态冒充做这项工作。 还有一大堆在相同条件下的其他文件,以及许多其他以读取模式打开的文件。 我还在“共享文件夹”>“会话”中找到了模拟用户会话的天文长列表,所有这些都具有很长的空闲时间。 由于模拟仅用于复制文件,然后被释放,我不应该期望这样,对吧?

我认为我们在文件复制期间模拟用户的方式可能存在问题,与目标文件夹中的大量文件相关联。 我会检查的。

结束编辑

谢谢,

克劳迪奥·瓦莱里奥

【问题讨论】:

  • 也许某人/某物在复制文件后正在删除文件?
  • 您好,Matteo,感谢您的评论。遗憾的是,没有任何代码行可以从该特定文件夹中删除,除了我和我的同事之外,没有人可以通过 Windows 资源管理器访问(没有 FTP 访问权限)。
  • 在某些情况下,您的destinationPath 可能不是您认为的那样。也许你也应该记录一下。
  • 嗨,Charles,目标路径始终是特定的现有文件夹(100% 确定),并带有唯一的文件名。此外,这段代码每次都在一小堆图像文件上运行,并且在一个单独的一堆中,有一个或两个文件被正确复制并存在。
  • 显然也存在源文件(否则我应该得到一个 FileNotFoundException,根据文档)。

标签: c# .net system.io.file


【解决方案1】:

我认为找到了解决方案。 我的问题是用于模拟具有目标文件夹写入权限的用户的代码。 (在我的辩护中,所有这些项目都是从以前的软件公司继承的,而且规模很大,所以要关注所有事情并不容易)

模拟过程被封装在一个实现 IDisposable 的类中

public class Impersonator :
    IDisposable
{
    public Impersonator()
    {
        string userName = // get username from config
        string password = // get password from config
        string domainName = // get domain from config
        ImpersonateValidUser(userName, domainName, password);
    }

    public void Dispose()
    {
        UndoImpersonation();
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int LogonUser(
        string lpszUserName,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int DuplicateToken(
        IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool RevertToSelf();

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

    private const int LOGON32_LOGON_INTERACTIVE = 2;
    private const int LOGON32_PROVIDER_DEFAULT = 0;

    private void ImpersonateValidUser(
        string userName,
        string domain,
        string password)
    {
        WindowsIdentity tempWindowsIdentity = null;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        try
        {
            if (RevertToSelf())
            {
                if (LogonUser(
                    userName,
                    domain,
                    password,
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT,
                    ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            else
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
        finally
        {
            if (token != IntPtr.Zero)
            {
                CloseHandle(token);
            }
            if (tokenDuplicate != IntPtr.Zero)
            {
                CloseHandle(tokenDuplicate);
            }
        }
    }

    private void UndoImpersonation()
    {
        if (impersonationContext != null)
        {
            impersonationContext.Undo();
        }
    }

    private WindowsImpersonationContext impersonationContext = null;

}

这个类是这样使用的:

using(new Impersonator())
{
    // do stuff with files in here
}

我的怀疑是关闭模拟用户的处理程序,不知何故,它可能会破坏 Windows 处理模拟用户通过网络共享打开文件的方式,就像我的情况一样,让共享文件在 read+ 中打开写模式,防止任何其他进程/用户打开它们。

我修改了 Impersonator 类如下:

public class Impersonator :
    IDisposable
{
    public Impersonator()
    {
        string userName = // get username from config
        string password = // get password from config
        string domainName = // get domain from config
        ImpersonateValidUser(userName, domainName, password);
    }

    public void Dispose()
    {
        UndoImpersonation();
        impersonationContext.Dispose();
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int LogonUser(
        string lpszUserName,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int DuplicateToken(
        IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool RevertToSelf();

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

    private const int LOGON32_LOGON_INTERACTIVE = 2;
    private const int LOGON32_PROVIDER_DEFAULT = 0;

    private void ImpersonateValidUser(
        string userName,
        string domain,
        string password)
    {
        WindowsIdentity tempWindowsIdentity = null;
        token = IntPtr.Zero;
        tokenDuplicate = IntPtr.Zero;

        try
        {
            if (RevertToSelf())
            {
                if (LogonUser(
                    userName,
                    domain,
                    password,
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT,
                    ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            else
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
        finally
        {
        }
    }

    private void UndoImpersonation()
    {
        try
        {
            if (impersonationContext != null)
            {
                impersonationContext.Undo();
            }
        }
        finally
        {
            if (token != IntPtr.Zero)
            {
                CloseHandle(token);
            }
            if (tokenDuplicate != IntPtr.Zero)
            {
                CloseHandle(tokenDuplicate);
            }
        }
    }

    private WindowsImpersonationContext impersonationContext = null;
    private IntPtr token;
    private IntPtr tokenDuplicate;

}

基本上,我在 UndoImpersonation 方法中移动了关闭的处理程序。另外,我对没有显式处理 impersonationContext 有疑问,我在 Impersonator 类的 Dispose 方法中处理了它。

自从我将此更新投入生产以来,此代码没有任何其他问题,也没有任何其他共享文件在目标服务器上以读写模式打开。 也许不是最佳解决方案(我仍然在计算机管理 > 共享文件夹 > 会话中有一大堆会话,但这似乎不会损害系统,目前。

如果有人对这种情况有一些额外的评论、建议或深入研究,我会很乐意阅读。

谢谢,

克劳迪奥

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多