From: djnewman on
Hi,
First let me state that I posted this to Technet and got no help there.
I know this is not the best code but I would like to get some pointers.

I've been working on controlling a tape library using IOCTL commands. I'm
working in C# and so far I can get the product data and the changer device
status. I would now like to send commands to the changer to move the
transport so I can move media. Obviously, I'm facing an uphill battle using C
type control structures with C#, but I'm marshalling the data and it appears
to be correct when I look at it in memory. I do have the
IOCTL_CHANGER_GET_PARAMETERS and IOCTL_CHANGER_GET_PRODUCT_DATA working. I do
not intend to use the overlapped process until I get all of these working in
a non threaded process.

The problem is that I'm getting Error 1 - Incorrect function when I call
either IOCTL_CHANGER_REINITIALIZE_TRANSPORT or IOCTL_CHANGER_SET_POSITON. The
changer is open.

These are the first 2 commands I have tried becuse they seem simple. Below
is a snippet to show the command, structure and call. I know this would be
easier in C or C++, but I'm doing my best to learn C# and hard problems are
the best way.

This is running on a Windows Server 2003 R2 system. The library is an HP
SURESTOREDAT 24x6 and the library and tape drive work normally with NTBackup.
I have stopped the removable storage service. I am coding in C# using VS 2010
in .Net 4.0 as a windows forms application, but the below code is in a class
by itself. The structures are taken from _ntddchgr.h and the types converted
to managed types where it made sense.

Any help is appreciated.

Dave Newman

djnewman(a)pacbell.net

[Flags]
private enum EIOControlCode : uint
{
// STORAGE
StorageBase = EFileDevice.MassStorage,
StorageCheckVerify = (StorageBase << 16) | (0x0200 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageCheckVerify2 = (StorageBase << 16) | (0x0200 << 2) |
EMethod.Buffered | (0 << 14), // FileAccess.Any
StorageMediaRemoval = (StorageBase << 16) | (0x0201 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageEjectMedia = (StorageBase << 16) | (0x0202 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageLoadMedia = (StorageBase << 16) | (0x0203 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageLoadMedia2 = (StorageBase << 16) | (0x0203 << 2) |
EMethod.Buffered | (0 << 14),
StorageReserve = (StorageBase << 16) | (0x0204 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageRelease = (StorageBase << 16) | (0x0205 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageFindNewDevices = (StorageBase << 16) | (0x0206 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageEjectionControl = (StorageBase << 16) | (0x0250 << 2) |
EMethod.Buffered | (0 << 14),
StorageMcnControl = (StorageBase << 16) | (0x0251 << 2) |
EMethod.Buffered | (0 << 14),
StorageGetMediaTypes = (StorageBase << 16) | (0x0300 << 2) |
EMethod.Buffered | (0 << 14),
StorageGetMediaTypesEx = (StorageBase << 16) | (0x0301 << 2) |
EMethod.Buffered | (0 << 14),
StorageResetBus = (StorageBase << 16) | (0x0400 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageResetDevice = (StorageBase << 16) | (0x0401 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
StorageGetDeviceNumber = (StorageBase << 16) | (0x0420 << 2) |
EMethod.Buffered | (0 << 14),
StoragePredictFailure = (StorageBase << 16) | (0x0440 << 2) |
EMethod.Buffered | (0 << 14),
StorageObsoleteResetBus = (StorageBase << 16) | (0x0400 << 2) |
EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
StorageObsoleteResetDevice = (StorageBase << 16) | (0x0401 << 2)
| EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
// CHANGER
ChangerBase = EFileDevice.Changer,
ChangerGetParameters = (ChangerBase << 16) | (0x0000 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerGetStatus = (ChangerBase << 16) | (0x0001 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerGetProductData = (ChangerBase << 16) | (0x0002 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerSetAccess = (ChangerBase << 16) | (0x0004 << 2) |
EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),
ChangerGetElementStatus = (ChangerBase << 16) | (0x0005 << 2) |
EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14),


ChangerInitializeElementStatus = (ChangerBase << 16) | (0x0006
<< 2) | EMethod.Buffered | (FileAccess.Read << 14),
ChangerSetPosition = (ChangerBase << 16) | (0x0007 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerExchangeMedium = (ChangerBase << 16) | (0x0008 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerMoveMedium = (ChangerBase << 16) | (0x0009 << 2) |
EMethod.Buffered | (FileAccess.Read << 14),
ChangerReinitializeTarget = (ChangerBase << 16) | (0x000A << 2)
| EMethod.Buffered | (FileAccess.Read << 14),
ChangerQueryVolumeTags = (ChangerBase << 16) | (0x000B << 2) |
EMethod.Buffered | ((FileAccess.Read | FileAccess.Write) << 14)
}


[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct CHANGER_ELEMENT
{

/// ELEMENT_TYPE->_ELEMENT_TYPE
public ELEMENT_TYPE ElementType;

/// DWORD->unsigned int
public uint ElementAddress;
}


[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct CHANGER_SET_POSITION
{
// Indicates which transport to move.
public CHANGER_ELEMENT Transport;
// Indicates the final destination of the transport.
public CHANGER_ELEMENT Destination;
// Indicates whether the media currently carried by Transport,
should be flipped.
public int Flip; //!0 == true 0 == false
}


[return:
System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
[System.Runtime.InteropServices.DllImportAttribute("kernel32.dll",
CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError =
true)]
internal static extern bool DeviceIoControl(
[System.Runtime.InteropServices.In]
Microsoft.Win32.SafeHandles.SafeFileHandle hDevice,
[System.Runtime.InteropServices.In] int dwIoControlCode,
[System.Runtime.InteropServices.In] IntPtr lpInBuffer,
[System.Runtime.InteropServices.In] int nInBufferSize,
[System.Runtime.InteropServices.Out] IntPtr lpOutBuffer,
[System.Runtime.InteropServices.In] int nOutBufferSize,
out int lpBytesReturned,
[System.Runtime.InteropServices.In] IntPtr lpOverlapped
);


public Boolean ChangerSetPosition(uint position, Boolean flip)
{
bool status = false;
if (hDevice == null)
return false;
IntPtr pChgSetPos = IntPtr.Zero;

CHANGER_SET_POSITION chSetPos = new CHANGER_SET_POSITION();
CHANGER_ELEMENT transp = new CHANGER_ELEMENT();
CHANGER_ELEMENT dest = new CHANGER_ELEMENT();
transp.ElementType = ELEMENT_TYPE.ChangerTransport;
//This should be the element address the transport wants to be
called from ChangerGetParms
transp.ElementAddress = ChangerParms.FirstTransportNumber;
dest.ElementType = ELEMENT_TYPE.ChangerSlot;
dest.ElementAddress = position; //move it to where?

chSetPos.Transport = transp;
chSetPos.Destination = dest;
if (flip)
chSetPos.Flip = 1; //C true
else
chSetPos.Flip = 0; //C false
Type structType = typeof(CHANGER_SET_POSITION);
int structSize =
System.Runtime.InteropServices.Marshal.SizeOf(structType);
nInBufferSize = (uint)structSize;
//This is now a pointer to a buffer the size of of
CHANGER_SET_POSITION
pChgSetPos =
System.Runtime.InteropServices.Marshal.AllocHGlobal(structSize);
// move the above structure into the pointer location
//as the structure is really just 5 ints we'll copy it using
offsets.
int x = 0;
System.Runtime.InteropServices.Marshal.WriteInt32(pChgSetPos, x
* sizeof(Int32), (int)chSetPos.Transport.ElementType);
x = 1;
System.Runtime.InteropServices.Marshal.WriteInt32(pChgSetPos, x
* sizeof(Int32), (int)chSetPos.Transport.ElementAddress);
x = 2;
System.Runtime.InteropServices.Marshal.WriteInt32(pChgSetPos, x
* sizeof(Int32), (int)chSetPos.Destination.ElementType);
x = 3;
System.Runtime.InteropServices.Marshal.WriteInt32(pChgSetPos, x
* sizeof(Int32), (int)chSetPos.Destination.ElementAddress);
x = 4;
System.Runtime.InteropServices.Marshal.WriteInt32(pChgSetPos, x
* sizeof(Int32), (int)chSetPos.Flip);
//@ this point, the unmanaged pointer data should match the
changer_set_position struct.

status = DeviceIoControl(
hDevice,
(int)EIOControlCode.ChangerSetPosition,
pChgSetPos,
(int)nInBufferSize,
IntPtr.Zero,
0,
out lpBytesReturned,
IntPtr.Zero
);

//do something with the result.

//Nothing here yet because the call to DeviceIoControl returns
false.


//free the memory.
System.Runtime.InteropServices.Marshal.FreeHGlobal(pChgSetPos);
pChgSetPos = IntPtr.Zero;

lastError = GetLastError();
lastErrorMessage = GetLastErrorMessage(lastError);
return status;
}