using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace MaksIT.LTO.Core;
public partial class TapeDeviceHandler : IDisposable {
#region Tape IOCTL Commands from ntddtape.h
//
// NtDeviceIoControlFile IoControlCode values for this device.
//
// Warning: Remember that the low two bits of the code specify how the
// buffers are passed to the driver!
//
private const uint IOCTL_TAPE_ERASE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0000 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_PREPARE = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS) << 14) | (0x0001 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_WRITE_MARKS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0002 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0003 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_POSITION = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0004 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0005 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_DRIVE_PARAMS = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x0006 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0007 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_SET_MEDIA_PARAMS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0008 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_GET_STATUS = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0009 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_CREATE_PARTITION = (FILE_DEVICE_TAPE << 16) | ((FILE_READ_ACCESS | FILE_WRITE_ACCESS) << 14) | (0x000A << 2) | METHOD_BUFFERED;
//
// The following device control codes are common for all class drivers. The
// functions codes defined here must match all of the other class drivers.
//
// Warning: these codes will be replaced in the future with the IOCTL_STORAGE
// codes included below
//
private const uint IOCTL_TAPE_MEDIA_REMOVAL = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0201 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_EJECT_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0202 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_LOAD_MEDIA = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0203 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_RESERVE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0204 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_RELEASE = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0205 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_CHECK_VERIFY = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0200 << 2) | METHOD_BUFFERED;
private const uint IOCTL_TAPE_FIND_NEW_DEVICES = (FILE_DEVICE_TAPE << 16) | (FILE_READ_ACCESS << 14) | (0x0206 << 2) | METHOD_BUFFERED;
#endregion
#region Tape IOCTL Structures from ntddtape.h
//
// IOCTL_TAPE_ERASE definitions
//
public const uint TAPE_ERASE_SHORT = 0;
public const uint TAPE_ERASE_LONG = 1;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_ERASE {
public uint Type; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_PREPARE definitions
//
public const uint TAPE_LOAD = 0;
public const uint TAPE_UNLOAD = 1;
public const uint TAPE_TENSION = 2;
public const uint TAPE_LOCK = 3;
public const uint TAPE_UNLOCK = 4;
public const uint TAPE_FORMAT = 5;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_PREPARE {
public uint Operation; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_WRITE_MARKS definitions
//
public const uint TAPE_SETMARKS = 0;
public const uint TAPE_FILEMARKS = 1;
public const uint TAPE_SHORT_FILEMARKS = 2;
public const uint TAPE_LONG_FILEMARKS = 3;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_WRITE_MARKS {
public uint Type; // ULONG in C is equivalent to uint in C#
public uint Count; // ULONG in C is equivalent to uint in C#
public byte Immediate; // BOOLEAN in C is typically a byte in C#
}
//
// IOCTL_TAPE_SET_POSITION definitions
//
public const uint TAPE_ABSOLUTE_POSITION = 0;
public const uint TAPE_LOGICAL_POSITION = 1;
public const uint TAPE_PSEUDO_LOGICAL_POSITION = 2;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_POSITION {
public uint Type;
public uint Partition;
public uint OffsetLow;
public uint OffsetHigh;
}
//
// IOCTL_TAPE_SET_POSITION definitions
//
public const uint TAPE_REWIND = 0;
public const uint TAPE_ABSOLUTE_BLOCK = 1;
public const uint TAPE_LOGICAL_BLOCK = 2;
public const uint TAPE_PSEUDO_LOGICAL_BLOCK = 3;
public const uint TAPE_SPACE_END_OF_DATA = 4;
public const uint TAPE_SPACE_RELATIVE_BLOCKS = 5;
public const uint TAPE_SPACE_FILEMARKS = 6;
public const uint TAPE_SPACE_SEQUENTIAL_FMKS = 7;
public const uint TAPE_SPACE_SETMARKS = 8;
public const uint TAPE_SPACE_SEQUENTIAL_SMKS = 9;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_POSITION {
public uint Method;
public uint Partition;
public long Offset;
public byte Immediate;
}
//
// IOCTL_TAPE_GET_DRIVE_PARAMS definitions
//
//
// Definitions for FeaturesLow parameter
//
public const uint TAPE_DRIVE_FIXED = 0x00000001;
public const uint TAPE_DRIVE_SELECT = 0x00000002;
public const uint TAPE_DRIVE_INITIATOR = 0x00000004;
public const uint TAPE_DRIVE_ERASE_SHORT = 0x00000010;
public const uint TAPE_DRIVE_ERASE_LONG = 0x00000020;
public const uint TAPE_DRIVE_ERASE_BOP_ONLY = 0x00000040;
public const uint TAPE_DRIVE_ERASE_IMMEDIATE = 0x00000080;
public const uint TAPE_DRIVE_TAPE_CAPACITY = 0x00000100;
public const uint TAPE_DRIVE_TAPE_REMAINING = 0x00000200;
public const uint TAPE_DRIVE_FIXED_BLOCK = 0x00000400;
public const uint TAPE_DRIVE_VARIABLE_BLOCK = 0x00000800;
public const uint TAPE_DRIVE_WRITE_PROTECT = 0x00001000;
public const uint TAPE_DRIVE_EOT_WZ_SIZE = 0x00002000;
public const uint TAPE_DRIVE_ECC = 0x00010000;
public const uint TAPE_DRIVE_COMPRESSION = 0x00020000;
public const uint TAPE_DRIVE_PADDING = 0x00040000;
public const uint TAPE_DRIVE_REPORT_SMKS = 0x00080000;
public const uint TAPE_DRIVE_GET_ABSOLUTE_BLK = 0x00100000;
public const uint TAPE_DRIVE_GET_LOGICAL_BLK = 0x00200000;
public const uint TAPE_DRIVE_SET_EOT_WZ_SIZE = 0x00400000;
public const uint TAPE_DRIVE_EJECT_MEDIA = 0x01000000; //don't use this bit!
// //can't be a low features bit!
// //reserved; high features only
//
// Definitions for FeaturesHigh parameter
//
public const uint TAPE_DRIVE_LOAD_UNLOAD = 0x80000001;
public const uint TAPE_DRIVE_TENSION = 0x80000002;
public const uint TAPE_DRIVE_LOCK_UNLOCK = 0x80000004;
public const uint TAPE_DRIVE_REWIND_IMMEDIATE = 0x80000008;
public const uint TAPE_DRIVE_SET_BLOCK_SIZE = 0x80000010;
public const uint TAPE_DRIVE_LOAD_UNLD_IMMED = 0x80000040;
public const uint TAPE_DRIVE_TENSION_IMMED = 0x80000080;
public const uint TAPE_DRIVE_LOCK_UNLK_IMMED = 0x80000100;
public const uint TAPE_DRIVE_SET_ECC = 0x80000200;
public const uint TAPE_DRIVE_SET_COMPRESSION = 0x80000400;
public const uint TAPE_DRIVE_SET_PADDING = 0x80000800;
public const uint TAPE_DRIVE_SET_REPORT_SMKS = 0x80001000;
public const uint TAPE_DRIVE_ABSOLUTE_BLK = 0x80002000;
public const uint TAPE_DRIVE_ABS_BLK_IMMED = 0x80004000;
public const uint TAPE_DRIVE_LOGICAL_BLK = 0x80008000;
public const uint TAPE_DRIVE_LOG_BLK_IMMED = 0x80010000;
public const uint TAPE_DRIVE_END_OF_DATA = 0x80020000;
public const uint TAPE_DRIVE_RELATIVE_BLKS = 0x80040000;
public const uint TAPE_DRIVE_FILEMARKS = 0x80080000;
public const uint TAPE_DRIVE_SEQUENTIAL_FMKS = 0x80100000;
public const uint TAPE_DRIVE_SETMARKS = 0x80200000;
public const uint TAPE_DRIVE_SEQUENTIAL_SMKS = 0x80400000;
public const uint TAPE_DRIVE_REVERSE_POSITION = 0x80800000;
public const uint TAPE_DRIVE_SPACE_IMMEDIATE = 0x81000000;
public const uint TAPE_DRIVE_WRITE_SETMARKS = 0x82000000;
public const uint TAPE_DRIVE_WRITE_FILEMARKS = 0x84000000;
public const uint TAPE_DRIVE_WRITE_SHORT_FMKS = 0x88000000;
public const uint TAPE_DRIVE_WRITE_LONG_FMKS = 0x90000000;
public const uint TAPE_DRIVE_WRITE_MARK_IMMED = 0xA0000000;
public const uint TAPE_DRIVE_FORMAT = 0xC0000000;
public const uint TAPE_DRIVE_FORMAT_IMMEDIATE = 0x80000000;
public const uint TAPE_DRIVE_HIGH_FEATURES = 0x80000000; //mask for high features flag
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_DRIVE_PARAMETERS {
public uint ECC;
public uint Compression;
public uint DataPadding;
public uint ReportSetmarks;
public uint DefaultBlockSize;
public uint MaximumBlockSize;
public uint MinimumBlockSize;
public uint MaximumPartitionCount;
public uint FeaturesLow;
public uint FeaturesHigh;
public uint EOTWarningZoneSize;
}
//
// IOCTL_TAPE_SET_DRIVE_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_DRIVE_PARAMETERS {
public uint ECC;
public uint Compression;
public uint DataPadding;
public uint ReportSetmarks;
public uint EOTWarningZoneSize;
}
//
// IOCTL_TAPE_GET_MEDIA_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_GET_MEDIA_PARAMETERS {
public uint Capacity;
public uint Remaining;
public uint BlockSize;
public uint PartitionCount;
public byte WriteProtected;
}
//
// IOCTL_TAPE_SET_MEDIA_PARAMETERS definitions
//
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_SET_MEDIA_PARAMETERS {
public uint BlockSize;
}
//
// IOCTL_TAPE_CREATE_PARTITION definitions
//
public const uint TAPE_FIXED_PARTITIONS = 0;
public const uint TAPE_SELECT_PARTITIONS = 1;
public const uint TAPE_INITIATOR_PARTITIONS = 2;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_CREATE_PARTITION {
public uint Method;
public uint Count;
public uint Size;
}
//
// WMI Methods
//
public const uint TAPE_QUERY_DRIVE_PARAMETERS = 0;
public const uint TAPE_QUERY_MEDIA_CAPACITY = 1;
public const uint TAPE_CHECK_FOR_DRIVE_PROBLEM = 2;
public const uint TAPE_QUERY_IO_ERROR_DATA = 3;
public const uint TAPE_QUERY_DEVICE_ERROR_DATA = 4;
[StructLayout(LayoutKind.Sequential)]
public struct TAPE_WMI_OPERATIONS {
public uint Method;
public uint DataBufferSize;
public IntPtr DataBuffer;
}
//
// Type of drive errors
//
public enum TAPE_DRIVE_PROBLEM_TYPE {
TapeDriveProblemNone,
TapeDriveReadWriteWarning,
TapeDriveReadWriteError,
TapeDriveReadWarning,
TapeDriveWriteWarning,
TapeDriveReadError,
TapeDriveWriteError,
TapeDriveHardwareError,
TapeDriveUnsupportedMedia,
TapeDriveScsiConnectionError,
TapeDriveTimetoClean,
TapeDriveCleanDriveNow,
TapeDriveMediaLifeExpired,
TapeDriveSnappedTape
}
#endregion
#region Tape IOCTL Methods from ntddtape.h
///
/// Erase the tape
///
/// The type of erase operation. Valid values are and .
public int Erase(uint type) {
TAPE_ERASE erase = new TAPE_ERASE {
Type = type,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(erase));
try {
Marshal.StructureToPtr(erase, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_ERASE, inBuffer, (uint)Marshal.SizeOf(erase), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Erase Tape ({type}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Erase Tape: Failed with error code {error}");
return error;
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
return 0;
}
///
/// Prepare the tape for a specific operation
///
/// The type of prepare operation. Valid values are , , and .
public void Prepare(uint operation) {
TAPE_PREPARE prepare = new TAPE_PREPARE {
Operation = operation,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(prepare));
try {
Marshal.StructureToPtr(prepare, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_PREPARE, inBuffer, (uint)Marshal.SizeOf(prepare), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Prepare Tape ({operation}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Prepare Tape: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
///
/// Write tape marks
/// The type of marks to write. Valid values are , , and .
/// The number of marks to write.
public void WriteMarks(uint type, uint count) {
TAPE_WRITE_MARKS marks = new TAPE_WRITE_MARKS {
Type = type,
Count = count,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(marks));
try {
Marshal.StructureToPtr(marks, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_WRITE_MARKS, inBuffer, (uint)Marshal.SizeOf(marks), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Write Marks: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Write Marks: Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public class TapePosition {
public uint? MethodType { get; set; }
public uint? Partition { get; set; }
public uint? OffsetLow { get; set; }
public uint? OffsetHigh { get; set; }
public int? Error { get; set; }
}
///
/// Get the current tape position
///
/// The type of position to get. Valid values are , and .
/// The partition number.
/// The low offset value.
/// The high offset value.
/// The tape position .
public TapePosition GetPosition(uint type, uint partition = 0, uint offsetLow = 0, uint offsetHigh = 0) {
TAPE_GET_POSITION position = new TAPE_GET_POSITION {
Type = 0,
Partition = partition,
OffsetLow = offsetLow,
OffsetHigh = offsetHigh
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_POSITION, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(position), out uint bytesReturned, IntPtr.Zero);
if (result) {
position = Marshal.PtrToStructure(outBuffer);
Console.WriteLine("Get Position: Success");
Console.WriteLine($"Type: {position.Type}");
Console.WriteLine($"Partition: {position.Partition}");
Console.WriteLine($"OffsetLow: {position.OffsetLow}");
Console.WriteLine($"OffsetHigh: {position.OffsetHigh}");
return new TapePosition {
MethodType = position.Type,
Partition = position.Partition,
OffsetLow = position.OffsetLow,
OffsetHigh = position.OffsetHigh
};
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Position: Failed with error code {error}");
return new TapePosition {
Error = error
};
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
///
/// Set the tape position
///
/// The method to use for setting the position. Valid values are , , , , , , , , and .
/// The partition number.
/// The offset value.
public void SetPosition(uint method, uint partition = 0, long offset = 0) {
TAPE_SET_POSITION position = new TAPE_SET_POSITION {
Method = method,
Partition = partition,
Offset = offset,
Immediate = 0
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(position));
try {
Marshal.StructureToPtr(position, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_POSITION, inBuffer, (uint)Marshal.SizeOf(position), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine($"Set Position ({method}): Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Position ({method}): Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
///
/// Get the drive parameters
///
/// The maximum block size supported by the drive.
/// The minimum block size supported by the drive.
public void GetDriveParameters(out uint minBlockSize, out uint maxBlockSize) {
TAPE_GET_DRIVE_PARAMETERS driveParams = new TAPE_GET_DRIVE_PARAMETERS {
ECC = 0,
Compression = 0,
DataPadding = 0,
ReportSetmarks = 0,
DefaultBlockSize = 0,
MaximumBlockSize = 0,
MinimumBlockSize = 0,
MaximumPartitionCount = 0,
FeaturesLow = 0,
FeaturesHigh = 0,
EOTWarningZoneSize = 0
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_DRIVE_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(driveParams), out uint bytesReturned, IntPtr.Zero);
if (result) {
driveParams = Marshal.PtrToStructure(outBuffer);
minBlockSize = driveParams.MinimumBlockSize;
maxBlockSize = driveParams.MaximumBlockSize;
Console.WriteLine($"Drive Parameters: MinBlockSize = {minBlockSize}, MaxBlockSize = {maxBlockSize}");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Drive Parameters Failed with error code {error}");
minBlockSize = 0;
maxBlockSize = 0;
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
///
/// Set the drive parameters
///
/// The error correction code (ECC) setting.
/// The compression setting.
/// The data padding setting.
/// The report setmarks setting.
/// The end-of-tape (EOT) warning zone size setting.
public void SetDriveParams(uint ecc, uint compression = 0, uint dataPadding = 0, uint reportSetmarks = 0, uint eotWarningZoneSize = 0) {
TAPE_SET_DRIVE_PARAMETERS driveParams = new TAPE_SET_DRIVE_PARAMETERS {
ECC = ecc,
Compression = compression,
DataPadding = dataPadding,
ReportSetmarks = reportSetmarks,
EOTWarningZoneSize = eotWarningZoneSize
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(driveParams));
try {
Marshal.StructureToPtr(driveParams, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_DRIVE_PARAMS, inBuffer, (uint)Marshal.SizeOf(driveParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Set Drive Params: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Drive Params Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public void GetMediaParams(uint capacity = 0, uint remaining = 0, uint blockSize = 0, uint partitionCount = 0, byte writeProtected = 0) {
TAPE_GET_MEDIA_PARAMETERS mediaParams = new TAPE_GET_MEDIA_PARAMETERS {
Capacity = 0,
Remaining = 0,
BlockSize = 0,
PartitionCount = 0,
WriteProtected = 0
};
IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
try {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_MEDIA_PARAMS, IntPtr.Zero, 0, outBuffer, (uint)Marshal.SizeOf(mediaParams), out uint bytesReturned, IntPtr.Zero);
if (result) {
mediaParams = Marshal.PtrToStructure(outBuffer);
Console.WriteLine("Get Media Params: Success");
Console.WriteLine($"Capacity: {mediaParams.Capacity}");
Console.WriteLine($"Remaining: {mediaParams.Remaining}");
Console.WriteLine($"BlockSize: {mediaParams.BlockSize}");
Console.WriteLine($"PartitionCount: {mediaParams.PartitionCount}");
Console.WriteLine($"WriteProtected: {mediaParams.WriteProtected}");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Media Params Failed with error code {error}");
}
}
finally {
Marshal.FreeHGlobal(outBuffer);
}
}
public void SetMediaParams(uint blockSize) {
TAPE_SET_MEDIA_PARAMETERS mediaParams = new TAPE_SET_MEDIA_PARAMETERS {
BlockSize = blockSize
};
IntPtr inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(mediaParams));
try {
Marshal.StructureToPtr(mediaParams, inBuffer, false);
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_SET_MEDIA_PARAMS, inBuffer, (uint)Marshal.SizeOf(mediaParams), IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (!result) {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Set Block Size Failed with error code {error}");
}
else {
Console.WriteLine($"Set Block Size ({blockSize}): Success");
}
}
finally {
Marshal.FreeHGlobal(inBuffer);
}
}
public int GetStatus() {
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_GET_STATUS, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (result) {
Console.WriteLine("Get Status: Success");
}
else {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Get Status: Failed with error code {error}");
return error;
}
return 0;
}
public void CreatePartition(uint method = 0, uint count = 0, uint size = 0) {
TAPE_CREATE_PARTITION partition = new TAPE_CREATE_PARTITION {
Method = 0,
Count = 1,
Size = 0
};
bool result = DeviceIoControl(_tapeHandle, IOCTL_TAPE_CREATE_PARTITION, IntPtr.Zero, 0, IntPtr.Zero, 0, out uint bytesReturned, IntPtr.Zero);
if (!result) {
int error = Marshal.GetLastWin32Error();
Console.WriteLine($"Create Partition: Failed with error code {error}");
}
else {
Console.WriteLine("Create Partition: Success");
}
}
#endregion
}