Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Marshalling structures of unknown length (CMDINPARAMS, CMDOUTPARAMS)

71 views
Skip to first unread message

Sam Morris

unread,
Apr 21, 2002, 11:37:10 AM4/21/02
to
I am trying to read the SMART data from my hard disks from inside a C#
program, doing so with the help of
<http://support.microsoft.com/support/kb/articles/Q208/0/48.ASP?LN=EN-US&SD=
gn&FR=0>. I have been able to open a handle and am now trying to enable
SMART operations.

I am having trouble with the CMDINPARAMS and CMDOUTPARAMS structures
however. They both have as their last member a BYTE array with one element,
that is of arbitrary length.

I need to pass the structures as parameters in a DeviceIOControl call. I
need to set values in the byte array of the CMDINPARAMS structure, and read
values from the byte array of the CMDOUTPARAMS structure.

The structure definitions I am suing are as follows:

struct SENDCMDINPARAMS
{
public uint cBufferSize; // Size of bBuffer in bytes
public IDEREGS irDriveRegs; // Structure with drive register values
public byte bDriveNumber; // Physical drive number to send command to
(0,1,2,3)
private byte reserved0; //How do you specify an array of fixed length
here?
private byte reserved1;
private byte reserved2;
private uint reserverd0;
private uint reserverd1;
private uint reserverd2;
private uint reserverd3;
public byte[] data; // Buffer of arbitrary length in which to store the
data to be written to drive.
}

struct SENDCMDOUTPARAMS
{
public uint cBufferSize; // Size of bBuffer in bytes
public DRIVERSTATUS DriverStatus; // Driver status structure
public byte[] data; // Buffer of arbitrary length in which to store the
data read from the drive
}

This fails at runtime during the Marshal.AllohHGlobal method: "Type
smarttest.SENDCMDINPARAMS can not be marshalled as an unmanaged structure;
no meaningful size or offset can be computed."

The entire program is reproduced below. Please could someone point me in the
right direction?

--
/* Sam Morris */

using System;
using System.Runtime.InteropServices;

namespace smarttest
{
/* Structures */

struct GETVERSIONOUTPARAMS
{
public byte bVersion; // Binary driver version (For this specification,
it will be 1)
public byte bRevision; // Binary driver revision (For this
specification, it will be 01)
public byte reserved0; // Reserved
public byte bIDEDeviceMap; // Bit map of all IDE devices, including ATAPI
(IDE CD-ROM)
public uint capabilities; // Bit mask of driver capabilities; bit 2 ==
smart
private uint reserved1; // Reserved for future expansion
private uint reserved2;
private uint reserved3;
private uint reserved4;
}

struct SENDCMDINPARAMS
{
public uint cBufferSize; // Size of bBuffer in bytes
public IDEREGS irDriveRegs; // Structure with drive register values
public byte bDriveNumber; // Physical drive number to send command to
(0,1,2,3)
private byte reserved0;
private byte reserved1;
private byte reserved2;
private uint reserverd0;
private uint reserverd1;
private uint reserverd2;
private uint reserverd3;
public byte[] data; // Buffer of arbitrary length in which to store the
data to be written to drive.
}

struct SENDCMDOUTPARAMS
{
public uint cBufferSize; // Size of bBuffer in bytes
public DRIVERSTATUS DriverStatus; // Driver status structure
public byte[] data; // Buffer of arbitrary length in which to store
the data read from the drive
}

struct IDEREGS
{
public byte bFeaturesReg; // Used for specifying DFP "sub commands".
public byte bSectorCountReg; // IDE sector count register
public byte bSectorNumberReg; // IDE sector number register
public byte bCylLowReg; // IDE low order cylinder value
public byte bCylHighReg; // IDE high order cylinder value
public byte bDriveHeadReg; // IDE drive/head register
public byte bCommandReg; // Actual IDE command. Checked for validity by
driver.
public byte bReserved; // Reserved for future use. Must be zero.
}

struct DRIVERSTATUS
{
public byte bDriverError; // Error code from driver, or 0 if no error
public byte bIDEStatus; // Contents of IDE error register
private byte reserved0;
private byte reserved1;
private uint reserved2;
private uint reserved3;
}

class SmartTest
{
/* Constants */

private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint OPEN_EXISTING = 3;
private const uint FILE_SHARE_READ = 1;
private const uint FILE_SHARE_WRITE = 2;

private const int INVALID_HANDLE_VALUE = -1;

private const uint DFP_GET_VERSION = 0x00074080;
private const uint DFP_SEND_DRIVE_COMMAND = 0x0007c084;

private const byte SMART_ENABLE_SMART_OPERATIONS = 0xd8;
private const byte IDE_EXECUTE_SMART_FUNCTION = 0xb0;
private const byte SMART_CYL_LOW = 0x4f;
private const byte SMART_CYL_HIGH = 0xc2;

/* Methods */

[DllImport("kernel32", SetLastError = true)]
private static extern uint CreateFile(string filename, uint access, uint
sharemode, IntPtr security_attributes, uint creation, uint flags, IntPtr
template);

[DllImport("kernel32")]
private static extern bool DeviceIoControl(IntPtr hDevice, uint
dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer,
uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped);

static void Main(string[] args)
{
string device;
if (args.Length == 1 && args[0].Trim() != "")
{
device = args[0];
}
else
{
device = "PhysicalDrive0";
}
Console.WriteLine("Querying device " + device + "...");

IntPtr SmartHandle = new IntPtr(CreateFile("\\\\.\\" + device,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
(IntPtr)null, OPEN_EXISTING, 0, (IntPtr)null));
if (SmartHandle.ToInt32() == INVALID_HANDLE_VALUE)
{
Console.Error.WriteLine("Could not open handle (" +
Marshal.GetLastWin32Error() + ")");
}
else
{
//Console.WriteLine("Got handle " + SmartHandle);

uint bytesReturned = 0;
IntPtr versionParamsPointer =
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(GETVERSIONOUTPARAMS)));

if (DeviceIoControl(SmartHandle, DFP_GET_VERSION, (IntPtr)null, 0,
versionParamsPointer, (uint)Marshal.SizeOf(typeof(GETVERSIONOUTPARAMS)), ref
bytesReturned, (IntPtr)null))
{
GETVERSIONOUTPARAMS versionParams =
(GETVERSIONOUTPARAMS)Marshal.PtrToStructure(versionParamsPointer,
typeof(GETVERSIONOUTPARAMS));

if ((versionParams.capabilities & 0x2) == 0x2)
{
Console.WriteLine("Drive supports SMART");

byte bDriveNum = 0;
SENDCMDINPARAMS cmdIn = new SENDCMDINPARAMS();
SENDCMDOUTPARAMS cmdOut;

//Set up data structures for the SMART enable command
cmdIn.cBufferSize = 0;
cmdIn.irDriveRegs.bFeaturesReg = SMART_ENABLE_SMART_OPERATIONS;
cmdIn.irDriveRegs.bSectorCountReg = 1;
cmdIn.irDriveRegs.bSectorNumberReg = 1;
cmdIn.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
cmdIn.irDriveRegs.bCylHighReg = SMART_CYL_HIGH;

//Compute the drive number
cmdIn.irDriveRegs.bDriveHeadReg = (byte)(0xa0 | ((bDriveNum & 1) <<
4));
cmdIn.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
cmdIn.bDriveNumber = bDriveNum;

//Export cmdIn
Marshal.
IntPtr cmdInPointer =
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SENDCMDINPARAMS)));
Marshal.StructureToPtr(cmdIn, cmdInPointer, true);

IntPtr cmdOutPointer =
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SENDCMDOUTPARAMS)));

if (DeviceIoControl(SmartHandle, DFP_SEND_DRIVE_COMMAND, cmdInPointer,
(uint)Marshal.SizeOf(typeof(SENDCMDINPARAMS)) - 1, cmdOutPointer,
(uint)Marshal.SizeOf(typeof(SENDCMDOUTPARAMS)) - 1, ref bytesReturned,
(IntPtr)null) )
{
//Import cmdOut
cmdOut = (SENDCMDOUTPARAMS)Marshal.PtrToStructure(cmdOutPointer,
typeof(SENDCMDOUTPARAMS));
}
else
{
cmdOut.DriverStatus.bDriverError = 0;
cmdOut.DriverStatus.bIDEStatus = 0;
Console.Error.WriteLine("Could not enable SMART on drive {0}:
bDriverError=0x{1}, bIDEStatus=0x{2}", bDriveNum,
cmdOut.DriverStatus.bDriverError, cmdOut.DriverStatus.bIDEStatus);
}

//Free memory
Marshal.FreeHGlobal(cmdInPointer);
Marshal.FreeHGlobal(cmdOutPointer);
}
else
{
Console.Error.WriteLine("Drive does not support SMART");
}
}
else
{
Console.Error.WriteLine("Could not query device for drive information;
is it a hard disk? (" + Marshal.GetLastWin32Error() + ")");
}

//Free memory
Marshal.FreeHGlobal(versionParamsPointer);
}

Console.ReadLine();
}
}
}

Nicholas Paldino [.NET MVP]

unread,
Apr 21, 2002, 12:04:42 PM4/21/02
to
Sam,

What you are going to have to do is declare the byte array as an IntPtr.
When you want to send the information to the function, you will have to
allocate the unmanaged memory block using one of the static Alloc methods on
the Marshal class. This will return an IntPtr that will point to the area
in memory. You can also use the static methods on the Marshal class to
write the byte values to the pointer. When getting values back from
unmanaged memory, you will have to do the same thing, but in reverse, using
the read methods to access the IntPtr that is returned to you get elements
in the byte array.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- casp...@caspershouse.com

"Sam Morris" <s...@netcity.co.FROGBLASTIT.uk> wrote in message
news:a9um7j$7rq$1...@godfrey.mcc.ac.uk...

Eliyahu (Vyacheslav) Biktagirov

unread,
Apr 21, 2002, 12:51:12 PM4/21/02
to
Look csharp group for my answer..

Sam Morris

unread,
Apr 22, 2002, 9:55:48 PM4/22/02
to
Until I get the call working I am going with declaring the buffer with a
fixed length of 512 bytes; after I get that working I will try and make it
more elegant along the lines you suggest.

Thanks for helping!

--
/* Sam Morris */

"Nicholas Paldino [.NET MVP]" <casp...@caspershouse.com> wrote in message
news:OpUG09U6BHA.2188@tkmsftngp07...

0 new messages