首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LockFileEX在C#中的应用

LockFileEX在C#中的应用
EN

Stack Overflow用户
提问于 2009-11-23 16:16:03
回答 2查看 4K关注 0票数 9

背景

我正试图在我的C#应用程序中实现块文件锁定。如果内置FileStream.Lock方法无法获取锁,则抛出异常。

底层的LockFile方法返回一个状态代码,但是我不希望使用自旋锁来等待文件被解锁。

问题

是否有人在C#中有任何代码片段显示如何正确地构造带有wait句柄的重叠结构,并将其传递给LockFileEx并等待操作完成?我试图避免使用Overlapped.Pack方法,部分原因是它们不安全,但主要是因为它们需要一个IOCompletionCallback,这不是我想要实现的。

我有声明,但是OverLapped结构的构造和使用似乎要复杂一些。

注意事项:我知道我需要手动固定重叠结构,直到等待完成。我的当前代码如下:

代码语言:javascript
复制
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
    }
}

分辨率

以我想要的方式工作的代码是:

代码语言:javascript
复制
[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
    }
}  

此代码将被阻塞,直到可以获取该区域的独占锁为止。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2009-11-23 17:07:39

以下内容应该“非常接近”一个好的解决方案。我真正不喜欢的唯一部分是使用反射访问mscorlib内部方法,但该方法完成了将Win32错误代码转换为IOException的出色工作。因为您已经有了不安全的NativeOverlapped*代码,所以权限不是问题。

可以通过创建一个SafeFileLockHandle或类似的方法来进一步改进它,以提供一个轻量级的IDisposable对象来解锁从CriticalFinalizerObject派生的文件。

代码语言:javascript
复制
    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);
票数 2
EN

Stack Overflow用户

发布于 2009-11-23 16:38:32

页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1784195

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档