I wrote a small program in VB .Net to physically sort folder entries on FAT partitions (see http://www.public.bplaced.net). To do this, I use API calls to Kernel32.dll. I open the partition with CreateFileA, use the handle to lock the drive with DeviceIoControl FSCTL_LOCK_VOLUME and then read and write sectors directly with SetFilePointer / ReadFile / WriteFile (synchronous, with NO_BUFFERING).
All works PERFECTLY, except when I use my program with partitions larger than 2147483647 sectors (7FFF FFFF hex), WriteFile reports success but returns (and has) 0 byte written. This is the case no matter which sector I try to write. EVERYTHING else seems to still work as it should (including correct sector reading). When I use PartitionWizard to shrink the partition below the above limit, writing works again.
Question: does ANYBODY have a clue what could cause this strange behavior? My wild guess is that 'something' might interpret a value greater than 7FFF FFFF as 'signed'? Not within my code, the 'total sectors of partition' is not needed anywhere.
A friend also said that when he did something similar with 'streams', writing worked even with a 'large' partition...
I'm a total N00b, I can't even memorize all the terminology (but I still want to program so dearly...), so if you might have an explanation / hint / whatever, please describe it as simple-worded and detailled as possible.
Some code snippets (don't know where to start)... program is compiled for x86 systems. Problem occurs on both Win7 x86 and Win7 x64.
<DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function DeviceIoControl _
( _
ByVal hDevice As IntPtr, _
ByVal dwIoControlCode As UInteger, _
ByVal lpInBuffer As IntPtr, _
ByVal nInBufferSize As UInteger, _
ByVal lpOutBuffer As IntPtr, _
ByVal nOutBufferSize As UInteger, _
ByRef lpBytesReturned As UInteger, _
ByVal lpOverlapped As IntPtr _
) _
As Integer
End Function
Public Declare Function CreateFile Lib "kernel32" _
Alias "CreateFileA" ( _
ByVal lpFileName As String, _
ByVal dwDesiredAccess As Int32, _
ByVal dwShareMode As Int32, _
ByVal lpSecurityAttributes As IntPtr, _
ByVal dwCreationDistribution As Int32, _
ByVal dwFlagsAndAttributes As Int32, _
ByVal hTemplateFile As Int32 _
) _
As IntPtr
Public Declare Function SetFilePointer Lib "kernel32" _
( _
ByVal hFile As IntPtr, _
ByVal lpDistanceToMove As UInt32, _
ByRef lpDistanceToMoveHigh As Int32, _
ByVal dwMoveMethod As UInt32 _
) _
As UInt32
Public Declare Function WriteFile Lib "kernel32" _
( _
ByVal hFile As IntPtr, _
ByVal lpBuffer As Byte(), _
ByVal nNumberOfBytesToWrite As Int32, _
ByRef lpNumberOfBytesWritten As Int32, _
ByVal lpOverlapped As IntPtr _
) _
As Boolean
' **********************************************************
' open the partition by drive letter
devicehandle = CreateFile( _
"\\.\" & driveletter & ":", _
GENERIC_READ Or GENERIC_WRITE, _
FILE_SHARE_READ Or FILE_SHARE_WRITE, _
IntPtr.Zero, _
OPEN_EXISTING, _
FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_NO_BUFFERING, _
0 _
)
' **********************************************************
' lock the partition
Dim unused_lv As UInteger
Dim locked As Integer = DeviceIoControl( _
devicehandle, _
FSCTL_LOCK_VOLUME, _
IntPtr.Zero, _
0, _
IntPtr.Zero, _
0, _
unused_lv, _
IntPtr.Zero _
)
' **********************************************************
' set the file pointer, sector = sector to read, bytes_per_sector = 512. I use Bitconverter to get the hi and lo DWORDs
Dim s_bytes() As Byte = BitConverter.GetBytes(sector * bytes_per_sector)
' Hi-DWORD
Dim byte_dist_high As Int32 = BitConverter.ToInt32(s_bytes, 4) ' byte 4 - 7
' Lo-DWORD
Dim byte_dist_low As UInt32 = BitConverter.ToUInt32(s_bytes, 0) ' byte 0 - 3
' move file pointer
Dim move As UInt32 = SetFilePointer( _
devicehandle, _
byte_dist_low, _
byte_dist_high, _
FILE_BEGIN _
)
' **********************************************************
' write a sector
Dim write As Boolean = WriteFile( _
devicehandle, _
buffer, _
bytes_per_sector, _
bytes_written, _
IntPtr.Zero _
)
If write = False Then
Return False
Else
Return True
End If