Управляемый .NET эквивалент CreateFile & WriteFile из WinBase (kernel32.dll)

Я работаю с устаревшим файловым форматом.

Файл создается с использованием неуправляемого С++, который использует функции CreateFile() и WriteFile() WinBase.h(найденные в файле kernel32.dll).

Я использовал P/Invoke для доступа к этим нативным функциям следующим образом:

[DllImport("kernel32.dll")]
 public static extern bool WriteFile(
 IntPtr hFile,
 byte[] lpBuffer,
 **** nNumberOfBytesToWrite,
 out **** lpNumberOfBytesWritten,
 [In] ref NativeOverlapped lpOverlapped);
 [DllImport("kernel32.dll", SetLastError = true)]
 public static extern bool WriteFileEx(
 IntPtr hFile,
 byte[] lpBuffer,
 **** nNumberOfBytesToWrite,
 [In] ref NativeOverlapped lpOverlapped,
 WriteFileCompletionDelegate lpCompletionRoutine);
 [DllImport("kernel32.dll", SetLastError = true)]
 public static extern IntPtr CreateFile(
 string lpFileName, **** dwDesiredAccess,
 **** dwShareMode, IntPtr lpSecurityAttributes,
 **** dwCreationDisposition,
 **** dwFlagsAndAttributes, IntPtr hTemplateFile);
 [DllImport("kernel32.dll", SetLastError = true)]
 public static extern bool CloseHandle(IntPtr hObject);
 public delegate void WriteFileCompletionDelegate(
 ****** dwErrorCode,
 ****** dwNumberOfBytesTransfered,
 ref NativeOverlapped lpOverlapped);

Проблема с этим - когда я вызываю WriteFile(), файл всегда перезаписывается исходящим вызовом.

Предпочтительно я хотел бы использовать совместимый эквивалент .NET, который позволил бы мне создать тот же формат вывода.

Код С++ выглядит так: (РАБОТА)

HANDLE hFile = CreateFile(sFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, &someVar1, sizeof(bool), &dwWritten, NULL);
WriteFile(hFile, &someVar1, sizeof(long), &dwWritten, NULL);
WriteFile(hFile, &someVar2, sizeof(bool), &dwWritten, NULL);
WriteFile(hFile, sStr.GetBuffer(0), dwStrLen*sizeof(TCHAR), &dwWritten, NULL);
CloseHandle(hFile);

С# выглядит следующим образом: (OVERWRITES PREVIOUS WRITE, то есть выход будет содержать только "T" )

{ 
 var hFile = COMFileOps2.CreateFile(FILE_NAME, (****) COMFileOps2.FILE_GENERIC_WRITE,
 COMFileOps2.FILE_SHARE_WRITE, IntPtr.Zero, COMFileOps2.CREATE_ALWAYS,
 COMFileOps2.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
 var natOverlap = new NativeOverlapped();
 COMFileOps2.WriteFileEx(hFile, new byte[] {(byte) 't'}, 1, ref natOverlap, Callback);
 COMFileOps2.WriteFileEx(hFile, new byte[] { (byte)'e' }, 1, ref natOverlap, Callback);
 COMFileOps2.WriteFileEx(hFile, new byte[] { (byte)'s' }, 1, ref natOverlap, Callback);
 COMFileOps2.WriteFileEx(hFile, new byte[] { (byte)'T' }, 1, ref natOverlap, Callback);
 COMFileOps2.CloseHandle(hFile);
}
private static void Callback(**** dwerrorcode, **** dwnumberofbytestransfered, ref NativeOverlapped lpoverlapped)
{
 throw new NotImplementedException();
}

UPDATE: Следующий код С# выведет "Test":

**** written;
**** position = 0;
var natOverlap0 = new NativeOverlapped();
COMFileOps.WriteFile(hFile, new byte[] {(byte) 'T'}, 1, out written, ref natOverlap0);
position += written;
var natOverlap1 = new NativeOverlapped {OffsetLow = (int) position};
COMFileOps.WriteFile(hFile, new byte[] { (byte)'e' }, 1, out written, ref natOverlap1);
position += written;
var natOverlap2 = new NativeOverlapped { OffsetLow = (int)position };
COMFileOps.WriteFile(hFile, new byte[] { (byte)'s' }, 1, out written, ref natOverlap2);
position += written;
var natOverlap3 = new NativeOverlapped { OffsetLow = (int)position };
COMFileOps.WriteFile(hFile, new byte[] { (byte)'t' }, 1, out written, ref natOverlap3);
COMFileOps.CloseHandle(hFile);

Спасибо.

4 ответа

WriteFileEx работает только асинхронно, вы должны предоставить экземпляр SEPARATE OVERLAPPED для каждого ожидающего вызова, и вы должны настроить членов OVERLAPPED, например смещение файла. Затем вы не можете вызвать CloseHandle, пока все операции не закончатся. И используя локальную переменную для OVERLAPPED и разрешение ее выйти из сферы действия? Ваши данные, которые будут перезаписаны, - это наименьшее из того, что может показаться неправильным с кодом, который вы показываете.

Итак, почему ваш код не работает. Я не знаю, почему вы переключились с WriteFile на WriteFileEx в первую очередь. Кроме того, вызов API Win32 с С# довольно неудобен, хотя иногда и необходим. Но сначала убедитесь, что .NET File API делают то, что вам нужно.

Так как API-интерфейсы .NET обычно работают с любыми строками (в текстовом режиме, для просмотра конверсий новой строки и т.д.) или массивами байтов, вам нужно превратить другие переменные в байты []. Класс BitConverter - ваш друг здесь.


Вы не объяснили, какой формат вы пытаетесь обработать, но не File.WriteAllText достаточно?

File.WriteAllText("someFile.txt", "some format");

Для двоичных файлов вы можете использовать File.WriteAllBytes:

File.WriteAllText("someFile.bin", new byte[] { 0x1, 0x2, 0x3 });


Нет ничего особенного в вызовах raw API. Просто получите System.IO.FileStream и вызовите Write на нем. (Один из способов получить FileStream - сначала сначала получить System.IO.File.


Посмотрите классы System.IO.File и System.IO.FileInfo, они оба предоставляют методы, которые вы ищете.

licensed under cc by-sa 3.0 with attribution.