Jef*_*ker 8 .net c# file-io locking
我正在尝试在我的C#应用程序中实现块文件锁定.如果内置FileStream.Lock方法无法获取锁定,则抛出异常.
底层LockFile方法返回状态代码但是我不想使用自旋锁来等待文件被解锁.
有没有人在C#中有任何代码片段,显示如何使用句柄正确构造OVERLAPPED结构wait并将其传递给LockFileEx等待操作完成?我试图避免使用Overlapped.Pack方法,因为它们不安全,但主要是因为它们需要的IOCompletionCallback不是我想要实现的.
我有声明,但结构的构造和使用OverLapped似乎有点复杂.
注意:我知道我需要手动固定重叠结构,直到等待完成.我目前的代码如下:
ManualResetEvent evt = new ManualResetEvent(false);
OVERLAPPED overlapped = new OVERLAPPED();
overlapped.OffsetLow = offsetLow;
overlapped.OffsetHigh = offsetHigh;
overlapped.hEvent = evt.SafeHandle;
GCHandle h = GCHandle.Alloc(overlapped, GCHandleType.Pinned);
int hr = Win32.LockFileEX(_handle, LockFlags.Exclusive, 0, offsetLow, offsetHigh,
GCHandle.ToIntPtr(h));
if(hr == 0)
{
int error = Marshal.GetLastWin32Error();
if(error = Win32.ERROR_IO_PENDING)
{
evt.WaitOne();
}
else
{
//ohpoo
}
}
Run Code Online (Sandbox Code Playgroud)
最终按照我的意愿工作的代码是:
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED
{
public uint internalLow;
public uint internalHigh;
public uint offsetLow;
public uint offsetHigh;
public IntPtr hEvent;
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool LockFileEx(SafeFileHandle handle, uint flags, uint reserved, uint countLow, uint countHigh, ref OVERLAPPED overlapped);
private const uint LOCKFILE_EXCLUSIVE_LOCK = 0x00000002;
public static void Lock(FileStream stream, ulong offset, ulong count)
{
uint countLow = (uint)count;
uint countHigh = (uint)(count >> 32);
OVERLAPPED overlapped = new OVERLAPPED()
{
internalLow = 0,
internalHigh = 0,
offsetLow = (uint)offset,
offsetHigh = (uint)(offset >> 32),
hEvent = IntPtr.Zero,
};
if (!LockFileEx(stream.SafeFileHandle, LOCKFILE_EXCLUSIVE_LOCK, 0, countLow,
countHigh, ref overlapped))
{
//TODO: throw an exception
}
}
Run Code Online (Sandbox Code Playgroud)
此代码将阻止,直到可以获取该区域的独占锁定.
以下应该“非常接近”一个好的解决方案。我真正不喜欢的唯一部分是使用反射来访问 mscorlib 内部方法,但该方法在将 Win32 错误代码转换为 IOException 方面做得非常出色。由于您已经拥有不安全的代码NativeOverlapped*,因此权限不是问题。
它可能可以通过创建一个SafeFileLockHandle或类似的对象来进一步改进,以提供一个轻量级IDisposable对象来解锁从CriticalFinalizerObject.
private const uint LOCKFILE_EXCLUSIVE_LOCK = 0x00000002;
private static readonly Action WinIOError;
static Win32Native()
{
BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var winIOErrorMethod = typeof(string).Assembly.GetType("System.IO.__Error").GetMethod("WinIOError", bindingAttr, null, Type.EmptyTypes, null);
WinIOError = (Action)Delegate.CreateDelegate(typeof(Action), winIOErrorMethod);
}
public static void LockFile(SafeFileHandle handle, bool exclusive, long offset, long length, Action action)
{
if (handle == null)
throw new ArgumentNullException("handle");
if (handle.IsInvalid)
throw new ArgumentException("An invalid file handle was specified.", "handle");
if (offset < 0)
throw new ArgumentOutOfRangeException("The offset cannot be negative.", "offset");
if (length < 0)
throw new ArgumentOutOfRangeException("The length cannot be negative.", "length");
if (action == null)
throw new ArgumentNullException("action");
LockFileUnsafe(handle, exclusive, offset, length, action);
}
private static unsafe void LockFileUnsafe(SafeFileHandle handle, bool exclusive, long offset, long length, Action action)
{
Overlapped overlapped = new Overlapped();
overlapped.OffsetHigh = (int)(offset >> 32);
overlapped.OffsetLow = (int)offset;
IOCompletionCallback callback =
(errorCode, numBytes, nativeOverlapped) =>
{
try
{
action();
}
finally
{
Overlapped.Free(nativeOverlapped);
}
};
NativeOverlapped* native = overlapped.Pack(callback, null);
uint flags = exclusive ? LOCKFILE_EXCLUSIVE_LOCK : 0;
if (!LockFileEx(handle, flags, 0, (int)length, (int)(length >> 32), native))
{
Overlapped.Free(native);
WinIOError();
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static unsafe extern bool LockFileEx(SafeFileHandle handle, uint flags, uint mustBeZero, int countLow, int countHigh, NativeOverlapped* overlapped);
Run Code Online (Sandbox Code Playgroud)