最近在我们的生产环境中出现了奇怪的行为。 我有以下内容:
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)
好吧,伙计们,显然文件根本没有被删除......显然根本没有理由,在复制它们之后,它们在客户端连接的用户的读+写模式下保持打开状态。 我发现这尝试在我的计算机上以 Debug模式运行阅读器应用程序,并尝试打开我知道最近复制的文件之一。 我得到一个异常,指出该文件是由其他人打开的,这对我来说似乎很奇怪。 在远程服务器(存储文件的服务器)中打开“计算机管理”,然后转到“共享文件夹”>“打开文件”,我发现该文件在模拟用户的读写模式下处于打开状态,即复制文件的 Web 应用程序正在冒充做那份工作。 还有一大堆处于相同条件下的其他文件,以及许多其他以读取模式打开的文件。 我还在“共享文件夹”>“ session ”中找到了模拟用户 session 的天文数字长列表,所有这些都有很长的空闲时间。 由于模拟仅用于复制文件,然后被处置,我不应该期望那样,对吧?
我认为我们在文件复制过程中模拟用户的方式可能存在问题,链接到目标文件夹中的大量文件。 我会检查一下。
结束编辑
谢谢,
克劳迪奥·瓦莱里奥
最佳答案
我认为找到了解决方案。 我的问题是用于模拟对目标文件夹具有写权限的用户的代码。 (在我看来,所有这个项目都是从以前的软件公司继承的,而且它非常庞大,所以要关注一切并不是那么容易)
模拟过程被包装在一个实现 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 方法中处置了它。
自从我将此更新投入生产后,此代码没有任何其他问题,目标服务器上也没有任何其他共享文件以读+写模式打开。 也许不是最佳解决方案(我在计算机管理 > 共享文件夹 > session 中仍然有一大堆 session ,但这似乎不会损害系统,目前。
如果有人对这种情况有任何补充意见、建议或深入研究,我会很乐意阅读。
谢谢,
克劳迪奥
关于c# - .NET 4.5 File.Copy 可以,但文件不存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38117945/