www.pudn.com > usb2303.rar > Usb2303Device.cpp
// Usb2303Device.cpp // Implementation of Usb2303Device device class // // Generated by DriverWizard version DriverStudio 3.1.0 (Build 1722) // Requires Compuware's DriverWorks classes // #pragma warning(disable:4065) // Allow switch statement with no cases #include#include #include "..\Usb2303Deviceinterface.h" #include "Usb2303.h" #include "Usb2303Device.h" #include "..\usb2303ioctl.h" #pragma hdrstop("Usb2303.pch") void Usb2303Device::GetStringDescriptors(); extern KDebugOnlyTrace t; // Global driver trace object GUID Usb2303Device_Guid = Usb2303Device_CLASS_GUID; //////////////////////////////////////////////////////////////////////// // Usb2303Device::Usb2303Device // // Routine Description: // This is the constructor for the Functional Device Object, or FDO. // It is derived from KPnpDevice, which builds in automatic // dispatching of subfunctions of IRP_MJ_POWER and IRP_MJ_PNP to // virtual member functions. // // Parameters: // Pdo - Physical Device Object - this is a pointer to a system // device object that represents the physical device. // // Unit - Unit number. This is a number to append to the device's // base device name to form the Logical Device Object's name // // Return Value: // None // // Comments: // The object being constructed contains a data member (m_Lower) of type // KPnpLowerDevice. By initializing it, the driver binds the FDO to the // PDO and creates an interface to the upper edge of the system class driver. // Usb2303Device::Usb2303Device(PDEVICE_OBJECT Pdo, ULONG Unit) : KPnpDevice(Pdo, &Usb2303Device_Guid) { t << "Entering Usb2303Device::Usb2303Device (constructor)\n"; // Check constructor status if ( ! NT_SUCCESS(m_ConstructorStatus) ) { return; } // Remember our unit number m_Unit = Unit; // Initialize the lower device m_Lower.Initialize(this, Pdo); // Initialize the interface object. The wizard generates code // to support a single interface. You may create and add additional // interfaces. By default, the wizard uses InterfaceNumber 0 (the // first interface descriptor), ConfigurationValue 1 (the first // configuration descriptor), and initial interface alternate // setting of 0. If your device has multiple interfaces or alternate // settings for an interface, you can add additional KUsbInterface // objects or adjust the parameters passed to this function. m_Interface.Initialize( m_Lower, //KUsbLowerDevice 0, //InterfaceNumber 1, //ConfigurationValue 0 //Initial Interface Alternate Setting ); // Initialize each Pipe object m_Endpoint1IN.Initialize(m_Lower, 0x81, 10); m_Endpoint2OUT.Initialize(m_Lower, 0x2, 64); m_Endpoint3IN.Initialize(m_Lower, 0x83, 64); // Inform the base class of the lower edge device object SetLowerDevice(&m_Lower); // Initialize the PnP Policy settings to the "standard" policy SetPnpPolicy(); // TODO: Customize the PnP Policy for this device by setting // flags in m_Policies. // Initialize the Power Policy settings to the "standard" policy SetPowerPolicy(); // TODO: Customize the Power Policy for this device by setting // flags in m_PowerPolicies. } //////////////////////////////////////////////////////////////////////// // Usb2303Device::~Usb2303Device // // Routine Description: // This is the destructor for the Functional Device Object, or FDO. // // Parameters: // None // // Return Value: // None // // Comments: // None // Usb2303Device::~Usb2303Device() { t << "Entering Usb2303Device::~Usb2303Device() (destructor)\n"; } //////////////////////////////////////////////////////////////////////// // Usb2303Device::DefaultPnp // // Routine Description: // Default handler for IRP_MJ_PNP // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result returned from lower device // // Comments: // This routine just passes the IRP through to the lower device. It is // the default handler for IRP_MJ_PNP. IRPs that correspond to // any virtual members of KpnpDevice that handle minor functions of // IRP_MJ_PNP and that are not overridden get passed to this routine. // NTSTATUS Usb2303Device::DefaultPnp(KIrp I) { t << "Entering Usb2303Device::DefaultPnp\n" << I << EOL; I.ForceReuseOfCurrentStackLocationInCalldown(); return m_Lower.PnpCall(this, I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::DefaultPower // // Routine Description: // Default handler for IRP_MJ_POWER // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result returned from lower device // // Comments: // This routine just passes the IRP through to the lower device. It is // the default handler for IRP_MJ_POWER. // NTSTATUS Usb2303Device::DefaultPower(KIrp I) { t << "Entering Usb2303Device::DefaultPower\n" << I << EOL; I.IndicatePowerIrpProcessed(); I.CopyParametersDown(); return m_Lower.PnpPowerCall(this, I); } //////////////////////////////////////////////////////////////////////////////// // Usb2303Device::SystemControl // // Routine Description: // Default handler for IRP_MJ_SYSTEM_CONTROL // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result returned from lower device // // Comments: // This routine just passes the IRP through to the next device since this driver // is not a WMI provider. // NTSTATUS Usb2303Device::SystemControl(KIrp I) { t << "Entering Usb2303Device::SystemControl\n"; I.ForceReuseOfCurrentStackLocationInCalldown(); return m_Lower.PnpCall(this, I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::OnStartDevice // // Routine Description: // Handler for IRP_MJ_PNP subfcn IRP_MN_START_DEVICE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // Typically, the driver sends a SET CONFIGURATION request for the // USB device by using KUsbLowerDevice::ActivateConfiguration with // the ConfigurationValue to activate. The wizard generates code to // support a single configuration. You may create and add additional // configurations. // NTSTATUS Usb2303Device::OnStartDevice(KIrp I) { t << "Entering Usb2303Device::OnStartDevice\n"; NTSTATUS status = STATUS_UNSUCCESSFUL; AC_STATUS acStatus = AC_SUCCESS; I.Information() = 0; // The default Pnp policy has already cleared the IRP with the lower device //By default, the wizard passes a ConfigurationValue of 1 to //ActivateConfiguration(). This corresponds to the first configuration //that the device reports in its configuration descriptor. If the device //supports multiple configurations, pass the appropriate //ConfigurationValue of the configuration to activate here. acStatus = m_Lower.ActivateConfiguration( 1 // ConfigurationValue 1 (the first configuration) ); switch (acStatus) { case AC_SUCCESS: t << "USB Configuration OK\n"; GetStringDescriptors(); status = STATUS_SUCCESS; break; case AC_COULD_NOT_LOCATE_INTERFACE: t << "Could not locate interface\n"; break; case AC_COULD_NOT_PRECONFIGURE_INTERFACE: t << "Could not get configuration descriptor\n"; break; case AC_CONFIGURATION_REQUEST_FAILED: t << "Board did not accept configuration URB\n"; break; case AC_FAILED_TO_INITIALIZE_INTERFACE_OBJECT: t << "Failed to initialize interface object\n"; break; case AC_FAILED_TO_GET_DESCRIPTOR: t << "Failed to get device descriptor\n"; break; case AC_FAILED_TO_OPEN_PIPE_OBJECT: //NOTE: this may not be an error. It could mean that //the device has an endpoint for which a KUsbPipe object has //not been instanced. If the intention is to not use this endpoint, //then it's likely not a problem. status = STATUS_SUCCESS; t << "Failed to open pipe object\n"; break; default: t << "Unexpected error activating USB configuration\n"; break; } return status; // base class completes the IRP } //////////////////////////////////////////////////////////////////////// // Usb2303Device::OnStopDevice // // Routine Description: // Handler for IRP_MJ_PNP subfcn IRP_MN_STOP_DEVICE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // The system calls this when the device is stopped. // The driver should release any hardware resources // in this routine. // // The base class passes the irp to the lower device. // NTSTATUS Usb2303Device::OnStopDevice(KIrp I) { NTSTATUS status = STATUS_SUCCESS; t << "Entering Usb2303Device::OnStopDevice\n"; m_Lower.DeActivateConfiguration(); // TODO: Add device-specific code to stop your device return status; // The following macro simply allows compilation at Warning Level 4 // If you reference this parameter in the function simply remove the macro. UNREFERENCED_PARAMETER(I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::OnRemoveDevice // // Routine Description: // Handler for IRP_MJ_PNP subfcn IRP_MN_REMOVE_DEVICE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // The system calls this when the device is removed. // Our PnP policy will take care of // (1) giving the IRP to the lower device // (2) detaching the PDO // (3) deleting the device object // NTSTATUS Usb2303Device::OnRemoveDevice(KIrp I) { t << "Entering Usb2303Device::OnRemoveDevice\n"; // Device removed, release the system resources used by the USB lower device. m_Lower.ReleaseResources(); // TODO: Add device-specific code to remove your device return STATUS_SUCCESS; // The following macro simply allows compilation at Warning Level 4 // If you reference this parameter in the function simply remove the macro. UNREFERENCED_PARAMETER(I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::OnDevicePowerUp // // Routine Description: // Handler for IRP_MJ_POWER with minor function IRP_MN_SET_POWER // for a request to go to power on state from low power state // // Parameters: // I - IRP containing POWER request // // Return Value: // NTSTATUS - Status code indicating success or failure // // Comments: // This routine implements the OnDevicePowerUp function. // This function was called by the framework from the completion // routine of the IRP_MJ_POWER dispatch handler in KPnpDevice. // The bus driver has completed the IRP and this driver can now // access the hardware device. // This routine runs at dispatch level. // NTSTATUS Usb2303Device::OnDevicePowerUp(KIrp I) { NTSTATUS status = STATUS_SUCCESS; t << "Entering Usb2303Device::OnDevicePowerUp\n"; // TODO: Service the device. // Restore any context to the hardware device that // was saved during the handling of a power down request. // See the OnDeviceSleep function. // Do NOT complete this IRP. // return status; // The following macro simply allows compilation at Warning Level 4 // If you reference this parameter in the function simply remove the macro. UNREFERENCED_PARAMETER(I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::OnDeviceSleep // // Routine Description: // Handler for IRP_MJ_POWER with minor function IRP_MN_SET_POWER // for a request to go to a low power state from a high power state // // Parameters: // I - IRP containing POWER request // // Return Value: // NTSTATUS - Status code indicating success or failure // // Comments: // This routine implements the OnDeviceSleep function. // This function was called by the framework from the IRP_MJ_POWER // dispatch handler in KPnpDevice prior to forwarding to the PDO. // The hardware has yet to be powered down and this driver can now // access the hardware device. // This routine runs at passive level. // NTSTATUS Usb2303Device::OnDeviceSleep(KIrp I) { NTSTATUS status = STATUS_SUCCESS; t << "Entering Usb2303Device::OnDeviceSleep\n"; // TODO: Service the device. // Save any context to the hardware device that will be required // during a power up request. See the OnDevicePowerUp function. // Do NOT complete this IRP. The base class handles forwarding // this IRP to the PDO. // return status; // The following macro simply allows compilation at Warning Level 4 // If you reference this parameter in the function simply remove the macro. UNREFERENCED_PARAMETER(I); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::Create // // Routine Description: // Handler for IRP_MJ_CREATE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // NTSTATUS Usb2303Device::Create(KIrp I) { NTSTATUS status; t << "Entering Usb2303Device::Create, " << I << EOL; // TODO: Add driver specific create handling code here // Generally a create IRP is targeted at our FDO, so we don't need // to pass it down to the PDO. We have found for some devices, the // PDO is not expecting this Irp and returns an error code. // The default wizard code, therefore completes the Irp here using // PnpComplete(). The following commented code could be used instead // of PnpComplete() to pass the Irp to the PDO, which would complete it. // // I.ForceReuseOfCurrentStackLocationInCalldown(); // status = m_Lower.PnpCall(this, I); status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT); t << "Usb2303Device::Create Status " << (ULONG)status << EOL; return status; } //////////////////////////////////////////////////////////////////////// // Usb2303Device::Close // // Routine Description: // Handler for IRP_MJ_CLOSE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // NTSTATUS Usb2303Device::Close(KIrp I) { NTSTATUS status; t << "Entering Usb2303Device::Close, " << I << EOL; // TODO: Add driver specific close handling code here // Generally a close IRP is targeted at our FDO, so we don't need // to pass it down to the PDO. We have found for some devices, the // PDO is not expecting this Irp and returns an error code. // The default wizard code, therefore completes the Irp here using // PnpComplete(). The following commented code could be used instead // of PnpComplete() to pass the Irp to the PDO, which would complete it. // // I.ForceReuseOfCurrentStackLocationInCalldown(); // status = m_Lower.PnpCall(this, I); status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT); t << "Usb2303Device::Close Status " << (ULONG)status << EOL; return status; } //////////////////////////////////////////////////////////////////////// // Usb2303Device::Cleanup // // Routine Description: // Handler for IRP_MJ_CLEANUP // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // NTSTATUS Usb2303Device::CleanUp(KIrp I) { t << "Entering CleanUp, " << I << EOL; // TODO: Insert your code to respond to the CLEANUP message. return I.PnpComplete(this, STATUS_SUCCESS); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::Read // // Routine Description: // Handler for IRP_MJ_READ // // Parameters: // I Current IRP // // Return Value: // NTSTATUS Result code // // Comments: // This routine handles read requests. // // The KPnpDevice class handles restricting IRP flow // if the device is stopping or being removed. // NTSTATUS Usb2303Device::Read(KIrp I) { t << "Entering Usb2303Device::Read, " << I << EOL; // TODO: Check the incoming request. Replace "FALSE" in the following // line with a check that returns TRUE if the request is not valid. if (FALSE) // If (Request is invalid) { // Invalid parameter in the Read request I.Information() = 0; return I.PnpComplete(this, STATUS_INVALID_PARAMETER); } // Always ok to read 0 elements. if (I.ReadSize() == 0) { I.Information() = 0; return I.PnpComplete(this, STATUS_SUCCESS); } NTSTATUS status = STATUS_SUCCESS; KMemory Mem(I.Mdl()); // Declare a memory object // Use the memory object to create a pointer to the caller's buffer PUCHAR pBuffer = (PUCHAR) Mem.VirtualAddress(); ULONG dwTotalSize = I.ReadSize(CURRENT); // Requested read size ULONG dwBytesRead = 0; // Count of bytes read // TODO: If the read can be satisfied immediately, set the Information // and Status fields now, then call NextIrp to complete this IRP // and start processing the next IRP in the queue. // TODO: If the data is not yet available, initiate a request to the // physical device here, and defer the Information, Status, // and NextIrp handling until the hardware indicates that the // read is complete. Typically, this might be handled in a // DPC that is called after the hardware finishes transferring // the data. // TODO: To satisfy the read now, transfer data from the device to // caller's buffer at "pBuffer". Then, indicate how much data was // transferred: I.Information() = dwBytesRead; return I.PnpComplete(this, status); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::Write // // Routine Description: // Handler for IRP_MJ_WRITE // // Parameters: // I - Current IRP // // Return Value: // NTSTATUS - Result code // // Comments: // This routine handles write requests. // // The KPnpDevice class handles restricting IRP flow // if the device is stopping or being removed. // NTSTATUS Usb2303Device::Write(KIrp I) { t << "Entering Usb2303Device::Write, " << I << EOL; // TODO: Check the incoming request. Replace "FALSE" in the following // line with a check that returns TRUE if the request is not valid. if (FALSE) { // Invalid parameter in the Write request I.Information() = 0; return I.PnpComplete(this, STATUS_INVALID_PARAMETER); } // Always ok to write 0 elements. if (I.WriteSize() == 0) { I.Information() = 0; return I.PnpComplete(this, STATUS_SUCCESS); } NTSTATUS status = STATUS_SUCCESS; KMemory Mem(I.Mdl()); // Declare a memory object // Use the memory object to create a pointer to the caller's buffer PUCHAR pBuffer = (PUCHAR) Mem.VirtualAddress(); ULONG dwTotalSize = I.WriteSize(CURRENT); ULONG dwBytesSent = 0; // TODO: If the write can be satisfied immediately, set the Information // and Status fields now, then call NextIrp to complete this IRP // and start processing the next IRP in the queue. // TODO: If the device cannot accept all of the data yet, initiate a // request to the physical device here, and defer the Information, // Status, and NextIrp handling until the hardware indicates that // the write is complete. Typically, this might be handled in a // DPC that is called after the hardware finishes transferring // the data. // TODO: To satisfy the write now, transfer data to the device // from caller's buffer at "pBuffer". Then, indicate how much // data was transferred: I.Information() = dwBytesSent; return I.PnpComplete(this, status); } //////////////////////////////////////////////////////////////////////// // Usb2303Device::DeviceControl // // Routine Description: // Handler for IRP_MJ_DEVICE_CONTROL // // Parameters: // I - Current IRP // // Return Value: // None // // Comments: // This routine is the first handler for Device Control requests. // The KPnpDevice class handles restricting IRP flow // if the device is stopping or being removed. // NTSTATUS Usb2303Device::DeviceControl(KIrp I) { NTSTATUS status; t << "Entering Usb2303Device::Device Control, " << I << EOL; switch (I.IoctlCode()) { case USB2303_IOCTL_Test: status = USB2303_IOCTL_Test_Handler(I); break; default: // Unrecognized IOCTL request status = STATUS_INVALID_PARAMETER; break; } // If the IRP's IOCTL handler deferred processing using some driver // specific scheme, the status variable is set to STATUS_PENDING. // In this case we simply return that status, and the IRP will be // completed later. Otherwise, complete the IRP using the status // returned by the IOCTL handler. if (status == STATUS_PENDING) { return status; } else { return I.PnpComplete(this, status); } } //////////////////////////////////////////////////////////////////////// // Usb2303Device::USB2303_IOCTL_Test_Handler // // Routine Description: // Handler for IO Control Code USB2303_IOCTL_Test // // Parameters: // I - IRP containing IOCTL request // // Return Value: // NTSTATUS - Status code indicating success or failure // // Comments: // This routine implements the USB2303_IOCTL_Test function. // This routine runs at passive level. // NTSTATUS Usb2303Device::USB2303_IOCTL_Test_Handler(KIrp I) { NTSTATUS status = STATUS_SUCCESS; t << "Entering Usb2303Device::USB2303_IOCTL_Test_Handler, " << I << EOL; // TODO: Verify that the input parameters are correct // If not, return STATUS_INVALID_PARAMETER // TODO: Handle the the USB2303_IOCTL_Test request, or // defer the processing of the IRP (i.e. by queuing) and set // status to STATUS_PENDING. // TODO: Assuming that the request was handled here. Set I.Information // to indicate how much data to copy back to the user. I.Information() = 0; return status; } void Usb2303Device::GetStringDescriptors() { NTSTATUS status = STATUS_SUCCESS; PWCHAR String = (PWCHAR) new (NonPagedPool) WCHAR[MAXIMUM_USB_STRING_LENGTH]; if(NULL == String) { //error during allocation t << "ERROR: Memory allocation error in GetStringDescriptors().\n"; return; } t << "***** PL2303 Device Descriptors *****\n"; USB_DEVICE_DESCRIPTOR desc; status = m_Lower.GetDeviceDescriptor( &desc ); if ( !NT_SUCCESS(status) ) { t << "ERROR: Could not get Device Descriptor.\n"; delete String; return; } t << "Index of Manufacturer string = " << desc.bLength << "\n"; t << "Index of Manufacturer string = " << desc.bDescriptorType << "\n"; t << "Index of Manufacturer string = " << desc.bcdUSB << "\n"; t << "Index of Manufacturer string = " << desc.bDeviceClass << "\n"; t << "Index of Manufacturer string = " << desc.bDeviceSubClass << "\n"; t << "Index of Manufacturer string = " << desc.bDeviceProtocol << "\n"; t << "Index of Manufacturer string = " << desc.bMaxPacketSize0 << "\n"; t << "Index of Manufacturer string = " << desc.idVendor << "\n"; t << "Index of Manufacturer string = " << desc.idProduct << "\n"; t << "Index of Manufacturer string = " << desc.bcdDevice << "\n"; t << "Index of Manufacturer string = " << desc.iManufacturer << "\n"; t << "Index of Product string = " << desc.iProduct << "\n"; t << "Index of Serial Number string = " << desc.iSerialNumber << "\n"; t << "Number of configurations = " << desc.bNumConfigurations << "\n"; t << "Index of configuration string = " << m_Lower.m_Config->iConfiguration << "\n"; t << "***** PL2303 Configuration Descriptors *****\n"; t << "Index of config string = " << m_Lower.m_Config->bLength << "\n"; t << "Index of config string = " << m_Lower.m_Config->bDescriptorType << "\n"; t << "Index of config string = " << m_Lower.m_Config->wTotalLength << "\n"; t << "Index of config string = " << m_Lower.m_Config->bNumInterfaces << "\n"; t << "Index of config string = " << m_Lower.m_Config->iConfiguration << "\n"; t << "Index of config string = " << m_Lower.m_Config->bmAttributes << "\n"; t << "Index of config string = " << m_Lower.m_Config->MaxPower << "\n"; t << "***** PL2303 interface Descriptors *****\n"; t << "Index of interface string = " << m_Interface.m_Information->Length << "\n"; t << "Index of interface string = " << m_Interface.m_Information->InterfaceNumber << "\n"; t << "Index of interface string = " << m_Interface.m_Information->AlternateSetting << "\n"; t << "Index of interface string = " << m_Interface.m_Information->NumberOfPipes << "\n"; t << "Index of interface string = " << m_Interface.m_Information->Class << "\n"; t << "Index of interface string = " << m_Interface.m_Information->SubClass << "\n"; t << "Index of interface string = " << m_Interface.m_Information->Protocol << "\n"; t << "***** PL2303 Endpoint1IN Descriptors *****\n"; t << "Index of m_Endpoint1IN string->EndpointAddress = " << m_Endpoint1IN.m_EndpointAddress << "\n"; t << "Index of m_Endpoint1IN string->MaximumPacketSize = " << m_Endpoint1IN.MaximumPacketSize() << "\n"; t << "Index of m_Endpoint1IN string->PollInterval = " << m_Endpoint1IN.PollInterval() << "\n"; t << "Index of m_Endpoint1IN string->PipeType = " << m_Endpoint1IN.Type() << "\n"; t << "***** PL2303 Endpoint2OUT Descriptors *****\n"; t << "Index of m_Endpoint2OUT string->EndpointAddress = " << m_Endpoint2OUT.m_EndpointAddress << "\n"; t << "Index of m_Endpoint2OUT string->MaximumPacketSize = " << m_Endpoint2OUT.MaximumPacketSize() << "\n"; t << "Index of m_Endpoint2OUT string->PollInterval = " << m_Endpoint2OUT.PollInterval() << "\n"; t << "Index of m_Endpoint2OUT string->PipeType = " << m_Endpoint2OUT.Type() << "\n"; t << "***** PL2303 Endpoint3IN Descriptors *****\n"; t << "Index of m_Endpoint3IN string->EndpointAddress = " << m_Endpoint3IN.m_EndpointAddress << "\n"; t << "Index of m_Endpoint3IN string->MaximumPacketSize = " << m_Endpoint3IN.MaximumPacketSize() << "\n"; t << "Index of m_Endpoint3IN string->PollInterval = " << m_Endpoint3IN.PollInterval() << "\n"; t << "Index of m_Endpoint3IN string->PipeType = " << m_Endpoint3IN.Type() << "\n"; t << "***** PL2303 String Descriptors *****\n"; for(UCHAR i = 0; i <= NUM_STRING_DESCRIPTORS; i++) { RtlZeroMemory(String, MAXIMUM_USB_STRING_LENGTH * sizeof(WCHAR)); if(NT_SUCCESS(status = m_Lower.GetStringDescriptor( i, String, MAXIMUM_USB_STRING_LENGTH, 0x409))) { t << "String " << i << ": " << String << "\n"; } else { t << "GetStringDescriptor returns status = " << ULONG(status) << "\n"; } } t << "\n"; delete String; }