WinUSB: Merge upstream changes

This commit is contained in:
Gus
2021-02-08 17:08:46 +01:00
parent f438d0dd71
commit 8630a89553
8 changed files with 203 additions and 139 deletions
+33 -7
View File
@@ -97,23 +97,48 @@ namespace MadWizard.WinUSBNet.API
return deviceDesc; return deviceDesc;
} }
public string GetStringDescriptor(byte index) private int ReadStringDescriptor(byte index, ushort languageID, byte[] buffer)
{ {
byte[] buffer = new byte[256];
uint transfered; uint transfered;
bool success = WinUsb_GetDescriptor(_winUsbHandle, USB_STRING_DESCRIPTOR_TYPE, bool success = WinUsb_GetDescriptor(_winUsbHandle, USB_STRING_DESCRIPTOR_TYPE,
index, 0, buffer, (uint)buffer.Length, out transfered); index, languageID, buffer, (uint)buffer.Length, out transfered);
if (!success) if (!success)
throw APIException.Win32("Failed to get USB string descriptor (" + index + "): 0x" + Marshal.GetLastWin32Error().ToString("X8")); throw APIException.Win32("Failed to get USB string descriptor (" + index + ").");
int length = buffer[0] - 2; if (transfered == 0)
if (length <= 0) throw new APIException("No data returned when reading USB descriptor.");
int length = buffer[0];
if (length != transfered)
throw new APIException("Unexpected length when reading USB descriptor.");
return length;
}
public ushort[] GetSupportedLanguageIDs()
{
byte[] buffer = new byte[256];
int length = ReadStringDescriptor(0, 0, buffer);
length -= 2; // Skip length byte and descriptor type
if (length < 0 || (length % 2) != 0)
throw new APIException("Unexpected length when reading supported languages.");
ushort[] langIDs = new ushort[length / 2];
Buffer.BlockCopy(buffer, 2, langIDs, 0, length);
return langIDs;
}
public string GetStringDescriptor(byte index, ushort languageID)
{
byte[] buffer = new byte[256];
int length = ReadStringDescriptor(index, languageID, buffer);
length -= 2; // Skip length byte and descriptor type
if (length < 0)
return null; return null;
char[] chars = System.Text.Encoding.Unicode.GetChars(buffer, 2, length); char[] chars = System.Text.Encoding.Unicode.GetChars(buffer, 2, length);
return new string(chars); return new string(chars);
} }
public void ControlTransfer(byte requestType, byte request, ushort value, ushort index, ushort length, byte[] data) public int ControlTransfer(byte requestType, byte request, ushort value, ushort index, ushort length, byte[] data)
{ {
uint bytesReturned = 0; uint bytesReturned = 0;
WINUSB_SETUP_PACKET setupPacket; WINUSB_SETUP_PACKET setupPacket;
@@ -127,6 +152,7 @@ namespace MadWizard.WinUSBNet.API
bool success = WinUsb_ControlTransfer(_winUsbHandle, setupPacket, data, length, ref bytesReturned, IntPtr.Zero); bool success = WinUsb_ControlTransfer(_winUsbHandle, setupPacket, data, length, ref bytesReturned, IntPtr.Zero);
if (!success) // todo check bytes returned? if (!success) // todo check bytes returned?
throw APIException.Win32("Control transfer on WinUSB device failed."); throw APIException.Win32("Control transfer on WinUSB device failed.");
return (int)bytesReturned;
} }
+38 -17
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -47,20 +47,13 @@ namespace MadWizard.WinUSBNet
/* /*
// Listen for the control's window creation and then hook into it. // Listen for the control's window creation and then hook into it.
internal void OnHandleCreated(object sender, EventArgs e) private void OnHandleCreated(object sender, EventArgs e)
{ {
try try
{ {
// Window is now created, assign handle to NativeWindow. // Window is now created, assign handle to NativeWindow.
IntPtr handle = ((Control)sender).Handle; IntPtr handle = ((Control)sender).Handle;
AssignHandle(handle); RegisterNotify(handle);
if (_notifyHandle != IntPtr.Zero)
{
API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle);
_notifyHandle = IntPtr.Zero;
}
API.DeviceManagement.RegisterForDeviceNotifications(handle, _guid, ref _notifyHandle);
} }
catch (API.APIException ex) catch (API.APIException ex)
{ {
@@ -68,17 +61,12 @@ namespace MadWizard.WinUSBNet
} }
} }
internal void OnHandleDestroyed(object sender, EventArgs e) private void OnHandleDestroyed(object sender, EventArgs e)
{ {
try try
{ {
// Window was destroyed, release hook. // Window was destroyed, release hook.
ReleaseHandle(); StopNotify();
if (_notifyHandle != null)
{
API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle);
_notifyHandle = IntPtr.Zero;
}
} }
catch (API.APIException ex) catch (API.APIException ex)
{ {
@@ -86,6 +74,28 @@ namespace MadWizard.WinUSBNet
} }
} }
private void RegisterNotify(IntPtr handle)
{
AssignHandle(handle);
if (_notifyHandle != IntPtr.Zero)
{
API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle);
_notifyHandle = IntPtr.Zero;
}
API.DeviceManagement.RegisterForDeviceNotifications(handle, _guid, ref _notifyHandle);
}
private void StopNotify()
{
//ReleaseHandle();
if (_notifyHandle != IntPtr.Zero)
{
API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle);
_notifyHandle = IntPtr.Zero;
}
}
protected override void WndProc(ref Message m) protected override void WndProc(ref Message m)
{ {
// Listen for operating system messages // Listen for operating system messages
@@ -95,6 +105,17 @@ namespace MadWizard.WinUSBNet
case API.DeviceManagement.WM_DEVICECHANGE: case API.DeviceManagement.WM_DEVICECHANGE:
_notifier.HandleDeviceChange(m); _notifier.HandleDeviceChange(m);
break; break;
case WM_NCDESTROY:
// Note: when a control is used, OnHandleDestroyed will be called and the
// handle is already released from NativeWindow. In that case, this
// WM_NCDESTROY message will not be caught here. This is no problem since
// StopNotify is already called. Even if it does, calling it twice does not cause
// problems.
// When a window handle is used instead of a Control the OnHandle events will not
// fire and this handler is necessary to release the handle and stop notifications
// when the window is destroyed.
StopNotify();
break;
} }
base.WndProc(ref m); base.WndProc(ref m);
} }
+3 -3
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -55,10 +55,10 @@ namespace MadWizard.WinUSBNet
/// <summary>Video base class (0x0E)</summary> /// <summary>Video base class (0x0E)</summary>
Video = 0x0E, Video = 0x0E,
/// <summary>Personal healthcare base class (0x0F)</summary> /// <summary>Personal health care base class (0x0F)</summary>
PersonalHealthcare = 0x0F, PersonalHealthcare = 0x0F,
/// <summary>Diagnosticdevice base class (0xDC)</summary> /// <summary>Diagnostic device base class (0xDC)</summary>
DiagnosticDevice = 0xDC, DiagnosticDevice = 0xDC,
/// <summary>Wireless controller base class (0xE0)</summary> /// <summary>Wireless controller base class (0xE0)</summary>
+51 -33
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -96,7 +96,7 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Disposes the object /// Disposes the object
/// </summary> /// </summary>
/// <param name="disposing">Indicates wether Dispose was called manually (true) or by /// <param name="disposing">Indicates whether Dispose was called manually (true) or by
/// the garbage collector (false) via the destructor.</param> /// the garbage collector (false) via the destructor.</param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
@@ -166,7 +166,7 @@ namespace MadWizard.WinUSBNet
API.WINUSB_PIPE_INFORMATION[] pipesInfo; API.WINUSB_PIPE_INFORMATION[] pipesInfo;
_wuDevice.GetInterfaceInfo(i, out descriptor, out pipesInfo); _wuDevice.GetInterfaceInfo(i, out descriptor, out pipesInfo);
USBPipe[] interfacePipes = new USBPipe[pipesInfo.Length]; USBPipe[] interfacePipes = new USBPipe[pipesInfo.Length];
for (int k = 0; k < pipesInfo.Length; k++) for(int k=0;k<pipesInfo.Length;k++)
{ {
USBPipe pipe = new USBPipe(this, pipesInfo[k]); USBPipe pipe = new USBPipe(this, pipesInfo[k]);
interfacePipes[k] = pipe; interfacePipes[k] = pipe;
@@ -187,13 +187,13 @@ namespace MadWizard.WinUSBNet
private void CheckControlParams(int value, int index, byte[] buffer, int length) private void CheckControlParams(int value, int index, byte[] buffer, int length)
{ {
if (value < ushort.MinValue || value > ushort.MaxValue) if (value < ushort.MinValue || value > ushort.MaxValue)
throw new ArgumentOutOfRangeException("Value parameter out of range."); throw new ArgumentOutOfRangeException(nameof(value), "Value parameter out of range.");
if (index < ushort.MinValue || index > ushort.MaxValue) if (index < ushort.MinValue || index > ushort.MaxValue)
throw new ArgumentOutOfRangeException("Index parameter out of range."); throw new ArgumentOutOfRangeException(nameof(index), "Index parameter out of range.");
if (length > buffer.Length) if (length > buffer.Length)
throw new ArgumentOutOfRangeException("Length parameter is larger than the size of the buffer."); throw new ArgumentOutOfRangeException(nameof(length), "Length parameter is larger than the size of the buffer.");
if (length > ushort.MaxValue) if (length > ushort.MaxValue)
throw new ArgumentOutOfRangeException("Length too large"); throw new ArgumentOutOfRangeException(nameof(length), "Length too large");
} }
/// <summary> /// <summary>
@@ -213,7 +213,7 @@ namespace MadWizard.WinUSBNet
set set
{ {
if (value < 0) if (value < 0)
throw new ArgumentOutOfRangeException("Control pipe timeout cannot be negative."); throw new ArgumentOutOfRangeException(nameof(value), "Control pipe timeout cannot be negative.");
//_wuDevice.SetPipePolicy(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); //_wuDevice.SetPipePolicy(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value);
if (InputPipe != null) if (InputPipe != null)
_wuDevice.SetPipePolicy(0, InputPipe.Address, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); _wuDevice.SetPipePolicy(0, InputPipe.Address, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value);
@@ -237,7 +237,8 @@ namespace MadWizard.WinUSBNet
/// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe.</param> /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe.</param>
/// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>.
/// The setup packet's length member will be set to this length.</param> /// The setup packet's length member will be set to this length.</param>
public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length) /// <returns>The number of bytes received from the device.</returns>
public int ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length)
{ {
// Parameters are int and not ushort because ushort is not CLS compliant. // Parameters are int and not ushort because ushort is not CLS compliant.
CheckNotDisposed(); CheckNotDisposed();
@@ -245,7 +246,7 @@ namespace MadWizard.WinUSBNet
try try
{ {
_wuDevice.ControlTransfer(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer); return _wuDevice.ControlTransfer(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer);
} }
catch (API.APIException e) catch (API.APIException e)
{ {
@@ -269,7 +270,7 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param> /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -318,7 +319,7 @@ namespace MadWizard.WinUSBNet
/// be set to the length of this buffer. Note: This buffer is not allowed to change for the duration of the asynchronous operation. </param> /// be set to the length of this buffer. Note: This buffer is not allowed to change for the duration of the asynchronous operation. </param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -334,13 +335,13 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Waits for a pending asynchronous control transfer to complete. /// Waits for a pending asynchronous control transfer to complete.
/// </summary> /// </summary>
/// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation, /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchronous operation,
/// as returned by one of the ControlIn, ControlOut or ControlTransfer methods.</param> /// as returned by one of the ControlIn, ControlOut or ControlTransfer methods.</param>
/// <returns>The number of bytes transfered during the operation.</returns> /// <returns>The number of bytes transfered during the operation.</returns>
/// <remarks>Every asynchronous control transfer must have a matching call to <see cref="EndControlTransfer"/> to dispose /// <remarks>Every asynchronous control transfer must have a matching call to <see cref="EndControlTransfer"/> to dispose
/// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number
/// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would
/// otherwise have ocurred during the operation. If the operation is not yet finished EndControlTransfer will wait for the /// otherwise have occurred during the operation. If the operation is not yet finished EndControlTransfer will wait for the
/// operation to finish before returning.</remarks> /// operation to finish before returning.</remarks>
public int EndControlTransfer(IAsyncResult asyncResult) public int EndControlTransfer(IAsyncResult asyncResult)
{ {
@@ -382,9 +383,10 @@ namespace MadWizard.WinUSBNet
/// <param name="buffer">The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be /// <param name="buffer">The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be
/// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The length of this /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The length of this
/// buffer is used as the number of bytes in the control transfer. The setup packet's length member will be set to this length as well.</param> /// buffer is used as the number of bytes in the control transfer. The setup packet's length member will be set to this length as well.</param>
public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer) /// <returns>The number of bytes received from the device.</returns>
public int ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer)
{ {
ControlTransfer(requestType, request, value, index, buffer, buffer.Length); return ControlTransfer(requestType, request, value, index, buffer, buffer.Length);
} }
/// <summary> /// <summary>
@@ -426,11 +428,20 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer. A buffer will be created with this length and the length member of the setup packet /// <param name="length">Length of the data to transfer. A buffer will be created with this length and the length member of the setup packet
/// will be set to this length.</param> /// will be set to this length.</param>
/// <returns>A buffer containing the data transfered.</returns> /// <returns>A buffer containing the data transfered.</returns>
/// <remarks>This routine initially allocates a buffer to hold the <paramref name="length"/> bytes of data expected from the device.
/// If the device responds with less data than expected, this routine will allocate a smaller buffer to copy and return only the bytes actually received.
/// </remarks>
public byte[] ControlIn(byte requestType, byte request, int value, int index, int length) public byte[] ControlIn(byte requestType, byte request, int value, int index, int length)
{ {
CheckIn(requestType); CheckIn(requestType);
byte[] buffer = new byte[length]; byte[] buffer = new byte[length];
ControlTransfer(requestType, request, value, index, buffer, buffer.Length); int actuallyReceived = ControlTransfer(requestType, request, value, index, buffer, buffer.Length);
if (actuallyReceived < length)
{
byte[] outBuffer = new byte[actuallyReceived];
Array.Copy(buffer, 0, outBuffer, 0, actuallyReceived);
return outBuffer;
}
return buffer; return buffer;
} }
@@ -446,10 +457,11 @@ namespace MadWizard.WinUSBNet
/// <param name="buffer">The buffer that will receive the data transfered.</param> /// <param name="buffer">The buffer that will receive the data transfered.</param>
/// <param name="length">Length of the data to transfer. The length member of the setup packet will be set to this length. The buffer specified /// <param name="length">Length of the data to transfer. The length member of the setup packet will be set to this length. The buffer specified
/// by the <paramref name="buffer"/> parameter should have at least this length.</param> /// by the <paramref name="buffer"/> parameter should have at least this length.</param>
public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length) /// <returns>The number of bytes received from the device.</returns>
public int ControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length)
{ {
CheckIn(requestType); CheckIn(requestType);
ControlTransfer(requestType, request, value, index, buffer, length); return ControlTransfer(requestType, request, value, index, buffer, length);
} }
/// <summary> /// <summary>
@@ -462,10 +474,11 @@ namespace MadWizard.WinUSBNet
/// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param> /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
/// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param> /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
/// <param name="buffer">The buffer that will receive the data transfered. The length of this buffer will be the number of bytes transfered.</param> /// <param name="buffer">The buffer that will receive the data transfered. The length of this buffer will be the number of bytes transfered.</param>
public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer) /// <returns>The number of bytes received from the device.</returns>
public int ControlIn(byte requestType, byte request, int value, int index, byte[] buffer)
{ {
CheckIn(requestType); CheckIn(requestType);
ControlTransfer(requestType, request, value, index, buffer); return ControlTransfer(requestType, request, value, index, buffer);
} }
/// <summary> /// <summary>
@@ -545,7 +558,7 @@ namespace MadWizard.WinUSBNet
/// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param> /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -571,7 +584,7 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param> /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -594,7 +607,7 @@ namespace MadWizard.WinUSBNet
/// <param name="buffer">The buffer that will receive the data transfered. The setup packet's length member will be set to the length of this buffer.</param> /// <param name="buffer">The buffer that will receive the data transfered. The setup packet's length member will be set to the length of this buffer.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -617,7 +630,7 @@ namespace MadWizard.WinUSBNet
/// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param> /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -641,7 +654,7 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param> /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -664,7 +677,7 @@ namespace MadWizard.WinUSBNet
/// <param name="buffer">The buffer that contains the data to be transfered. The setup packet's length member will be set to the length of this buffer.</param> /// <param name="buffer">The buffer that contains the data to be transfered. The setup packet's length member will be set to the length of this buffer.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -687,7 +700,7 @@ namespace MadWizard.WinUSBNet
/// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param> /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous control transfer, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -781,8 +794,13 @@ namespace MadWizard.WinUSBNet
{ {
wuDevice.OpenDevice(devicePath); wuDevice.OpenDevice(devicePath);
API.USB_DEVICE_DESCRIPTOR deviceDesc = wuDevice.GetDeviceDescriptor(); API.USB_DEVICE_DESCRIPTOR deviceDesc = wuDevice.GetDeviceDescriptor();
// string q = wuDevice.GetStringDescriptor(0);
// TODO: use language id properly // Get first supported language ID
ushort[] langIDs = wuDevice.GetSupportedLanguageIDs();
ushort langID = 0;
if (langIDs.Length > 0)
langID = langIDs[0];
string manufacturer = null, product = null, serialNumber = null; string manufacturer = null, product = null, serialNumber = null;
byte idx = 0; byte idx = 0;
@@ -790,7 +808,7 @@ namespace MadWizard.WinUSBNet
{ {
idx = deviceDesc.iManufacturer; idx = deviceDesc.iManufacturer;
if (idx > 0) if (idx > 0)
manufacturer = wuDevice.GetStringDescriptor(idx); manufacturer = wuDevice.GetStringDescriptor(idx, langID);
} }
catch { } catch { }
@@ -798,7 +816,7 @@ namespace MadWizard.WinUSBNet
{ {
idx = deviceDesc.iProduct; idx = deviceDesc.iProduct;
if (idx > 0) if (idx > 0)
product = wuDevice.GetStringDescriptor(idx); product = wuDevice.GetStringDescriptor(idx, langID);
} }
catch { } catch { }
@@ -806,7 +824,7 @@ namespace MadWizard.WinUSBNet
{ {
idx = deviceDesc.iSerialNumber; idx = deviceDesc.iSerialNumber;
if (idx > 0) if (idx > 0)
serialNumber = wuDevice.GetStringDescriptor(idx); serialNumber = wuDevice.GetStringDescriptor(idx, langID);
} }
catch { } catch { }
+2 -2
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -67,7 +67,7 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Device class code as defined in the interface descriptor /// Device class code as defined in the interface descriptor
/// This property can be used if the class type is not defined /// This property can be used if the class type is not defined
/// int the USBBaseClass enumeraiton /// int the USBBaseClass enumeration
/// </summary> /// </summary>
public byte ClassValue public byte ClassValue
{ {
+2 -2
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -73,7 +73,7 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Interface class code as defined in the interface descriptor /// Interface class code as defined in the interface descriptor
/// This property can be used if the class type is not defined /// This property can be used if the class type is not defined
/// int the USBBaseClass enumeraiton /// int the USBBaseClass enumeration
/// </summary> /// </summary>
public byte ClassValue public byte ClassValue
{ {
+2 -2
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -146,7 +146,7 @@ namespace MadWizard.WinUSBNet
_Arrival(this, new USBEvent(USBEventType.DeviceArrival, _guid, devicePath)); _Arrival(this, new USBEvent(USBEventType.DeviceArrival, _guid, devicePath));
} }
/// <summary> /// <summary>
/// Trigggers the removal event /// Triggers the removal event
/// </summary> /// </summary>
/// <param name="devicePath">Device pathname of the device that has been connected</param> /// <param name="devicePath">Device pathname of the device that has been connected</param>
protected void OnRemoval(string devicePath) protected void OnRemoval(string devicePath)
+16 -17
View File
@@ -1,4 +1,4 @@
/* WinUSBNet library /* WinUSBNet library
* (C) 2010 Thomas Bleeker (www.madwizard.org) * (C) 2010 Thomas Bleeker (www.madwizard.org)
* *
* Licensed under the MIT license, see license.txt or: * Licensed under the MIT license, see license.txt or:
@@ -139,29 +139,29 @@ namespace MadWizard.WinUSBNet
private void CheckReadParams(byte[] buffer, int offset, int length) private void CheckReadParams(byte[] buffer, int offset, int length)
{ {
if (!IsIn) if (!IsIn)
// throw new NotSupportedException("Cannot read from a pipe with OUT direction."); // throw new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries.");
LogAndThrowException(new NotSupportedException("Cannot read from a pipe with OUT direction.")); LogAndThrowException(new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries."));
int bufferLength = buffer.Length; int bufferLength = buffer.Length;
if (offset < 0 || offset >= bufferLength) if (offset < 0 || offset >= bufferLength)
// throw new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries."); // throw new ArgumentOutOfRangeException(nameof(offset), "Offset of data to read is outside the buffer boundaries.");
LogAndThrowException(new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries.")); LogAndThrowException(new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries."));
if (length < 0 || (offset + length) > bufferLength) if (length < 0 || (offset + length) > bufferLength)
// throw new ArgumentOutOfRangeException("Length of data to read is outside the buffer boundaries."); // throw new ArgumentOutOfRangeException(nameof(length), "Length of data to read is outside the buffer boundaries.");
LogAndThrowException(new ArgumentOutOfRangeException("Length of data to read is outside the buffer boundaries.")); LogAndThrowException(new ArgumentOutOfRangeException("Length of data to read is outside the buffer boundaries."));
} }
private void CheckWriteParams(byte[] buffer, int offset, int length) private void CheckWriteParams(byte[] buffer, int offset, int length)
{ {
if (!IsOut) if (!IsOut)
// throw new NotSupportedException("Cannot write to a pipe with IN direction."); //throw new NotSupportedException("Cannot write to a pipe with IN direction.");
LogAndThrowException(new NotSupportedException("Cannot write to a pipe with IN direction.")); LogAndThrowException(new NotSupportedException("Cannot write to a pipe with IN direction."));
int bufferLength = buffer.Length; int bufferLength = buffer.Length;
if (offset < 0 || offset >= bufferLength) if (offset < 0 || offset >= bufferLength)
// throw new ArgumentOutOfRangeException("Offset of data to write is outside the buffer boundaries."); // throw new ArgumentOutOfRangeException(nameof(offset), "Offset of data to write is outside the buffer boundaries.");
LogAndThrowException(new ArgumentOutOfRangeException("Offset of data to write is outside the buffer boundaries.")); LogAndThrowException(new ArgumentOutOfRangeException("Offset of data to write is outside the buffer boundaries."));
if (length < 0 || (offset + length) > bufferLength) if (length < 0 || (offset + length) > bufferLength)
// throw new ArgumentOutOfRangeException("Length of data to write is outside the buffer boundaries."); // throw new ArgumentOutOfRangeException(nameof(length), "Length of data to write is outside the buffer boundaries.");
LogAndThrowException(new ArgumentOutOfRangeException("Length of data to write is outside the buffer boundaries.")); LogAndThrowException(new ArgumentOutOfRangeException("Length of data to write is outside the buffer boundaries."));
} }
@@ -171,7 +171,7 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer.</param> /// <param name="length">Length of the data to transfer.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous operation, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous operation, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndRead"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndRead"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndRead"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndRead"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -189,9 +189,8 @@ namespace MadWizard.WinUSBNet
} }
catch (API.APIException e) catch (API.APIException e)
{ {
if (result != null) result.Dispose();
result.Dispose(); //throw new USBException("Failed to read from pipe.", e);
// throw new USBException("Failed to read from pipe.", e);
LogAndThrowException(new USBException("Failed to read from pipe.", e)); LogAndThrowException(new USBException("Failed to read from pipe.", e));
} }
catch (Exception e) catch (Exception e)
@@ -208,13 +207,13 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Waits for a pending asynchronous read operation to complete. /// Waits for a pending asynchronous read operation to complete.
/// </summary> /// </summary>
/// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation, /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchronous operation,
/// as returned by <see cref="BeginRead"/>.</param> /// as returned by <see cref="BeginRead"/>.</param>
/// <returns>The number of bytes transfered during the operation.</returns> /// <returns>The number of bytes transfered during the operation.</returns>
/// <remarks>Every call to <see cref="BeginRead"/> must have a matching call to <see cref="EndRead"/> to dispose /// <remarks>Every call to <see cref="BeginRead"/> must have a matching call to <see cref="EndRead"/> to dispose
/// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number
/// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would
/// otherwise have ocurred during the operation. If the operation is not yet finished EndWrite will wait for the /// otherwise have occurred during the operation. If the operation is not yet finished EndWrite will wait for the
/// operation to finish before returning.</remarks> /// operation to finish before returning.</remarks>
public int EndRead(IAsyncResult asyncResult) public int EndRead(IAsyncResult asyncResult)
{ {
@@ -285,7 +284,7 @@ namespace MadWizard.WinUSBNet
/// <param name="length">Length of the data to transfer.</param> /// <param name="length">Length of the data to transfer.</param>
/// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param> /// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param>
/// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param> /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
/// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous operation, which could still be pending.</returns> /// <returns>An <see cref="IAsyncResult"/> object representing the asynchronous operation, which could still be pending.</returns>
/// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
/// and must be passed to <see cref="EndWrite"/> to retrieve the result of the operation. For every call to this method a matching call to /// and must be passed to <see cref="EndWrite"/> to retrieve the result of the operation. For every call to this method a matching call to
/// <see cref="EndWrite"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional /// <see cref="EndWrite"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
@@ -327,13 +326,13 @@ namespace MadWizard.WinUSBNet
/// <summary> /// <summary>
/// Waits for a pending asynchronous write operation to complete. /// Waits for a pending asynchronous write operation to complete.
/// </summary> /// </summary>
/// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation, /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchronous operation,
/// as returned by <see cref="BeginWrite"/>.</param> /// as returned by <see cref="BeginWrite"/>.</param>
/// <returns>The number of bytes transfered during the operation.</returns> /// <returns>The number of bytes transfered during the operation.</returns>
/// <remarks>Every call to <see cref="BeginWrite"/> must have a matching call to <see cref="EndWrite"/> to dispose /// <remarks>Every call to <see cref="BeginWrite"/> must have a matching call to <see cref="EndWrite"/> to dispose
/// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number
/// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would
/// otherwise have ocurred during the operation. If the operation is not yet finished EndWrite will wait for the /// otherwise have occurred during the operation. If the operation is not yet finished EndWrite will wait for the
/// operation to finish before returning.</remarks> /// operation to finish before returning.</remarks>
public void EndWrite(IAsyncResult asyncResult) public void EndWrite(IAsyncResult asyncResult)
{ {