5

Is there any class in the .NET framework which provides access to \.\G: - style paths. (i.e. raw volumes)?

We're currently doing this without any problem using p/invoked ReadFile and WriteFile, which is not complex for synchronous access, but it's tedious to add async read/write, because of all the care you need to take over pinning and handling the OVERLAPPED structure and managing the event object lifetime, etc. (i.e. all the tedious stuff we'd have to do in Win32 code...)

It's hard to prove you've got the interaction with the GC correct too, with any simple testing technique.

The FileStream class contains all this code in, no doubt, a completely bomb-proof and refined fashion, and taking advantage of lots of internal helpers which we can't use. Unfortunately FileStream explicitly stops you opening a raw volume, so we can't use it.

Is there anything else in framework which helps avoid writing this sort of code from scratch? I've poked about in Reference Source, but nothing leaps out.

Update - we had already tried the suggestion below to avoid the check on the path type by opening the device ourselves and passing in the handle. When we try this, it blows up with the following error (note that this trace goes through the contstructor of FileStream - i.e we don't get any chance to interact with the stream at all):

System.IO.IOException: The parameter is incorrect.

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.SeekCore(Int64 offset, SeekOrigin origin)
at System.IO.FileStream..ctor(SafeFileHandle handle, FileAccess access, Int32 bufferSize, Boolean isAsync)
at OurApp.USBComms.UsbDevice..ctor(Char driveLetter) in 

For reference, our CreateFile call looks like this:

var deviceName = String.Format(@"\\.\{0}:", driveLetter);

var handle = SafeNativeMethods.CreateFile(deviceName,
     0x80000000 | 0x40000000,
     FileShare.ReadWrite,
     0,
     FileMode.Open,
     (uint)FileOptions.Asynchronous | 0x20000000, // Last option is       'FILE_FLAG_NO_BUFFERING'
     IntPtr.Zero);
     if (handle.IsInvalid)
     {
        throw new IOException("CreateFile Error: " + Marshal.GetLastWin32Error());
     }

Update3: It turns out that (on a volume handle, anyway), you can't call SetFilePointer on a handle which has been opened with FILE_FLAG_OVERLAPPED. This makes some sense, as SetFilePointer is useless on files where there's any kind of multithreaded access anyway. Unfortunately FileStream seems determined to call this during construction for some reason (still trying to track down why) which is what causes the failure.

Will Dean
  • 39,055
  • 11
  • 90
  • 118
  • 1
    Give me a break! Is this kind of question now off-topic on SO? Have I stepped through the looking-glass? – Will Dean Mar 06 '15 at 14:19
  • 1
    I don't think this is off-topic. Maybe the close voter got confused with this *Is there anything else in framework which helps avoid writing this sort of code from scratch?* – Sriram Sakthivel Mar 06 '15 at 14:20
  • 2
    @WillDean That's exactly why SO is doomed. People here are now _bots_. You can't say "hi" in a question or ask something without posting a bunch of code without being downvoted to hell. Sad, but true :/ – ken2k Mar 06 '15 at 14:21
  • I know it's on-topic :-). Somewhere between August 2008 and now I've picked up the general idea of SO... – Will Dean Mar 06 '15 at 14:22
  • perhaps you should consider using IOCP through kernel32 for your async calls instead of switching to managed code https://github.com/cloudfoundry-incubator/if_warden/blob/5123983b5241571ff8e8ee6e7589896541879c53/IronFoundry.Warden/PInvoke/IoCompletionPort.cs – slf Mar 06 '15 at 15:28
  • @slf - I think that would be the proper route to take, it's not trivial though, which is what I was hoping for... – Will Dean Mar 06 '15 at 15:45
  • @WillDean there are managed libraries for this http://www.codeproject.com/Articles/10280/Managed-I-O-Completion-Ports-IOCP – slf Mar 06 '15 at 16:11

2 Answers2

3

As Sriram Sakthivel noted (+1), you could pass a SafeFileHandle to the FileStream constructor.

From your stacktrace I'm assuming you tried to seek the stream to an invalid position ; raw disks / volume have special rules about read/write positions.

For instance, you cannot start reading a disk in the middle of a sector (you'll have to read/seek per chunk of 512 bytes). Try to read for instance at offset 0 to see if it work better.

ken2k
  • 48,145
  • 10
  • 116
  • 176
  • I assume the rules pointed out in [SetFilePointer](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365541(v=vs.85).aspx) apply... – rene Mar 06 '15 at 14:45
  • You're probably right about the alignment issue - we take proper care over that in our existing code. The problem is that *I'm* not trying to seek it anywhere - that call is coming from the ctor of the FileStream class. – Will Dean Mar 06 '15 at 14:53
  • @WillDean That's the constructor trying to seek to the origin of the stream for you, but I don't know why it's implemented that way because if the handle is async it doesn't really make sense (unless I'm missing something). – ken2k Mar 06 '15 at 15:53
  • @ken2k - Yes - sadly it seeks to zero if it's FILE_TYPE_DISK, which I guess a volume handle is. I'm 100% in agreement with you guys that it's pointless on an async handle, but anyway. I guess now that .NET core is open source I can find this on GitHub and put in a PR... – Will Dean Mar 06 '15 at 16:18
2

I believe you can use this constructor of FileStream passing the pre opened FileHandle as SafeFileHandle instance. With that you have managed FileStream instance which you can use to issue an async I/O operation.

public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync);

Also when you're planning to do async I/O, don't forget to set isAsnc flag to true. Otherwise you'll not get all the benefits of async I/O.

Community
  • 1
  • 1
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Yes, we did try that subversive approach, but FileStream then suffers an 'bad parameter' error somewhere inside it, when it tries to do something inappropriate with that handle. – Will Dean Mar 06 '15 at 14:18
  • @WillDean Can you give me some more details about where it explodes with this error? Maybe we're doing something else wrong? – Sriram Sakthivel Mar 06 '15 at 14:21
  • I've added to the OP with the stack-trace where it goes when we pass in a handle - I am going to go and double-check the quality of the handle we're passing in though, if you think this should work – Will Dean Mar 06 '15 at 14:32
  • @WillDean I haven't tried it earlier. But I believe this should work, I see no reason for this to fail. Can you update the code to show how you get the FileHandle? So that I can try to reproduce the problem locally? – Sriram Sakthivel Mar 06 '15 at 14:38
  • I have put the CreateFile code into the OP - I'll catch the Win32 SetFilePointer call in Windbg and see where it's being asked to seek to. – Will Dean Mar 06 '15 at 14:57