DeviceIoControl & USB using Managed C++ & C#

Marshalling

And here is one way you can create a managed data structure which you can easily marshal to the unmanaged world.

[StructLayout(LayoutKind::Explicit, Size=18, CharSet=CharSet::Auto)]
public __gc class UsbDeviceDescriptor
{
  public:
    [FieldOffset(0)] System::Byte bLength;
    [FieldOffset(1)] System::Byte bDescriptorType;
    [FieldOffset(2)] System::UInt16 bcdUSB;
    [FieldOffset(4)] System::Byte bDeviceClass;
    [FieldOffset(5)] System::Byte bDeviceSubClass;
    [FieldOffset(6)] System::Byte bDeviceProtocol;
    [FieldOffset(7)] System::Byte bMaxPacketSize0;
    [FieldOffset(8)] System::UInt16 idVendor;
    [FieldOffset(10)] System::UInt16 idProduct;
    [FieldOffset(12)] System::UInt16 bcdDevice;
    [FieldOffset(14)] System::Byte iManufacturer;
    [FieldOffset(15)] System::Byte iProduct;
    [FieldOffset(16)] System::Byte iSerialNumber;
    [FieldOffset(17)] System::Byte bNumConfigurations;
};

Use LayoutKind::Explicit in the attribute and FieldOffset in front of each field to make sure the memory layout is the same as the unmanaged stuct that this one will be marshaled to.  Using CharSet=CharSet::Auto will let the compiler choose between Ansi or Unicode. The CharSet probably doesn't matter on this structure since there are no strings involved.

Here is how you define a managed class in C++:

public __gc class Usb

Now I will show you some bits and pieces of the code which is contained in the Usb class.  If you want to see how it all fits together, take a look at the code in the download.

I wanted to report Win32 errors to my GUI layer, so I added an event.  In hindsight it would have been better to use exceptions for this.  Being able to trigger events in C++ is useful, so I will discuss this here instead of converting the code to use exceptions.  Here is the C++ syntax for your delegate & event, which goes in the public section of your class.

__delegate void ErrorMessage( Object* sender, ErrorEventArgs* e );
__event ErrorMessage* OnErrorMessage;

Here is the code to fire the event:

void ReportError( String* msg )
{
  if( OnErrorMessage )
  {
    OnErrorMessage( this, new ErrorEventArgs( msg ) );
  }
}
void CheckWin32Error()
{
  int errCode = GetLastError();
  if( errCode != 0 )
  {
    char msg[256];
    FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, 0, msg, 256, NULL );
    ReportError( msg );
  }
}

ReportError and CheckWin32Error, are declared as private. Note that the GetLastError and FormatMessage functions from the Win32 API is being used here.  The ability to mix managed and unmanaged code like this is one of the reasons for using managed C++ instead of C# for this code.

If you were writing this code in C# you would want to use:

int errCode = Marshal.GetLastWin32Error();

to obtain the Win32 last error code. We need to obtain a HANDLE to the device driver using CreateFile. The HANDLE is defined as private:

HANDLE _hEzUsb;

And the public function Open, which is just a wrapper around the API function CreateFile, with some error checking.

bool Open( String* driverName )
{
  char* lpFileName = new char[driverName->Length + 10];
  sprintf( lpFileName, "\\\\.\\%s", driverName );
  _hEzUsb = CreateFile( lpFileName,
      GENERIC_WRITE,
      FILE_SHARE_WRITE,
      NULL,
      OPEN_EXISTING,
      0,
      NULL);
  if( _hEzUsb == INVALID_HANDLE_VALUE )
  {
      CheckWin32Error();
  }
  delete lpFileName;
  return( _hEzUsb != INVALID_HANDLE_VALUE );
}

Make sure the HANDLE gets closed, when you are finished.  I have defined a Close function which can be called from the C# code:

void Close() { CloseHandle( _hEzUsb ); }

If you forget to call Close, before calling Open again, strange things will happen.  I spent a lot of time trying to figure out why I the code on the EZ-USB FX board wouldn't run a second time.  After adding calls to Close in my test routines, the problem disappeared.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“There are only 3 numbers of interest to a computer scientist: 1, 0 and infinity”