pinvoke using structs

E

Einar Høst

Hi,

I'm reading data from a tape device using p/invoke. It's working pretty
well, but when I'm trying to get data about the tape device, I'm doing
something wrong. I believe it has something to do with the struct I'm
passing in, but I just can't figure out what. Any help would be much
appreciated. I'm an absolute interop newbie, so please bear with me.

The Win32 API signature looks like this:

DWORD GetTapeParameters(
HANDLE hDevice,
DWORD dwOperation,
LPDWORD lpdwSize,
LPVOID lpTapeInformation
);

I've translated that into the following import signature:

[DllImport("kernel32.dll", SetLastError=true)]
static extern uint GetTapeParameters(
IntPtr hDevice, // Handle to tape device.
uint dwOperation, // Parameter type (media/drive).
out uint lpdwSize, // Size of parameter buffer.
IntPtr lpTapeInformation // Pointer to parameter struct.
);

The media parameter struct originally looks like this:

typedef struct _TAPE_GET_MEDIA_PARAMETERS {
LARGE_INTEGER Capacity;
LARGE_INTEGER Remaining;
DWORD BlockSize;
DWORD PartitionCount;
BOOLEAN WriteProtected;
} TAPE_GET_MEDIA_PARAMETERS,
* PTAPE_GET_MEDIA_PARAMETERS;

Consulting winnt.h (I've never programmed Windows pre-.NET), I found that
LARGE_INTEGER was a 64-bit signed integer, DWORD a 32-bit unsigned integer,
and BOOLEAN a single byte. So I translated the struct into the following:

[StructLayout(LayoutKind.Sequential)]
internal struct MediaParameters
{
internal Int64 Capacity;
internal Int64 Remaining;
internal UInt32 BlockSize;
internal UInt32 PartitionCount;
internal Byte WriteProtected;
}

I try to make all this work from a C# wrapper method that calls the imported
Win32 API method.

internal MediaParameters GetTapeMediaParameters()
{
// Keep object alive for the remainder of this method.
GC.KeepAlive(this);

MediaParameters param = new MediaParameters();
IntPtr paramPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, paramPtr, false);
uint size;
// Call imported Win32 API method to populate struct.
// 0 specifies media parameters (as opposed to drive parameters).
SystemErrorCode code = (SystemErrorCode) GetTapeParameters(_hTape, 0, out
size, paramPtr);
Debug.Assert(code == SystemErrorCode.NO_ERROR,
"Failed trying to get tape media parameters!",
"Error code: " + code.ToString());
// Should I call Marshal.PtrToStructure here?
Marshal.FreeCoTaskMem(paramPtr);
}

However, the Assert statement fails, giving me system error code 234
(ERROR_MORE_DATA).

If someone could help me, I'd be very very grateful!

Regards,
Einar
 
B

BMermuys

Hi,

It's not clear in msdn, but since it's probely complaining about buffer size
and you didn't specify a buffer size, you might try to do that. Change the
size parameter from out to ref. And pass the size of the struct.

uint size = Marshal.SizeOf(typeof(MediaParameters));
IntPtr paramPtr = Marshal.AllocHGlobal( size );

SystemErrorCode code =
(SystemErrorCode) GetTapeParameters(_hTape, 0, ref size, paramPtr);

Debug.Assert(code == SystemErrorCode.NO_ERROR,
"Failed trying to get tape media parameters!", "Error code: " +
code.ToString());

// Should I call Marshal.PtrToStructure here? Yes.
MediaParameters mp = (MediaParameters)Marshal.PtrToStructure( paramPtr,
typeof(MediaParameters));

Marshal.FreeHGlobal( paramPtr );

// use mp


If it still fails check the size parameter after function returns and
compare with the number of bytes in the struct.

HTH,
greetings
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top