Background
I'm trying to implement block file locking in my C# application. The built-in FileStream.Lock
method throws an exception if it is unable to acquire the lock.
The underlying LockFile
method returns a status code however I'd prefer not to use a spin-lock to wait for the file to be unlocked.
Question
Does anyone have any code snippets in C# showing how to properly construct the OVERLAPPED structure with a wait
handle and pass it to LockFileEx
and wait for the operation to complete? I am trying to avoid using the Overlapped.Pack methods partially because they're unsafe but mostly because they require an IOCompletionCallback
which isn't what I'm trying to achieve.
I have the declarations but the construction & use of the OverLapped
structure seems to be a little bit more complicated.
Note: I know I need to manually pin the overlapped structure until the wait completes. My current code looks like:
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
}
}
Resolution
The code that ended up working as I wanted was:
[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, int flags, int reserved, int countLow, int countHigh, OVERLAPPED overlapped);
private const int ExclusiveLock = 0x00000002;
public void Lock(long offset, long count)
{
int countLow = (int)count;
int countHigh = (int)(count >> 32);
OVERLAPPED l = new OVERLAPPED()
{
internalLow = 0,
internalHigh = 0,
offsetLow = (uint)(int)offset,
offsetHigh = (uint)(int)(offset >> 32),
hEvent = IntPtr.Zero,
};
if (!LockFileEx(_handle, ExclusiveLock, 0, countLow, countHigh, l))
{
//TODO: throw an exception
}
}
This code will block until the exclusive lock on the region can be acquired.