init
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
using DTS.DASLib.Connection.USBFramework;
|
||||
using DTS.DASLib.DASResource;
|
||||
|
||||
namespace DTS.DASLib.Connection
|
||||
{
|
||||
public class HIDUSBConnection: IConnection
|
||||
{
|
||||
public double GetCurrentDownloadRate() { return 0D; }
|
||||
public double GetCurrentUploadRate() { return 0D; }
|
||||
|
||||
public const int HIDSLICE_PID = 0x0003;
|
||||
public const int DTS_VID = 0x1CB9;
|
||||
protected bool _Connected;
|
||||
|
||||
protected int NumberOfInputBuffers = 0;
|
||||
protected int _HIDHandle;
|
||||
protected bool _MyDeviceDetected;
|
||||
protected HIDevice _MyHID = new HIDevice();
|
||||
protected int _ReadHandle;
|
||||
protected int _WriteHandle;
|
||||
protected byte[] InputReportBuffer;
|
||||
protected FileIODeclarations.SECURITY_ATTRIBUTES Security;
|
||||
protected string Device_Name;
|
||||
private bool disposed;
|
||||
|
||||
public event EventHandler OnDisconnected;
|
||||
|
||||
public bool Connected { get { return _Connected; } }
|
||||
|
||||
public string ConnectString { get { return Device_Name; } }
|
||||
|
||||
public System.Net.Sockets.SocketFlags Flags { get; set; }
|
||||
|
||||
public string GetConnectionData() { return ""; }
|
||||
|
||||
public void Create(string ConnectString)
|
||||
{
|
||||
Device_Name = ConnectString;
|
||||
}
|
||||
|
||||
~HIDUSBConnection()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if(disposed)
|
||||
return;
|
||||
|
||||
if(disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileIODeclarations.CloseHandle(_HIDHandle);
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
FileIODeclarations.CloseHandle(_ReadHandle);
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
FileIODeclarations.CloseHandle(_WriteHandle);
|
||||
}
|
||||
catch { }
|
||||
_MyHID.Dispose();
|
||||
_Connected = false;
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public IAsyncResult BeginDisconnect(bool reuseSocket,
|
||||
AsyncCallback cb,
|
||||
Object state)
|
||||
{
|
||||
HIDUSBRecAsyncResult rar = new HIDUSBRecAsyncResult();
|
||||
rar.AsyncState = state;
|
||||
rar.AsyncWaitHandle = new ManualResetEvent(false);
|
||||
rar.CompletedSynchronously = false;
|
||||
rar.IsCompleted = false;
|
||||
rar.buffer = null;
|
||||
rar.offset = 0;
|
||||
rar.size = 0;
|
||||
if(!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "HIDUSBConnection.BeginDisconnect: Unable to enqueue function"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_BeginDisconnect_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public void EndDisconnect(IAsyncResult asyncResult)
|
||||
{
|
||||
int Result;
|
||||
|
||||
Result = FileIODeclarations.CloseHandle(_HIDHandle);
|
||||
Result = FileIODeclarations.CloseHandle(_ReadHandle);
|
||||
Result = FileIODeclarations.CloseHandle(_WriteHandle);
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
public IAsyncResult BeginAccept(AsyncCallback callback,
|
||||
Object state)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IConnection EndAccept(IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Bind(int port)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Listen(int backlog)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
// Define a class of delegates that point to the Hid.DeviceReport.Read function.
|
||||
// The delegate has the same parameters as Hid.DeviceReport.Read.
|
||||
// Used for asynchronous reads from the device.
|
||||
protected delegate void ReadInputReportDelegate(int readHandle,
|
||||
int hidHandle,
|
||||
int writeHandle,
|
||||
ref bool myDeviceDetected,
|
||||
ref byte[] readBuffer,
|
||||
ref bool success);
|
||||
|
||||
private class AUSBRecFix
|
||||
{
|
||||
public AsyncCallback cb;
|
||||
public HIDUSBRecAsyncResult res;
|
||||
|
||||
public AUSBRecFix(AsyncCallback _cb, HIDUSBRecAsyncResult _res)
|
||||
{
|
||||
cb = _cb;
|
||||
res = _res;
|
||||
}
|
||||
}
|
||||
|
||||
public HIDUSBConnection()
|
||||
{
|
||||
disposed = false;
|
||||
Security = new FileIODeclarations.SECURITY_ATTRIBUTES();
|
||||
Security.lpSecurityDescriptor = 0;
|
||||
Security.bInheritHandle = System.Convert.ToInt32(true);
|
||||
Security.nLength = Marshal.SizeOf(Security);
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
public IAsyncResult BeginConnect(AsyncCallback cb, object state)
|
||||
{
|
||||
HIDUSBRecAsyncResult rar = new HIDUSBRecAsyncResult();
|
||||
rar.AsyncState = state;
|
||||
rar.AsyncWaitHandle = new ManualResetEvent(false);
|
||||
rar.CompletedSynchronously = false;
|
||||
rar.IsCompleted = false;
|
||||
rar.buffer = null;
|
||||
rar.offset = 0;
|
||||
rar.size = 0;
|
||||
if(!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "USBConnection.BeginConnect: Unable to enqueue function"
|
||||
throw new System.Exception(Strings.USBConnection_BeginConnect_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
private void NetCallbackFix(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
AUSBRecFix ausbrf = obj as AUSBRecFix;
|
||||
ausbrf.cb(ausbrf.res);
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show("HIDUSBConnection.NetCallbackFix: Exception " + ex.Message + " " + ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFirstConnectString()
|
||||
{
|
||||
#region Hack to connect just one recorder by manually finding the first one ...
|
||||
int Result;
|
||||
int _HIDHandle;
|
||||
HIDevice _MyHID = new HIDevice();
|
||||
FileIODeclarations.SECURITY_ATTRIBUTES Security;
|
||||
|
||||
Security = new FileIODeclarations.SECURITY_ATTRIBUTES();
|
||||
Security.lpSecurityDescriptor = 0;
|
||||
Security.bInheritHandle = System.Convert.ToInt32(true);
|
||||
Security.nLength = Marshal.SizeOf(Security);
|
||||
|
||||
System.Guid HIDGuid = new Guid();
|
||||
string GuidString;
|
||||
string[] DevicePathName = new string[128];
|
||||
|
||||
HIDDeclarations.HidD_GetHidGuid(ref HIDGuid);
|
||||
GuidString = HIDGuid.ToString();
|
||||
DeviceManagement _MyDeviceManagement = new DeviceManagement();
|
||||
bool DeviceFound = _MyDeviceManagement.FindDeviceFromGuid(HIDGuid, ref DevicePathName);
|
||||
|
||||
if (true == DeviceFound)
|
||||
{
|
||||
int MemberIndex = 0;
|
||||
DeviceFound = false;
|
||||
|
||||
do
|
||||
{
|
||||
//GRV - this works and is a true copy of Axelson
|
||||
_HIDHandle = FileIODeclarations.CreateFile(DevicePathName[MemberIndex],
|
||||
0,
|
||||
FileIODeclarations.FILE_SHARE_READ | FileIODeclarations.FILE_SHARE_WRITE,
|
||||
ref Security, FileIODeclarations.OPEN_EXISTING, 0, 0);
|
||||
|
||||
if (_HIDHandle != FileIODeclarations.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// The returned handle is valid,
|
||||
// so find out if this is the device we're looking for.
|
||||
|
||||
// Set the Size property of DeviceAttributes to the number of bytes in the structure.
|
||||
//_MyHID.DeviceAttributes.Size = _MyHID.DeviceAttributes.ToString().Length;
|
||||
_MyHID.DeviceAttributes.Size = Marshal.SizeOf(_MyHID.DeviceAttributes);
|
||||
|
||||
// ***
|
||||
// API function:
|
||||
// HidD_GetAttributes
|
||||
// Purpose:
|
||||
// Retrieves a HIDD_ATTRIBUTES structure containing the Vendor ID,
|
||||
// Product ID, and Product Version Number for a device.
|
||||
// Accepts:
|
||||
// A handle returned by CreateFile.
|
||||
// A pointer to receive a HIDD_ATTRIBUTES structure.
|
||||
// Returns:
|
||||
// True on success, False on failure.
|
||||
// ***
|
||||
Result = HIDDeclarations.HidD_GetAttributes(_HIDHandle, ref _MyHID.DeviceAttributes);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
// Find out if the device matches the one we're looking for.
|
||||
if (_MyHID.DeviceAttributes.VendorID == DTS_VID &&
|
||||
_MyHID.DeviceAttributes.ProductID == HIDSLICE_PID)
|
||||
{
|
||||
|
||||
// It's the desired device.
|
||||
DeviceFound = true;
|
||||
|
||||
// Close the device so it can be opened for real later
|
||||
Result = FileIODeclarations.CloseHandle(_HIDHandle);
|
||||
|
||||
// return the device's name
|
||||
return DevicePathName[MemberIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// It's not a match, so close the handle.
|
||||
DeviceFound = false;
|
||||
Result = FileIODeclarations.CloseHandle(_HIDHandle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There was a problem in retrieving the information.
|
||||
DeviceFound = false;
|
||||
Result = FileIODeclarations.CloseHandle(_HIDHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep looking until we find the device or there are no more left to examine.
|
||||
MemberIndex = MemberIndex + 1;
|
||||
} while (!((DeviceFound == true) || (MemberIndex == DevicePathName.Length)));
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
||||
#endregion // Hack to connect just one recorder by manually finding the first one ...
|
||||
}
|
||||
|
||||
public void EndConnect(IAsyncResult ar)
|
||||
{
|
||||
int Result;
|
||||
|
||||
_HIDHandle = FileIODeclarations.CreateFile(ConnectString,
|
||||
0,
|
||||
FileIODeclarations.FILE_SHARE_READ | FileIODeclarations.FILE_SHARE_WRITE,
|
||||
ref Security,
|
||||
FileIODeclarations.OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if(_HIDHandle == FileIODeclarations.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// "HIDUSBConnection.EndConnect: Can't open {0} in Connect"
|
||||
throw new System.Exception(string.Format(Strings.HIDUSBConnection_EndConnect_Err1, ConnectString));
|
||||
}
|
||||
|
||||
// Set the Size property of DeviceAttributes to the number of bytes in the structure.
|
||||
_MyHID.DeviceAttributes.Size = Marshal.SizeOf(_MyHID.DeviceAttributes);
|
||||
|
||||
// HidD_GetAttributes
|
||||
// Purpose: Retrieves a HIDD_ATTRIBUTES structure containing the Vendor ID,
|
||||
// Product ID, and Product Version Number for a device.
|
||||
// Accepts: A handle returned by CreateFile.
|
||||
// A pointer to receive a HIDD_ATTRIBUTES structure.
|
||||
// Returns: True on success, False on failure.
|
||||
Result = HIDDeclarations.HidD_GetAttributes(_HIDHandle, ref _MyHID.DeviceAttributes);
|
||||
|
||||
// Learn the capabilities of the device.
|
||||
_MyHID.Capabilities = _MyHID.GetDeviceCapabilities(_HIDHandle);
|
||||
|
||||
//Find out if device is system mouse or keyboard //GRV
|
||||
string HIDUsage = _MyHID.GetHIDUsage(_MyHID.Capabilities);
|
||||
|
||||
// Get and display the Input report buffer size.
|
||||
GetInputReportBufferSize();
|
||||
|
||||
// Get another handle to use in overlapped ReadFiles (for requesting Input reports).
|
||||
_ReadHandle = FileIODeclarations.CreateFile(ConnectString,
|
||||
FileIODeclarations.GENERIC_READ,
|
||||
FileIODeclarations.FILE_SHARE_READ | FileIODeclarations.FILE_SHARE_WRITE,
|
||||
ref Security,
|
||||
FileIODeclarations.OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
|
||||
_WriteHandle = FileIODeclarations.CreateFile(ConnectString,
|
||||
FileIODeclarations.GENERIC_WRITE,
|
||||
FileIODeclarations.FILE_SHARE_READ | FileIODeclarations.FILE_SHARE_WRITE,
|
||||
ref Security,
|
||||
FileIODeclarations.OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
|
||||
// (optional)
|
||||
// Flush any waiting reports in the input buffer.
|
||||
_MyHID.FlushQueue(_ReadHandle);
|
||||
|
||||
HIDevice.InputReport myInputReport = new HIDevice.InputReport();
|
||||
|
||||
// Define a delegate for the Read method of myInputReport.
|
||||
ReadInputReportDelegate MyReadInputReportDelegate = new ReadInputReportDelegate(myInputReport.Read);
|
||||
|
||||
// Set the size of the Input report buffer.
|
||||
InputReportBuffer = new byte[_MyHID.Capabilities.InputReportByteLength];
|
||||
|
||||
_Connected = true;
|
||||
}
|
||||
|
||||
protected void GetInputReportBufferSize()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the number of input buffers.
|
||||
_MyHID.GetNumberOfInputBuffers(_HIDHandle, ref NumberOfInputBuffers);
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
// "HIDUSBConnection.GetInputReportBufferSize: Error during {0}"
|
||||
throw new ApplicationException(string.Format(Strings.HIDUSBConnection_GetInputReportBufferSize_Err1, ex.Message));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class HIDUSBRecAsyncResult: IAsyncResult
|
||||
{
|
||||
public object AsyncState { get; set; }
|
||||
public WaitHandle AsyncWaitHandle { get; set; }
|
||||
public bool CompletedSynchronously { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public byte[] buffer { get; set; }
|
||||
public int offset { get; set; }
|
||||
public int size { get; set; }
|
||||
}
|
||||
|
||||
public IAsyncResult BeginSend(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
if(_ReadHandle == FileIODeclarations.INVALID_HANDLE_VALUE || _WriteHandle == FileIODeclarations.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// "HIDUSBConnection.BeginSend: Invalid read or write handle"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_BeginSend_Err1);
|
||||
}
|
||||
HIDUSBRecAsyncResult rar = new HIDUSBRecAsyncResult();
|
||||
rar.AsyncState = state;
|
||||
rar.AsyncWaitHandle = new ManualResetEvent(false);
|
||||
rar.CompletedSynchronously = false;
|
||||
rar.IsCompleted = false;
|
||||
rar.buffer = buffer;
|
||||
rar.offset = offset;
|
||||
rar.size = size;
|
||||
if(!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "HIDUSBConnection.BeginSend: Unable to enqueue function"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_BeginSend_Err2);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult ar)
|
||||
{
|
||||
HIDUSBRecAsyncResult rar = ar as HIDUSBRecAsyncResult;
|
||||
int bytesLeft = rar.size;
|
||||
int maxOutputReportLength = _MyHID.Capabilities.OutputReportByteLength;
|
||||
byte[] OutputReportBuffer = new byte[maxOutputReportLength];
|
||||
OutputReportBuffer[0] = 0;
|
||||
|
||||
for(int i = 0; i < rar.size; i += (maxOutputReportLength - 1))
|
||||
{
|
||||
bytesLeft = rar.size - i;
|
||||
int bytesToCopy = ((bytesLeft < OutputReportBuffer.Length - 1) ? bytesLeft : OutputReportBuffer.Length - 1);
|
||||
Buffer.BlockCopy(rar.buffer, rar.offset + i, OutputReportBuffer, 1, bytesToCopy);
|
||||
HIDevice.OutputReport myOutputReport = new HIDevice.OutputReport();
|
||||
if(!myOutputReport.Write(OutputReportBuffer, _WriteHandle))
|
||||
{
|
||||
// "HIDUSBConnection.EndSend: Error writing report"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_EndSend_Err1);
|
||||
}
|
||||
}
|
||||
return rar.size;
|
||||
}
|
||||
|
||||
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
if(_ReadHandle == FileIODeclarations.INVALID_HANDLE_VALUE || _WriteHandle == FileIODeclarations.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// "HIDUSBConnection.BeginReceive: Invalid read or write handle"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_BeginReceive_Err1);
|
||||
}
|
||||
HIDUSBRecAsyncResult rar = new HIDUSBRecAsyncResult();
|
||||
rar.AsyncState = state;
|
||||
rar.AsyncWaitHandle = new ManualResetEvent(false);
|
||||
rar.CompletedSynchronously = false;
|
||||
rar.IsCompleted = false;
|
||||
rar.buffer = buffer;
|
||||
rar.offset = offset;
|
||||
rar.size = size;
|
||||
if(!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "HIDUSBConnection.BeginReceive: Unable to enqueue function"
|
||||
throw new System.Exception(Strings.HIDUSBConnection_BeginReceive_Err2);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public int EndReceive(IAsyncResult ar)
|
||||
{
|
||||
HIDUSBRecAsyncResult rar = ar as HIDUSBRecAsyncResult;
|
||||
bool Success = false;
|
||||
HIDevice.InputReport myInputReport = new HIDevice.InputReport();
|
||||
//byte[] InputBuffer = new byte[rar.size+1];
|
||||
myInputReport.Read(_ReadHandle, _HIDHandle, _WriteHandle, ref _MyDeviceDetected, ref InputReportBuffer, ref Success);
|
||||
if(Success)
|
||||
{
|
||||
Buffer.BlockCopy(InputReportBuffer, 1, rar.buffer, rar.offset, InputReportBuffer.Length - 1);
|
||||
rar.IsCompleted = true;
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return rar.size;
|
||||
}
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{8D8E5590-051E-4D69-8372-F92603418420}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DTS.DASLib.Connection</RootNamespace>
|
||||
<AssemblyName>HIDUSBConnection</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="HIDUSBConnection.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\DASResource\DASResource.csproj">
|
||||
<Project>{B28D0091-50DC-48D1-A265-90FC3BF5EECC}</Project>
|
||||
<Name>DASResource</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\IConnection.csproj">
|
||||
<Project>{D49AEE4A-4004-43AB-B034-962A4BCF9BCD}</Project>
|
||||
<Name>IConnection</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\USBFramework\USBFramework.csproj">
|
||||
<Project>{E9506393-9B65-45FB-A119-BF328440822E}</Project>
|
||||
<Name>USBFramework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("USBConnection")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("USBConnection")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2008")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9127ae79-928b-4187-a425-97f49034c5ad")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.06.0081")]
|
||||
[assembly: AssemblyFileVersion("1.06.0081")]
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{30F9A58B-6808-4C93-A294-7267C3D2E7EB}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DTS.DASLib.Connection.USBFramework</RootNamespace>
|
||||
<AssemblyName>HIDFramework</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
<SccLocalPath>
|
||||
</SccLocalPath>
|
||||
<SccAuxPath>
|
||||
</SccAuxPath>
|
||||
<SccProvider>
|
||||
</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DeviceManagement.cs" />
|
||||
<Compile Include="DeviceManagementDeclarations.cs" />
|
||||
<Compile Include="FileIODeclarations.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Windows Installer 3.1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
|
||||
<Project>{d6da1b74-c711-43c2-91b1-1908a8d04dbf}</Project>
|
||||
<Name>DTS.Common.Utilities</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Design\DTS.Common.USBFrameworkClassDiagram.cd" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram MajorVersion="1" MinorVersion="1">
|
||||
<Class Name="DTS.Common.USBFramework.DeviceManagement" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="0.5" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAIQAAACAAEBAAAAAAAAAgAAAAAAAAAAAAAAAAACA=</HashCode>
|
||||
<FileName>DeviceManagement.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Class Name="DTS.Common.USBFramework.DeviceManagementDeclarations" Collapsed="true">
|
||||
<Position X="2.25" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>ABEIgAAAAAwKACABAAGAAEAABnIgAoAQCEIBgBkIgAA=</HashCode>
|
||||
<FileName>DeviceManagementDeclarations.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.USBFramework.FileIODeclarations" Collapsed="true">
|
||||
<Position X="2.25" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAQDgAgAAQBAQEAAgAQAgCgAAAAAQAAAAIABCCAgAAQ=</HashCode>
|
||||
<FileName>FileIODeclarations.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.USBFramework.FileIO" Collapsed="true">
|
||||
<Position X="0.5" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAQAAAAAAAAAAAAAgAQAgCgAAAAAAAAAAAAFACAAAAQ=</HashCode>
|
||||
<FileName>FileIODeclarations.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Font Name="Segoe UI" Size="9" />
|
||||
</ClassDiagram>
|
||||
@@ -0,0 +1,416 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.Common.USBFramework
|
||||
{
|
||||
public class DeviceManagement : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool DeviceNameMatch(Message m, string mydevicePathName)
|
||||
{
|
||||
// Purpose : Compares two device path names. Used to find out if the device name
|
||||
// : of a recently attached or removed device matches the name of a
|
||||
// : device the application is communicating with.
|
||||
// Accepts : m - a WM_DEVICECHANGE message. A call to RegisterDeviceNotification
|
||||
// : causes WM_DEVICECHANGE messages to be passed to an OnDeviceChange routine.
|
||||
// : mydevicePathName - a device pathname returned by SetupDiGetDeviceInterfaceDetail
|
||||
// : in an SP_DEVICE_INTERFACE_DETAIL_DATA structure.
|
||||
// Returns : True if the names match, False if not.
|
||||
|
||||
var devBroadcastDeviceInterface = new DeviceManagementDeclarations.DEV_BROADCAST_DEVICEINTERFACE_1();
|
||||
var devBroadcastHeader = new DeviceManagementDeclarations.DEV_BROADCAST_HDR();
|
||||
|
||||
// The LParam parameter of Message is a pointer to a DEV_BROADCAST_HDR structure.
|
||||
Marshal.PtrToStructure(m.LParam, devBroadcastHeader);
|
||||
|
||||
if (devBroadcastHeader.dbch_devicetype != DeviceManagementDeclarations.DBT_DEVTYP_DEVICEINTERFACE) return false;
|
||||
// The dbch_devicetype parameter indicates that the event applies to a device interface.
|
||||
// So the structure in LParam is actually a DEV_BROADCAST_INTERFACE structure,
|
||||
// which begins with a DEV_BROADCAST_HDR.
|
||||
|
||||
// Obtain the number of characters in dbch_name by subtracting the 28 bytes
|
||||
// in the other members of the structure and dividing by 2 because there are
|
||||
// 2 bytes per character.
|
||||
var stringSize = Convert.ToInt32((devBroadcastHeader.dbch_size - 28) / 2);
|
||||
|
||||
// The dbcc_name parameter of DevBroadcastDeviceInterface contains the device name.
|
||||
// Trim dbcc_name to match the size of the string.
|
||||
devBroadcastDeviceInterface.dbcc_name = new char[stringSize + 1];
|
||||
|
||||
// Marshal data from the unmanaged block pointed to by m.LParam
|
||||
// to the managed object DevBroadcastDeviceInterface.
|
||||
Marshal.PtrToStructure(m.LParam, devBroadcastDeviceInterface);
|
||||
|
||||
// Store the device name in a String.
|
||||
var deviceNameString = new string(devBroadcastDeviceInterface.dbcc_name, 0, stringSize);
|
||||
|
||||
//Debug.WriteLine("Device Name = " + DeviceNameString);
|
||||
//Debug.WriteLine("");
|
||||
|
||||
// Compare the name of the newly attached device with the name of the device
|
||||
// the application is accessing (mydevicePathName).
|
||||
// Set ignorecase True.
|
||||
if (string.Compare(deviceNameString, mydevicePathName, true) == 0)
|
||||
{
|
||||
// The name matches.
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's a different device.
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool FindDeviceFromGuid(System.Guid myGuid, ref string[] devicePathName)
|
||||
{
|
||||
// Purpose : Uses SetupDi API functions to retrieve the device path name of an
|
||||
// : attached device that belongs to an interface class.
|
||||
// Accepts : myGuid - an interface class GUID.
|
||||
// : devicePathName - a pointer to an array of strings that will contain
|
||||
// : the device path names of attached devices.
|
||||
// Returns : True if at least one device is found, False if not.
|
||||
|
||||
var lastDevice = false;
|
||||
var bufferSize = 0;
|
||||
var myDeviceInterfaceDetailData = new DeviceManagementDeclarations.SP_DEVICE_INTERFACE_DETAIL_DATA();
|
||||
var myDeviceInterfaceData = new DeviceManagementDeclarations.SP_DEVICE_INTERFACE_DATA();
|
||||
int result;
|
||||
var detailDataBuffer = IntPtr.Zero;
|
||||
|
||||
// ***
|
||||
// API function: SetupDiGetClassDevs
|
||||
// Purpose:
|
||||
// Retrieves a device information set for a specified group of devices.
|
||||
// SetupDiEnumDeviceInterfaces uses the device information set.
|
||||
// Accepts:
|
||||
// An interface class GUID
|
||||
// Null to retrieve information for all device instances
|
||||
// An optional handle to a top-level window (unused here)
|
||||
// Flags to limit the returned information to currently present devices
|
||||
// and devices that expose interfaces in the class specified by the GUID.
|
||||
// Returns:
|
||||
// A handle to a device information set for the devices.
|
||||
// ***
|
||||
|
||||
var deviceInfoSet = DeviceManagementDeclarations.SetupDiGetClassDevs(ref myGuid,
|
||||
null,
|
||||
0,
|
||||
DeviceManagementDeclarations.DIGCF_PRESENT | DeviceManagementDeclarations.DIGCF_DEVICEINTERFACE);
|
||||
|
||||
var deviceFound = false;
|
||||
var memberIndex = 0;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
|
||||
// Begin with 0 and increment through the device information set until
|
||||
// no more devices are available.
|
||||
|
||||
// The cbSize element of the MyDeviceInterfaceData structure must be set to
|
||||
// the structure's size in bytes.
|
||||
// The size is 28 bytes for 32-bit code and 32 bits for 64-bit code.
|
||||
|
||||
myDeviceInterfaceData.cbSize = Marshal.SizeOf(myDeviceInterfaceData);
|
||||
|
||||
// ***
|
||||
// API function:
|
||||
// SetupDiEnumDeviceInterfaces()
|
||||
// Purpose: Retrieves a handle to a SP_DEVICE_INTERFACE_DATA
|
||||
// structure for a device.
|
||||
// On return, MyDeviceInterfaceData contains the handle to a
|
||||
// SP_DEVICE_INTERFACE_DATA structure for a detected device.
|
||||
// Accepts:
|
||||
// A DeviceInfoSet returned by SetupDiGetClassDevs.
|
||||
// An interface class GUID.
|
||||
// An index to specify a device in a device information set.
|
||||
// A pointer to a handle to a SP_DEVICE_INTERFACE_DATA structure for a device.
|
||||
// Returns:
|
||||
// Non-zero on success, zero on True.
|
||||
// ***
|
||||
|
||||
result = DeviceManagementDeclarations.SetupDiEnumDeviceInterfaces(deviceInfoSet,
|
||||
IntPtr.Zero,
|
||||
ref myGuid,
|
||||
memberIndex,
|
||||
ref myDeviceInterfaceData);
|
||||
|
||||
// Find out if a device information set was retrieved.
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
lastDevice = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A device is present.
|
||||
// ***
|
||||
// API function:
|
||||
// SetupDiGetDeviceInterfaceDetail()
|
||||
// Purpose:
|
||||
// Retrieves an SP_DEVICE_INTERFACE_DETAIL_DATA structure
|
||||
// containing information about a device.
|
||||
// To retrieve the information, call this function twice.
|
||||
// The first time returns the size of the structure.
|
||||
// The second time returns a pointer to the data.
|
||||
// Accepts:
|
||||
// A DeviceInfoSet returned by SetupDiGetClassDevs
|
||||
// An SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces
|
||||
// A pointer to an SP_DEVICE_INTERFACE_DETAIL_DATA structure to receive information
|
||||
// about the specified interface.
|
||||
// The size of the SP_DEVICE_INTERFACE_DETAIL_DATA structure.
|
||||
// A pointer to a variable that will receive the returned required size of the
|
||||
// SP_DEVICE_INTERFACE_DETAIL_DATA structure.
|
||||
// A pointer to an SP_DEVINFO_DATA structure to receive information about the device.
|
||||
// Returns:
|
||||
// Non-zero on success, zero on failure.
|
||||
// ***
|
||||
|
||||
DeviceManagementDeclarations.SetupDiGetDeviceInterfaceDetail(deviceInfoSet,
|
||||
ref myDeviceInterfaceData,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
ref bufferSize,
|
||||
IntPtr.Zero);
|
||||
|
||||
// Store the structure's size.
|
||||
myDeviceInterfaceDetailData.cbSize = Marshal.SizeOf(myDeviceInterfaceDetailData);
|
||||
|
||||
// Allocate memory for the MyDeviceInterfaceDetailData Structure using the returned buffer size.
|
||||
detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
|
||||
|
||||
// Store cbSize in the first 4 bytes of the array
|
||||
if (IS64_BIT_PROCESS)
|
||||
{
|
||||
Marshal.WriteInt64(detailDataBuffer,
|
||||
(IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.WriteInt32(detailDataBuffer,
|
||||
(IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
|
||||
}
|
||||
|
||||
// Call SetupDiGetDeviceInterfaceDetail again.
|
||||
// This time, pass a pointer to DetailDataBuffer
|
||||
// and the returned required buffer size.
|
||||
DeviceManagementDeclarations.SetupDiGetDeviceInterfaceDetail(deviceInfoSet,
|
||||
ref myDeviceInterfaceData,
|
||||
detailDataBuffer,
|
||||
bufferSize,
|
||||
ref bufferSize,
|
||||
IntPtr.Zero);
|
||||
|
||||
// Skip over cbsize (4 bytes) to get the address of the devicePathName.
|
||||
var pdevicePathName = IS64_BIT_PROCESS ? new IntPtr(detailDataBuffer.ToInt64() + 4) : new IntPtr(detailDataBuffer.ToInt32() + 4);
|
||||
|
||||
// Get the String containing the devicePathName.
|
||||
var singledevicePathName = Marshal.PtrToStringAuto(pdevicePathName);
|
||||
devicePathName[memberIndex] = singledevicePathName;
|
||||
|
||||
// Free the memory allocated previously by AllocHGlobal.
|
||||
Marshal.FreeHGlobal(detailDataBuffer);
|
||||
detailDataBuffer = IntPtr.Zero;
|
||||
deviceFound = true;
|
||||
}
|
||||
memberIndex++;
|
||||
} while (!lastDevice);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (detailDataBuffer != IntPtr.Zero)
|
||||
{
|
||||
// Free the memory allocated previously by AllocHGlobal.
|
||||
Marshal.FreeHGlobal(detailDataBuffer);
|
||||
}
|
||||
if (deviceInfoSet != IntPtr.Zero)
|
||||
{
|
||||
// ***
|
||||
// API function
|
||||
|
||||
// summary
|
||||
// Frees the memory reserved for the DeviceInfoSet returned by SetupDiGetClassDevs.
|
||||
|
||||
// parameters
|
||||
// DeviceInfoSet returned by SetupDiGetClassDevs.
|
||||
|
||||
// returns
|
||||
// True on success.
|
||||
// ***
|
||||
|
||||
|
||||
// Trim the array to the number of devices found.
|
||||
|
||||
// ***
|
||||
// API function:
|
||||
// SetupDiDestroyDeviceInfoList
|
||||
// Purpose:
|
||||
// Frees the memory reserved for the DeviceInfoSet returned by SetupDiGetClassDevs.
|
||||
// Accepts:
|
||||
// A DeviceInfoSet returned by SetupDiGetClassDevs.
|
||||
// Returns:
|
||||
// True on success, False on failure.
|
||||
// ***
|
||||
|
||||
DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
}
|
||||
}
|
||||
return deviceFound;
|
||||
}
|
||||
|
||||
public bool RegisterForDeviceNotifications(IntPtr formHandle, Guid classGuid, ref IntPtr deviceNotificationHandle)
|
||||
{
|
||||
// Purpose : Request to receive a notification when a device is attached or removed.
|
||||
// Accepts : devicePathName - a handle to a device.
|
||||
// : formHandle - a handle to the window that will receive device events.
|
||||
// : classGuid - an interface class GUID.
|
||||
//
|
||||
// Returns : True on success, False on failure.
|
||||
|
||||
// A DEV_BROADCAST_DEVICEINTERFACE header holds information about the request.
|
||||
var dbdi = new DeviceManagementDeclarations.DEV_BROADCAST_DEVICEINTERFACE();
|
||||
|
||||
// Set the parameters in the DEV_BROADCAST_DEVICEINTERFACE structure.
|
||||
// Set the size.
|
||||
var size = Marshal.SizeOf(dbdi);
|
||||
dbdi.dbcc_size = size;
|
||||
|
||||
// Request to receive notifications about a class of devices.
|
||||
dbdi.dbcc_devicetype = DeviceManagementDeclarations.DBT_DEVTYP_DEVICEINTERFACE;
|
||||
|
||||
dbdi.dbcc_reserved = 0;
|
||||
|
||||
// Specify the interface class to receive notifications about.
|
||||
dbdi.dbcc_classguid = classGuid;
|
||||
|
||||
dbdi.dbcc_name = "";
|
||||
|
||||
// Allocate memory for the buffer that holds the DEV_BROADCAST_DEVICEINTERFACE structure.
|
||||
var buffer = Marshal.AllocHGlobal(size);
|
||||
|
||||
// Copy the DEV_BROADCAST_DEVICEINTERFACE structure to the buffer.
|
||||
// Set fDeleteOld True to prevent memory leaks.
|
||||
Marshal.StructureToPtr(dbdi, buffer, true);
|
||||
|
||||
// ***
|
||||
// API function:
|
||||
// RegisterDeviceNotification
|
||||
// Purpose:
|
||||
// Request to receive notification messages when a device in an interface class
|
||||
// is attached or removed.
|
||||
// Accepts:
|
||||
// Aa handle to the window that will receive device events
|
||||
// A pointer to a DEV_BROADCAST_DEVICEINTERFACE to specify the type of
|
||||
// device to send notifications for,
|
||||
// DEVICE_NOTIFY_WINDOW_HANDLE to indicate that Handle is a window handle.
|
||||
// Returns:
|
||||
// A device notification handle or NULL on failure.
|
||||
// ***
|
||||
|
||||
deviceNotificationHandle = DeviceManagementDeclarations.RegisterDeviceNotification(formHandle,
|
||||
buffer,
|
||||
DeviceManagementDeclarations.DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
// Marshal data from the unmanaged block DevBroadcastDeviceInterfaceBuffer to
|
||||
// the managed object DevBroadcastDeviceInterface
|
||||
//Marshal.PtrToStructure(buffer, dbdi);
|
||||
|
||||
// Free the memory allocated previously by AllocHGlobal.
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
// Find out if RegisterDeviceNotification was successful.
|
||||
if (IS64_BIT_PROCESS)
|
||||
{
|
||||
return deviceNotificationHandle.ToInt64() != IntPtr.Zero.ToInt64();
|
||||
}
|
||||
return deviceNotificationHandle.ToInt32() != IntPtr.Zero.ToInt32();
|
||||
}
|
||||
static readonly bool IS64_BIT_PROCESS = (IntPtr.Size == 8);
|
||||
|
||||
public void StopReceivingDeviceNotifications(IntPtr deviceNotificationHandle)
|
||||
{
|
||||
|
||||
// Purpose : Requests to stop receiving notification messages when a device in an
|
||||
// interface class is attached or removed.
|
||||
// Accepts : deviceNotificationHandle - a handle returned previously by
|
||||
// RegisterDeviceNotification
|
||||
|
||||
// ***
|
||||
// API function: UnregisterDeviceNotification
|
||||
// Purpose: Stop receiving notification messages.
|
||||
// Accepts: a handle returned previously by RegisterDeviceNotification
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
|
||||
// Ignore failures.
|
||||
DeviceManagementDeclarations.UnregisterDeviceNotification(deviceNotificationHandle);
|
||||
}
|
||||
|
||||
public bool GetDeviceRegistryProperty(Guid myGuid)
|
||||
{
|
||||
const int maxDevLen = 1000;
|
||||
// Create a HDEVINFO with all present devices.
|
||||
var deviceInfoSet = DeviceManagementDeclarations.SetupDiGetClassDevs(ref myGuid, // null?
|
||||
null, // Enumerator
|
||||
0,
|
||||
DeviceManagementDeclarations.DIGCF_PRESENT | DeviceManagementDeclarations.DIGCF_ALLCLASSES);
|
||||
|
||||
if ((int)deviceInfoSet == FileIODeclarations.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Insert error handling here.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enumerate through all devices in Set.
|
||||
DeviceManagementDeclarations.SP_DEVINFO_DATA deviceInfoData;
|
||||
deviceInfoData.cbSize = Marshal.SizeOf(typeof(DeviceManagementDeclarations.SP_DEVINFO_DATA));
|
||||
|
||||
for (uint i = 0; DeviceManagementDeclarations.SetupDiEnumDeviceInfo(deviceInfoSet, i, out deviceInfoData); i++)
|
||||
{
|
||||
var deviceName = new StringBuilder(maxDevLen);
|
||||
if (!DeviceManagementDeclarations.SetupDiGetDeviceRegistryPropertyA(deviceInfoSet,
|
||||
deviceInfoData,
|
||||
DeviceManagementDeclarations.SPDRP_DRIVER,
|
||||
0,
|
||||
deviceName,
|
||||
maxDevLen,
|
||||
IntPtr.Zero))
|
||||
{
|
||||
//incorrect device name:
|
||||
DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
deviceName = new StringBuilder("");
|
||||
return false;
|
||||
}
|
||||
APILogger.Log(deviceName.ToString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (DeviceManagementDeclarations.GetLastError() != DeviceManagementDeclarations.NO_ERROR &&
|
||||
DeviceManagementDeclarations.GetLastError() != DeviceManagementDeclarations.ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
try
|
||||
{
|
||||
var theGuid = Guid.Empty;
|
||||
if (null != myGuid) { theGuid = myGuid; }
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("GetDeviceRegistryProperty(", theGuid.ToString(), ") error - ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
// Insert error handling here.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
DeviceManagementDeclarations.SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTS.Common.USBFramework
|
||||
{
|
||||
public class DeviceManagementDeclarations
|
||||
{
|
||||
// API declarations relating to device management (SetupDixxx and
|
||||
// RegisterDeviceNotification functions).
|
||||
|
||||
// ******************************************************************************
|
||||
// API constants
|
||||
// ******************************************************************************
|
||||
|
||||
// from dbt.h
|
||||
public const int DBT_DEVICEARRIVAL = 0x8000;
|
||||
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
|
||||
public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
|
||||
public const int DBT_DEVTYP_HANDLE = 6;
|
||||
public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4;
|
||||
public const int DEVICE_NOTIFY_SERVICE_HANDLE = 1;
|
||||
public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0;
|
||||
public const int WM_DEVICECHANGE = 0x219;
|
||||
public const int ERROR_INSUFFICIENT_BUFFER = 122;
|
||||
public const int NO_ERROR = 0;
|
||||
public const int ERROR_NO_MORE_ITEMS = 259;
|
||||
public const int DICS_FLAG_GLOBAL = 1;
|
||||
public const int DICS_FLAG_CONFIGSPECIFIC = 2;
|
||||
public const int DICS_FLAG_CONFIGGENERAL = 4;
|
||||
public const int DIREG_DEV = 1;
|
||||
public const int DIREG_DRV = 2;
|
||||
public const int DIREG_BOTH = 4;
|
||||
public const int SPDRP_FRIENDLYNAME = (0x0000000C);
|
||||
public const int SPDRP_DEVICEDESC = (0x00000000);
|
||||
public const int SPDRP_DRIVER = (0x00000009);
|
||||
|
||||
// from setupapi.h
|
||||
public const int DIGCF_PRESENT = 0x00000002;
|
||||
public const int DIGCF_DEVICEINTERFACE = 0x00000010;
|
||||
public const int DIGCF_ALLCLASSES = 0x00000004;
|
||||
|
||||
// ******************************************************************************
|
||||
// Structures and classes for API calls, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
// There are two declarations for the DEV_BROADCAST_DEVICEINTERFACE structure.
|
||||
|
||||
// Use this in the call to RegisterDeviceNotification() and
|
||||
// in checking dbch_devicetype in a DEV_BROADCAST_HDR structure.
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct DEV_BROADCAST_DEVICEINTERFACE
|
||||
{
|
||||
public int dbcc_size;
|
||||
public int dbcc_devicetype;
|
||||
public int dbcc_reserved;
|
||||
public Guid dbcc_classguid;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 400)]
|
||||
public string dbcc_name;
|
||||
}
|
||||
|
||||
// Use this to read the dbcc_name string and classguid.
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public class DEV_BROADCAST_DEVICEINTERFACE_1
|
||||
{
|
||||
public int dbcc_size;
|
||||
public int dbcc_devicetype;
|
||||
public int dbcc_reserved;
|
||||
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)]
|
||||
public byte[] dbcc_classguid;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
|
||||
public char[] dbcc_name;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class DEV_BROADCAST_HANDLE
|
||||
{
|
||||
public int dbch_size;
|
||||
public int dbch_devicetype;
|
||||
public int dbch_reserved;
|
||||
public int dbch_handle;
|
||||
public int dbch_hdevnotify;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class DEV_BROADCAST_HDR
|
||||
{
|
||||
public int dbch_size;
|
||||
public int dbch_devicetype;
|
||||
public int dbch_reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SP_DEVICE_INTERFACE_DATA
|
||||
{
|
||||
public int cbSize;
|
||||
public System.Guid InterfaceClassGuid;
|
||||
public int Flags;
|
||||
public IntPtr Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
|
||||
{
|
||||
public int cbSize;
|
||||
public string DevicePath;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SP_DEVINFO_DATA
|
||||
{
|
||||
public int cbSize;
|
||||
public System.Guid ClassGuid;
|
||||
public int DevInst;
|
||||
public int Reserved;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum RegSAM
|
||||
{
|
||||
QueryValue = 0x0001,
|
||||
SetValue = 0x0002,
|
||||
CreateSubKey = 0x0004,
|
||||
EnumerateSubKeys = 0x0008,
|
||||
Notify = 0x0010,
|
||||
CreateLink = 0x0020,
|
||||
WOW64_32Key = 0x0200,
|
||||
WOW64_64Key = 0x0100,
|
||||
WOW64_Res = 0x0300,
|
||||
Read = 0x00020019,
|
||||
Write = 0x00020006,
|
||||
Execute = 0x00020019,
|
||||
AllAccess = 0x000f003f
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// API functions, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient,
|
||||
IntPtr NotificationFilter,
|
||||
int Flags);
|
||||
|
||||
[DllImport("setupapi.dll")]
|
||||
public static extern int SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid,
|
||||
int hwndParent);
|
||||
|
||||
[DllImport("setupapi.dll")]
|
||||
public static extern int SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
|
||||
|
||||
[DllImport("setupapi.dll")]
|
||||
public static extern int SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet,
|
||||
IntPtr DeviceInfoData,
|
||||
ref System.Guid InterfaceClassGuid,
|
||||
int MemberIndex,
|
||||
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
|
||||
|
||||
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid,
|
||||
string Enumerator,
|
||||
int hwndParent,
|
||||
uint Flags);
|
||||
|
||||
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet,
|
||||
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
|
||||
IntPtr DeviceInterfaceDetailData,
|
||||
int DeviceInterfaceDetailDataSize,
|
||||
ref int RequiredSize,
|
||||
IntPtr DeviceInfoData);
|
||||
|
||||
[DllImport("setupapi.dll", SetLastError = true)]
|
||||
public static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, out SP_DEVINFO_DATA DeviceInfoData);
|
||||
|
||||
[DllImport("setupapi.dll")] //result HDEVINFO
|
||||
public static extern IntPtr SetupDiGetClassDevsA(ref Guid ClassGuid,
|
||||
uint Enumerator,
|
||||
IntPtr hwndParent,
|
||||
uint Flags);
|
||||
|
||||
[DllImport("setupapi.dll")]
|
||||
public static extern bool SetupDiGetDeviceRegistryPropertyA(IntPtr DeviceInfoSet,
|
||||
SP_DEVINFO_DATA DeviceInfoData,
|
||||
uint Property,
|
||||
uint PropertyRegDataType,
|
||||
StringBuilder PropertyBuffer,
|
||||
uint PropertyBufferSize,
|
||||
IntPtr RequiredSize);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ms791236.aspx
|
||||
[DllImport("setupapi.dll", SetLastError = true)]
|
||||
public static extern int SetupDiOpenDevRegKey(IntPtr DeviceInfoSet,
|
||||
ref SP_DEVINFO_DATA DeviceInfoData,
|
||||
uint Scope,
|
||||
uint HwProfile,
|
||||
uint KeyType,
|
||||
RegSAM samDesired);
|
||||
|
||||
[DllImport("Advapi32.dll")]
|
||||
public static extern uint RegQueryValueEx(int hKey,
|
||||
string lpValueName,
|
||||
uint lpReserved, // must be 0
|
||||
out uint lpType,
|
||||
ref byte[] lpData,
|
||||
ref uint lpcbData);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool UnregisterDeviceNotification(IntPtr Handle);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int GetLastError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTS.Common.USBFramework
|
||||
{
|
||||
public class FileIODeclarations
|
||||
{
|
||||
|
||||
// API declarations relating to file I/O.
|
||||
|
||||
// ******************************************************************************
|
||||
// API constants
|
||||
// ******************************************************************************
|
||||
|
||||
public const uint GENERIC_READ = 0x80000000;
|
||||
public const uint GENERIC_WRITE = 0x40000000;
|
||||
public const uint FILE_SHARE_READ = 0x00000001;
|
||||
public const uint FILE_SHARE_WRITE = 0x00000002;
|
||||
public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||
public const int INVALID_HANDLE_VALUE = -1;
|
||||
public const short OPEN_EXISTING = 3;
|
||||
public const int WAIT_TIMEOUT = 0x102;
|
||||
public const uint WAIT_OBJECT_0 = 0;
|
||||
public const uint WAIT_FAILED = 0xFFFFFFFF;
|
||||
public const uint WAIT_ABANDONED = 0x00000080;
|
||||
public const int FSCTL_SET_COMPRESSION = 0x9C040;
|
||||
|
||||
// ******************************************************************************
|
||||
// Structures and classes for API calls, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct OVERLAPPED
|
||||
{
|
||||
public int Internal;
|
||||
public int InternalHigh;
|
||||
public int Offset;
|
||||
public int OffsetHigh;
|
||||
public int hEvent;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public int lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// API functions, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int CancelIo(int hFile);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int CloseHandle(int hObject);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int CreateEvent(ref SECURITY_ATTRIBUTES SecurityAttributes, int bManualReset, int bInitialState, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int CreateFile(string lpFileName,
|
||||
uint dwDesiredAccess,
|
||||
uint dwShareMode,
|
||||
ref SECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
int dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
int hTemplateFile);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int GetLastError();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
// static public extern int ReadFile(int hFile, ref byte lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
|
||||
public static extern int ReadFile(int hFile, ref byte lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, int lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint WaitForSingleObject(int hHandle, int dwMilliseconds);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int WriteFile(int hFile, ref byte lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, int lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
|
||||
public static extern int DeviceIoControl(IntPtr hDevice,
|
||||
int dwIoControlCode,
|
||||
ref short lpInBuffer,
|
||||
int nInBufferSize,
|
||||
IntPtr lpOutBuffer,
|
||||
int nOutBufferSize,
|
||||
ref int lpBytesReturned,
|
||||
IntPtr lpOverlapped);
|
||||
}
|
||||
|
||||
public class FileIO
|
||||
{
|
||||
public const short FILE_ATTRIBUTE_NORMAL = 0X80;
|
||||
public const int FILE_FLAG_OVERLAPPED = 0X40000000;
|
||||
public const short FILE_SHARE_READ = 0X1;
|
||||
public const short FILE_SHARE_WRITE = 0X2;
|
||||
public const uint GENERIC_READ = 0X80000000;
|
||||
public const uint GENERIC_WRITE = 0X40000000;
|
||||
public const int INVALID_HANDLE_VALUE = -1;
|
||||
public const short OPEN_EXISTING = 3;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool CloseHandle(SafeFileHandle hObject);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
|
||||
}
|
||||
}
|
||||
633
Common/DTS.Common.IConnection/USBConnection/USBFramework/HID.cs
Normal file
633
Common/DTS.Common.IConnection/USBConnection/USBFramework/HID.cs
Normal file
@@ -0,0 +1,633 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTS.DASLib.Connection.USBFramework
|
||||
{
|
||||
public class HIDevice: IDisposable
|
||||
{
|
||||
// Used in error messages.
|
||||
const string ModuleName = "Hid";
|
||||
|
||||
public HIDDeclarations.HIDP_CAPS Capabilities;
|
||||
public HIDDeclarations.HIDD_ATTRIBUTES DeviceAttributes;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
// For viewing results of API calls in debug.write statements:
|
||||
//static Debugging MyDebugging = new Debugging();
|
||||
|
||||
public abstract class DeviceReport
|
||||
{
|
||||
// For reports that the device sends to the host.
|
||||
internal int Result;
|
||||
|
||||
// Each DeviceReport class defines a ProtectedRead method for reading a type of report.
|
||||
// ProtectedRead and Read are declared as Subs rather than as Functions because
|
||||
// asynchronous reads use a callback method that can access parameters passed by ByRef
|
||||
// but not Function return values.
|
||||
|
||||
protected abstract void ProtectedRead(int readHandle, int hidHandle, int writeHandle,
|
||||
ref bool myDeviceDetected, ref byte[] readBuffer, ref bool success);
|
||||
|
||||
public void Read(int readHandle, int hidHandle, int writeHandle,
|
||||
ref bool myDeviceDetected, ref byte[] readBuffer, ref bool success)
|
||||
{
|
||||
// Purpose : Calls the overridden ProtectedRead routine.
|
||||
// Enables other classes to override ProtectedRead
|
||||
// while limiting access as Friend.
|
||||
// (Directly declaring Write as Friend MustOverride causes the
|
||||
// compiler warning : "Other languages may permit Friend
|
||||
// Overridable members to be overridden.")
|
||||
|
||||
// Accepts : readHandle - a handle for reading from the device.
|
||||
// hidHandle - a handle for other device communications.
|
||||
// myDeviceDetected - tells whether the device is currently
|
||||
// attached and communicating.
|
||||
// readBuffer - a byte array to hold the report ID and report data.
|
||||
// success - read success
|
||||
|
||||
{
|
||||
// Request the report.
|
||||
ProtectedRead(readHandle, hidHandle, writeHandle, ref myDeviceDetected, ref readBuffer, ref success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class InFeatureReport: DeviceReport
|
||||
{
|
||||
// For reading Feature reports.
|
||||
protected override void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] inFeatureReportBuffer, ref bool success)
|
||||
{
|
||||
|
||||
// Purpose : reads a Feature report from the device.
|
||||
|
||||
// Accepts : readHandle - the handle for reading from the device.
|
||||
// hidHandle - the handle for other device communications.
|
||||
// myDeviceDetected - tells whether the device is currently attached.
|
||||
// readBuffer - contains the requested report.
|
||||
// success - read success
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// ***
|
||||
// API function: HidD_GetFeature
|
||||
// Attempts to read a Feature report from the device.
|
||||
// Requires:
|
||||
// A handle to a HID
|
||||
// A pointer to a buffer containing the report ID and report
|
||||
// The size of the buffer.
|
||||
// Returns: true on success, false on failure.
|
||||
// ***
|
||||
|
||||
success = HIDDeclarations.HidD_GetFeature(hidHandle, ref inFeatureReportBuffer[0], inFeatureReportBuffer.Length);
|
||||
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class InputReport: DeviceReport
|
||||
{
|
||||
// For reading Input reports.
|
||||
bool ReadyForOverlappedTransfer; // initialize to false
|
||||
|
||||
internal void CancelTransfer(int readHandle, int hidHandle)
|
||||
{
|
||||
|
||||
// Purpose : closes open handles to a device.
|
||||
|
||||
// Accepts : ReadHandle - the handle for reading from the device.
|
||||
// HIDHandle - the handle for other device communications.
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// ***
|
||||
// API function: CancelIo
|
||||
// Purpose: Cancels a call to ReadFile
|
||||
// Accepts: the device handle.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
Result = FileIODeclarations.CancelIo(readHandle);
|
||||
|
||||
// The failure may have been because the device was removed,
|
||||
// so close any open handles and
|
||||
// set myDeviceDetected=False to cause the application to
|
||||
// look for the device on the next attempt.
|
||||
|
||||
if(hidHandle != 0)
|
||||
{
|
||||
FileIODeclarations.CloseHandle(hidHandle);
|
||||
|
||||
}
|
||||
|
||||
if(hidHandle != 0)
|
||||
{
|
||||
FileIODeclarations.CloseHandle(readHandle);
|
||||
}
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal void PrepareForOverlappedTransfer(ref FileIODeclarations.OVERLAPPED hidOverlapped, ref int eventObject)
|
||||
{
|
||||
|
||||
// Purpose : Creates an event object for the overlapped structure used with
|
||||
// : ReadFile.
|
||||
// ; Called before the first call to ReadFile.
|
||||
|
||||
FileIODeclarations.SECURITY_ATTRIBUTES Security = new FileIODeclarations.SECURITY_ATTRIBUTES();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Values for the SECURITY_ATTRIBUTES structure:
|
||||
Security.lpSecurityDescriptor = 0;
|
||||
Security.bInheritHandle = System.Convert.ToInt32(true);
|
||||
Security.nLength = Marshal.SizeOf(Security);
|
||||
|
||||
// ***
|
||||
// API function: CreateEvent
|
||||
// Purpose: Creates an event object for the overlapped structure used with ReadFile.
|
||||
// Accepts:
|
||||
// A security attributes structure.
|
||||
// Manual Reset = False (The system automatically resets the state to nonsignaled
|
||||
// after a waiting thread has been released.)
|
||||
// Initial state = True (signaled)
|
||||
// An event object name (optional)
|
||||
// Returns: a handle to the event object
|
||||
// ***
|
||||
|
||||
eventObject = FileIODeclarations.CreateEvent(ref Security, System.Convert.ToInt32(false), System.Convert.ToInt32(true), "");
|
||||
|
||||
// Set the members of the overlapped structure.
|
||||
hidOverlapped.Offset = 0;
|
||||
hidOverlapped.OffsetHigh = 0;
|
||||
hidOverlapped.hEvent = eventObject;
|
||||
ReadyForOverlappedTransfer = true;
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void ProtectedRead(int readHandle,
|
||||
int hidHandle,
|
||||
int writeHandle,
|
||||
ref bool myDeviceDetected,
|
||||
ref byte[] inputReportBuffer,
|
||||
ref bool success)
|
||||
{
|
||||
// Purpose : reads an Input report from the device using interrupt transfers.
|
||||
|
||||
// Accepts : readHandle - the handle for reading from the device.
|
||||
// hidHandle - the handle for other device communications.
|
||||
// myDeviceDetected - tells whether the device is currently attached.
|
||||
// readBuffer - contains the requested report.
|
||||
// success - read success
|
||||
// FileIOApiDeclarations.OVERLAPPED HIDOverlapped = new HID.FileIOApiDeclarations.OVERLAPPED();
|
||||
{
|
||||
int NumberOfBytesRead = 0;
|
||||
int TotalBytesRead = 0;
|
||||
long Result = 1;
|
||||
while(Result != 0 && TotalBytesRead < inputReportBuffer.Length)
|
||||
{
|
||||
uint ret = FileIODeclarations.WaitForSingleObject(readHandle, 200);
|
||||
switch(ret)
|
||||
{
|
||||
case FileIODeclarations.WAIT_OBJECT_0:
|
||||
Result = FileIODeclarations.ReadFile(readHandle,
|
||||
ref inputReportBuffer[TotalBytesRead],
|
||||
inputReportBuffer.Length,
|
||||
ref NumberOfBytesRead,
|
||||
0);
|
||||
TotalBytesRead += NumberOfBytesRead;
|
||||
break;
|
||||
case FileIODeclarations.WAIT_TIMEOUT:
|
||||
Result = 0;
|
||||
break;
|
||||
case FileIODeclarations.WAIT_ABANDONED:
|
||||
case FileIODeclarations.WAIT_FAILED:
|
||||
default:
|
||||
// throw new SliceRecorder.Recorder.RecorderException("InputReport.ProtectedRead WaitForSingleObject failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == Result)
|
||||
{
|
||||
}
|
||||
|
||||
// If it's the first attempt to read, set up the overlapped structure for ReadFile.
|
||||
if(!ReadyForOverlappedTransfer)
|
||||
{
|
||||
// PrepareForOverlappedTransfer(ref HIDOverlapped, ref EventObject);
|
||||
}
|
||||
success = Result == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class InputReportViaControlTransfer: DeviceReport
|
||||
{
|
||||
protected override void ProtectedRead(int readHandle, int hidHandle, int writeHandle, ref bool myDeviceDetected, ref byte[] inputReportBuffer, ref bool success)
|
||||
{
|
||||
// Purpose : reads an Input report from the device using a control transfer.
|
||||
// Accepts : readHandle - the handle for reading from the device.
|
||||
// hidHandle - the handle for other device communications.
|
||||
// myDeviceDetected - tells whether the device is currently attached.
|
||||
// readBuffer - contains the requested report.
|
||||
// success - read success
|
||||
try
|
||||
{
|
||||
// API function: HidD_GetInputReport
|
||||
// Purpose: Attempts to read an Input report from the device using a control transfer.
|
||||
// Supported under Windows XP and later only.
|
||||
// Requires:
|
||||
// A handle to a HID
|
||||
// A pointer to a buffer containing the report ID and report
|
||||
// The size of the buffer.
|
||||
// Returns: true on success, false on failure.
|
||||
success = HIDDeclarations.HidD_GetInputReport(hidHandle, ref inputReportBuffer[0], inputReportBuffer.Length);
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class HostReport
|
||||
{
|
||||
// For reports the host sends to the device.
|
||||
// Each report class defines a ProtectedWrite method for writing a type of report.
|
||||
protected abstract bool ProtectedWrite(int deviceHandle, byte[] reportBuffer);
|
||||
|
||||
public bool Write(byte[] reportBuffer, int deviceHandle)
|
||||
{
|
||||
bool Success = false;
|
||||
|
||||
// Purpose : Calls the overridden ProtectedWrite routine.
|
||||
// : This method enables other classes to override ProtectedWrite
|
||||
// : while limiting access as Friend.
|
||||
// : (Directly declaring Write as Friend MustOverride causes the
|
||||
// : compiler(warning) "Other languages may permit Friend
|
||||
// : Overridable members to be overridden.")
|
||||
// Accepts : reportBuffer - contains the report ID and report data.
|
||||
// : deviceHandle - handle to the device. '
|
||||
// Returns : True on success. False on failure.
|
||||
|
||||
try
|
||||
{
|
||||
Success = ProtectedWrite(deviceHandle, reportBuffer);
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
internal class OutFeatureReport: HostReport
|
||||
{
|
||||
// For Feature reports the host sends to the device.
|
||||
protected override bool ProtectedWrite(int hidHandle, byte[] outFeatureReportBuffer)
|
||||
{
|
||||
// Purpose : writes a Feature report to the device.
|
||||
// Accepts : hidHandle - a handle to the device.
|
||||
// featureReportBuffer - contains the report ID and report to send.
|
||||
// Returns : True on success. False on failure.
|
||||
|
||||
bool Success = false;
|
||||
|
||||
try
|
||||
{
|
||||
// API function: HidD_SetFeature
|
||||
// Purpose: Attempts to send a Feature report to the device.
|
||||
// Accepts:
|
||||
// A handle to a HID
|
||||
// A pointer to a buffer containing the report ID and report
|
||||
// The size of the buffer.
|
||||
// Returns: true on success, false on failure.
|
||||
Success = HIDDeclarations.HidD_SetFeature(hidHandle, ref outFeatureReportBuffer[0], outFeatureReportBuffer.Length);
|
||||
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
public class OutputReport: HostReport
|
||||
{
|
||||
// For Output reports the host sends to the device.
|
||||
// Uses interrupt or control transfers depending on the device and OS.
|
||||
protected override bool ProtectedWrite(int hidHandle, byte[] outputReportBuffer)
|
||||
{
|
||||
// Purpose : writes an Output report to the device.
|
||||
// Accepts : HIDHandle - a handle to the device.
|
||||
// OutputReportBuffer - contains the report ID and report to send.
|
||||
// Returns : True on success. False on failure.
|
||||
|
||||
int NumberOfBytesWritten = 0;
|
||||
int Result;
|
||||
bool Success = false;
|
||||
|
||||
try
|
||||
{
|
||||
// The host will use an interrupt transfer if the the HID has an interrupt OUT
|
||||
// endpoint (requires USB 1.1 or later) AND the OS is NOT Windows 98 Gold (original version).
|
||||
// Otherwise the the host will use a control transfer.
|
||||
// The application doesn't have to know or care which type of transfer is used.
|
||||
// Purpose: writes an Output report to the device.
|
||||
// Accepts:
|
||||
// A handle returned by CreateFile
|
||||
// The output report byte length returned by HidP_GetCaps.
|
||||
// An integer to hold the number of bytes written.
|
||||
// Returns: True on success, False on failure.
|
||||
Result = FileIODeclarations.WriteFile(hidHandle, ref outputReportBuffer[0], outputReportBuffer.Length, ref NumberOfBytesWritten, 0);
|
||||
|
||||
// Return True on success, False on failure.
|
||||
Success = (Result == 0) ? false : true;
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
internal class OutputReportViaControlTransfer: HostReport
|
||||
{
|
||||
protected override bool ProtectedWrite(int hidHandle, byte[] outputReportBuffer)
|
||||
{
|
||||
// Purpose : writes an Output report to the device using a control transfer.
|
||||
// Accepts : hidHandle - a handle to the device.
|
||||
// outputReportBuffer - contains the report ID and report to send.
|
||||
// Returns : True on success. False on failure.
|
||||
|
||||
bool Success = false;
|
||||
try
|
||||
{
|
||||
// API function: HidD_SetOutputReport
|
||||
// Purpose:
|
||||
// Attempts to send an Output report to the device using a control transfer.
|
||||
// Requires Windows XP or later.
|
||||
// Accepts:
|
||||
// A handle to a HID
|
||||
// A pointer to a buffer containing the report ID and report
|
||||
// The size of the buffer.
|
||||
// Returns: true on success, false on failure.
|
||||
Success = HIDDeclarations.HidD_SetOutputReport(hidHandle, ref outputReportBuffer[0], outputReportBuffer.Length);
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlushQueue(int hidHandle)
|
||||
{
|
||||
// Purpose : Remove any Input reports waiting in the buffer.
|
||||
// Accepts : hidHandle - a handle to a device.
|
||||
// Returns : True on success, False on failure.
|
||||
|
||||
bool Result = false;
|
||||
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// API function: HidD_FlushQueue
|
||||
// Purpose: Removes any Input reports waiting in the buffer.
|
||||
// Accepts: a handle to the device.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
|
||||
Result = HIDDeclarations.HidD_FlushQueue(hidHandle);
|
||||
|
||||
//Debug.WriteLine(MyDebugging.ResultOfAPICall("HidD_FlushQueue, ReadHandle"));
|
||||
//Debug.WriteLine("Result = " + Result.ToString());
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public HIDDeclarations.HIDP_CAPS GetDeviceCapabilities(int hidHandle)
|
||||
{
|
||||
|
||||
// Purpose : Retrieves a structure with information about a device's capabilities.
|
||||
// Accepts : HIDHandle - a handle to a device.
|
||||
// Returns : An HIDP_CAPS structure.
|
||||
|
||||
byte[] PreparsedDataBytes = new byte[30];
|
||||
string PreparsedDataString;
|
||||
IntPtr PreparsedDataPointer = new IntPtr();
|
||||
int Result;
|
||||
bool Success = false;
|
||||
byte[] ValueCaps = new byte[1024]; // (the array size is a guess)
|
||||
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// API function: HidD_GetPreparsedData
|
||||
// Purpose: retrieves a pointer to a buffer containing information about the device's capabilities.
|
||||
// HidP_GetCaps and other API functions require a pointer to the buffer.
|
||||
// Requires:
|
||||
// A handle returned by CreateFile.
|
||||
// A pointer to a buffer.
|
||||
// Returns:
|
||||
// True on success, False on failure.
|
||||
// ***
|
||||
Success = HIDDeclarations.HidD_GetPreparsedData(hidHandle, ref PreparsedDataPointer);
|
||||
|
||||
//Debug.WriteLine(MyDebugging.ResultOfAPICall("HidD_GetPreparsedData"));
|
||||
//Debug.WriteLine("");
|
||||
|
||||
// Copy the data at PreparsedDataPointer into a byte array.
|
||||
PreparsedDataString = System.Convert.ToBase64String(PreparsedDataBytes);
|
||||
|
||||
// ***
|
||||
// API function: HidP_GetCaps
|
||||
// Purpose: find out a device's capabilities.
|
||||
// For standard devices such as joysticks, you can find out the specific
|
||||
// capabilities of the device.
|
||||
// For a custom device where the software knows what the device is capable of,
|
||||
// this call may be unneeded.
|
||||
// Accepts:
|
||||
// A pointer returned by HidD_GetPreparsedData
|
||||
// A pointer to a HIDP_CAPS structure.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
|
||||
Result = HIDDeclarations.HidP_GetCaps(PreparsedDataPointer, ref Capabilities);
|
||||
if(Result != 0)
|
||||
{
|
||||
|
||||
// ***
|
||||
// API function: HidP_GetValueCaps
|
||||
// Purpose: retrieves a buffer containing an array of HidP_ValueCaps structures.
|
||||
// Each structure defines the capabilities of one value.
|
||||
// This application doesn't use this data.
|
||||
// Accepts:
|
||||
// A report type enumerator from hidpi.h,
|
||||
// A pointer to a buffer for the returned array,
|
||||
// The NumberInputValueCaps member of the device's HidP_Caps structure,
|
||||
// A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
|
||||
Result = HIDDeclarations.HidP_GetValueCaps(HIDDeclarations.HidP_Input, ref ValueCaps[0], ref Capabilities.NumberInputValueCaps, PreparsedDataPointer);
|
||||
|
||||
// (To use this data, copy the ValueCaps byte array into an array of structures.)
|
||||
// ***
|
||||
// API function: HidD_FreePreparsedData
|
||||
// Purpose: frees the buffer reserved by HidD_GetPreparsedData.
|
||||
// Accepts: A pointer to the PreparsedData structure returned by HidD_GetPreparsedData.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
|
||||
Success = HIDDeclarations.HidD_FreePreparsedData(ref PreparsedDataPointer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Capabilities;
|
||||
}
|
||||
|
||||
public string GetHIDUsage(HIDDeclarations.HIDP_CAPS MyCapabilities)
|
||||
{
|
||||
//'Purpose : Creates a 32-bit Usage from the Usage Page and Usage ID.
|
||||
// ' : Determines whether the Usage is a system mouse or keyboard.
|
||||
// ' : Can be modified to detect other Usages.
|
||||
//Accepts : MyCapabilities - a HIDP_CAPS structure retrieved with HidP_GetCaps.
|
||||
//'Returns : A string describing the Usage.
|
||||
|
||||
string UsageDescription= "";
|
||||
|
||||
try
|
||||
{
|
||||
//Create32-bit Usage from Usage Page and Usage ID.
|
||||
int Usage = MyCapabilities.UsagePage * 256 + MyCapabilities.Usage;
|
||||
|
||||
if(Usage == 0x102)
|
||||
UsageDescription = "mouse";
|
||||
if(Usage == 0x106)
|
||||
UsageDescription = "keyboard";
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return UsageDescription;
|
||||
|
||||
}
|
||||
|
||||
public bool GetNumberOfInputBuffers(int hidDeviceObject, ref int numberOfInputBuffers)
|
||||
{
|
||||
// Purpose : Retrieves the number of Input reports the host can store.
|
||||
// Accepts : hidDeviceObject - a handle to a device
|
||||
// : numberBuffers - an integer to hold the returned value.
|
||||
// Returns : True on success, False on failure.
|
||||
|
||||
bool Success = false;
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// API function: HidD_GetNumInputBuffers
|
||||
// Purpose: retrieves the number of Input reports the host can store.
|
||||
// Not supported by Windows 98 Gold.
|
||||
// If the buffer is full and another report arrives, the host drops the
|
||||
// oldest report.
|
||||
// Accepts: a handle to a device and an integer to hold the number of buffers.
|
||||
// Returns: True on success, False on failure.
|
||||
// ***
|
||||
Success = HIDDeclarations.HidD_GetNumInputBuffers(hidDeviceObject, ref numberOfInputBuffers);
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal bool SetNumberOfInputBuffers(int hidDeviceObject, int numberBuffers)
|
||||
{
|
||||
// Purpose : sets the number of input reports the host will store.
|
||||
// : Requires Windows XP or later.
|
||||
// Accepts : hidDeviceObject - a handle to the device.
|
||||
// : numberBuffers - the requested number of input reports.
|
||||
// Returns : True on success. False on failure.
|
||||
bool Success = false;
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// API function: HidD_SetNumInputBuffers
|
||||
// Purpose: Sets the number of Input reports the host can store.
|
||||
// If the buffer is full and another report arrives, the host drops the
|
||||
// oldest report.
|
||||
// Requires:
|
||||
// A handle to a HID
|
||||
// An integer to hold the number of buffers.
|
||||
// Returns: true on success, false on failure.
|
||||
// ***
|
||||
Success = HIDDeclarations.HidD_SetNumInputBuffers(hidDeviceObject, numberBuffers);
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
HandleException(ModuleName + ":" + System.Reflection.MethodBase.GetCurrentMethod(), ex);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static public void HandleException(string moduleName, System.Exception e)
|
||||
{
|
||||
string Message = "Exception: " + e.Message + Environment.NewLine + "Module: " + moduleName + Environment.NewLine + "Method: " + e.TargetSite.Name;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTS.DASLib.Connection.USBFramework
|
||||
{
|
||||
public sealed class HIDDeclarations
|
||||
{
|
||||
|
||||
// API Declarations for communicating with HID-class devices.
|
||||
|
||||
// ******************************************************************************
|
||||
// API constants
|
||||
// ******************************************************************************
|
||||
|
||||
// from hidpi.h
|
||||
// Typedef enum defines a set of integer constants for HidP_Report_Type
|
||||
public const short HidP_Input = 0;
|
||||
public const short HidP_Output = 1;
|
||||
public const short HidP_Feature = 2;
|
||||
|
||||
// ******************************************************************************
|
||||
// Structures and classes for API calls, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HIDD_ATTRIBUTES
|
||||
{
|
||||
public int Size;
|
||||
public short VendorID;
|
||||
public short ProductID;
|
||||
public short VersionNumber;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HIDP_CAPS
|
||||
{
|
||||
public short Usage;
|
||||
public short UsagePage;
|
||||
public short InputReportByteLength;
|
||||
public short OutputReportByteLength;
|
||||
public short FeatureReportByteLength;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
|
||||
public short[] Reserved;
|
||||
public short NumberLinkCollectionNodes;
|
||||
public short NumberInputButtonCaps;
|
||||
public short NumberInputValueCaps;
|
||||
public short NumberInputDataIndices;
|
||||
public short NumberOutputButtonCaps;
|
||||
public short NumberOutputValueCaps;
|
||||
public short NumberOutputDataIndices;
|
||||
public short NumberFeatureButtonCaps;
|
||||
public short NumberFeatureValueCaps;
|
||||
public short NumberFeatureDataIndices;
|
||||
|
||||
}
|
||||
|
||||
// If IsRange is false, UsageMin is the Usage and UsageMax is unused.
|
||||
// If IsStringRange is false, StringMin is the string index and StringMax is unused.
|
||||
// If IsDesignatorRange is false, DesignatorMin is the designator index and DesignatorMax is unused.
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HidP_Value_Caps
|
||||
{
|
||||
public short UsagePage;
|
||||
public byte ReportID;
|
||||
public int IsAlias;
|
||||
public short BitField;
|
||||
public short LinkCollection;
|
||||
public short LinkUsage;
|
||||
public short LinkUsagePage;
|
||||
public int IsRange;
|
||||
public int IsStringRange;
|
||||
public int IsDesignatorRange;
|
||||
public int IsAbsolute;
|
||||
public int HasNull;
|
||||
public byte Reserved;
|
||||
public short BitSize;
|
||||
public short ReportCount;
|
||||
public short Reserved2;
|
||||
public short Reserved3;
|
||||
public short Reserved4;
|
||||
public short Reserved5;
|
||||
public short Reserved6;
|
||||
public int LogicalMin;
|
||||
public int LogicalMax;
|
||||
public int PhysicalMin;
|
||||
public int PhysicalMax;
|
||||
public short UsageMin;
|
||||
public short UsageMax;
|
||||
public short StringMin;
|
||||
public short StringMax;
|
||||
public short DesignatorMin;
|
||||
public short DesignatorMax;
|
||||
public short DataIndexMin;
|
||||
public short DataIndexMax;
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// API functions, listed alphabetically
|
||||
// ******************************************************************************
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_FlushQueue(int HidDeviceObject);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_FreePreparsedData(ref IntPtr PreparsedData);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern int HidD_GetAttributes(int HidDeviceObject, ref HIDD_ATTRIBUTES Attributes);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_GetFeature(int HidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_GetInputReport(int HidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern void HidD_GetHidGuid(ref System.Guid HidGuid);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_GetNumInputBuffers(int HidDeviceObject, ref int NumberBuffers);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_GetPreparsedData(int HidDeviceObject, ref IntPtr PreparsedData);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_SetFeature(int HidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_SetNumInputBuffers(int HidDeviceObject, int NumberBuffers);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern bool HidD_SetOutputReport(int HidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern int HidP_GetCaps(IntPtr PreparsedData, ref HIDP_CAPS Capabilities);
|
||||
|
||||
[DllImport("hid.dll")]
|
||||
static public extern int HidP_GetValueCaps(short ReportType, ref byte ValueCaps, ref short ValueCapsLength, IntPtr PreparsedData);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("HIDFramework")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("HIDFramework")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2008")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c655f31f-ca6c-4e9b-9480-934762d20a8c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.06.0081")]
|
||||
[assembly: AssemblyFileVersion("1.06.0081")]
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,422 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DTS.Common.DASResource;
|
||||
using DTS.Common.Interface.Connection;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.Common.WINUSBConnection
|
||||
{
|
||||
public class UsbRecAsyncResult : IAsyncResult
|
||||
{
|
||||
public object AsyncState { get; set; }
|
||||
public WaitHandle AsyncWaitHandle { get; set; }
|
||||
public bool CompletedSynchronously { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public byte[] buffer { get; set; }
|
||||
public int Offset { get; set; }
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
public class CDCUSBConnection : IConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// returns true if the das has been soft disconnected or not
|
||||
/// </summary>
|
||||
public bool IsSoftDisconnected { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// connect the das
|
||||
/// :note does nothing for CDCUSB, we don't have a soft disconnect option
|
||||
/// </summary>
|
||||
public void SoftConnect()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// disconnect the das
|
||||
/// :note does nothing for CDCUSB, we don't have a soft disconnect option
|
||||
/// </summary>
|
||||
public void SoftDisconnect()
|
||||
{
|
||||
|
||||
}
|
||||
void IConnection.KeepAliveErrorReceived()
|
||||
{
|
||||
|
||||
}
|
||||
public string GetConnectionData() { return ""; }
|
||||
public const int DTS_VENDOR_ID = 0x1CB9;
|
||||
public const string DTS_VENDOR_ID_STR = "VID_1CB9";
|
||||
private const string DTS_TSR2_CDCUSB_PRODUCT_ID_STR = "PID_001A";
|
||||
private static List<string> _regKeys;
|
||||
private static readonly object KEY_LOCK = new object();
|
||||
public static IList<string> RegKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (KEY_LOCK)
|
||||
{
|
||||
if (null != _regKeys) return _regKeys;
|
||||
_regKeys = new List<string>();
|
||||
ReadTSR2CDCUSBRegKeys();
|
||||
}
|
||||
return _regKeys;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Searches the system registry for a key that corresponds to the Peak CAN USB
|
||||
/// vendor and product ID.
|
||||
/// </summary>
|
||||
internal static void ReadTSR2CDCUSBRegKeys()
|
||||
{
|
||||
var ourKey = ReadOurNameRegistryKey();
|
||||
if (null == ourKey) { return; }
|
||||
foreach (var subKey in ourKey.GetSubKeyNames())
|
||||
{
|
||||
RegKeys.Add(subKey);
|
||||
}
|
||||
}
|
||||
internal static Microsoft.Win32.RegistryKey ReadOurNameRegistryKey()
|
||||
{
|
||||
var key = Microsoft.Win32.Registry.LocalMachine;
|
||||
const string ourName = @"SYSTEM\CurrentControlSet\Enum\USB\" + DTS_VENDOR_ID_STR + "&" + DTS_TSR2_CDCUSB_PRODUCT_ID_STR;
|
||||
key = key.OpenSubKey(ourName);
|
||||
|
||||
return key;
|
||||
}
|
||||
/// <summary>
|
||||
/// current upload rate in b/s
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public double GetCurrentUploadRate() { return 0D; }
|
||||
/// <summary>
|
||||
/// current download rate in b/s
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public double GetCurrentDownloadRate() { return 0D; }
|
||||
public event EventHandler OnDisconnected;
|
||||
private int _baudRate = 9600;
|
||||
private Parity _parity = Parity.None;
|
||||
private StopBits _stopBits = StopBits.One;
|
||||
private const int DATA_BITS = 8;
|
||||
public string PortName { get; set; }
|
||||
private readonly SerialPort _comPort = new SerialPort();
|
||||
|
||||
private string _devicePathname;
|
||||
protected bool _Connected;
|
||||
protected volatile bool Disposed;
|
||||
protected volatile bool Disposing;
|
||||
|
||||
public bool Connected => _Connected;
|
||||
|
||||
public string ConnectString => _devicePathname;
|
||||
|
||||
public System.Net.Sockets.SocketFlags Flags { get; set; }
|
||||
|
||||
private class AUSBRecFix
|
||||
{
|
||||
public readonly AsyncCallback Callback;
|
||||
public readonly UsbRecAsyncResult USBResult;
|
||||
|
||||
public AUSBRecFix(AsyncCallback callback, UsbRecAsyncResult usbResult)
|
||||
{
|
||||
Callback = callback;
|
||||
USBResult = usbResult;
|
||||
}
|
||||
}
|
||||
|
||||
public CDCUSBConnection()
|
||||
{
|
||||
Disposed = false;
|
||||
Disposing = false;
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
~CDCUSBConnection()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
// Use SupressFinalize in case a subclass
|
||||
// of this type implements a finalizer.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
// make sure we're not already disposed
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Disposing = true;
|
||||
}
|
||||
|
||||
if (_comPort.IsOpen)
|
||||
{
|
||||
_comPort.Close();
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
Disposing = false;
|
||||
}
|
||||
|
||||
public void Create(string connectString)
|
||||
{
|
||||
//Create(connectString); - this is recursive!
|
||||
}
|
||||
|
||||
public void Create(string connectString, string hostIPAddress)
|
||||
{
|
||||
foreach (var regKey in RegKeys)
|
||||
{
|
||||
if (!connectString.Contains(regKey.ToLower())) continue;
|
||||
var ourKey = ReadOurNameRegistryKey();
|
||||
ourKey = ourKey.OpenSubKey(regKey);
|
||||
ourKey = ourKey?.OpenSubKey("Device Parameters");
|
||||
var value = (string)ourKey?.GetValue("PortName");
|
||||
PortName = value;
|
||||
break;
|
||||
}
|
||||
_devicePathname = connectString;
|
||||
}
|
||||
|
||||
#region Disconnect
|
||||
|
||||
public IAsyncResult BeginDisconnect(bool reuseSocket,
|
||||
AsyncCallback cb,
|
||||
Object state)
|
||||
{
|
||||
var rar = new UsbRecAsyncResult
|
||||
{
|
||||
AsyncState = state,
|
||||
AsyncWaitHandle = new ManualResetEvent(false),
|
||||
CompletedSynchronously = false,
|
||||
IsCompleted = false,
|
||||
buffer = null,
|
||||
Offset = 0,
|
||||
Size = 0
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
throw new Exception(DASResource.Strings.CDCUSBConnection_BeginDisconnect_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public void EndDisconnect(IAsyncResult ar)
|
||||
{
|
||||
var rar = ar as UsbRecAsyncResult;
|
||||
|
||||
if (_comPort.IsOpen)
|
||||
{
|
||||
_comPort.Close();
|
||||
_Connected = false;
|
||||
_comPort.Dispose();
|
||||
}
|
||||
|
||||
((ManualResetEvent)rar?.AsyncWaitHandle)?.Set();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accept
|
||||
|
||||
public IAsyncResult BeginAccept(AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IConnection EndAccept(IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Listen
|
||||
|
||||
public void Listen(int backlog)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bind
|
||||
|
||||
public void Bind(int port)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Connect
|
||||
|
||||
public IAsyncResult BeginConnect(AsyncCallback cb, object state)
|
||||
{
|
||||
var rar = new UsbRecAsyncResult
|
||||
{
|
||||
AsyncState = state,
|
||||
AsyncWaitHandle = new ManualResetEvent(false),
|
||||
CompletedSynchronously = false,
|
||||
IsCompleted = false,
|
||||
buffer = null,
|
||||
Offset = 0,
|
||||
Size = 0
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "USBConnection.BeginConnect: Unable to enqueue function"
|
||||
throw new Exception(DASResource.Strings.CDCUSBConnection_BeginConnect_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
private void NetCallbackFix(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Disposed) return;
|
||||
var ausbrf = obj as AUSBRecFix;
|
||||
ausbrf?.Callback(ausbrf.USBResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(@"PcanUsbConnection.NetCallbackFix: Exception " + ex.Message + @" " + ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void EndConnect(IAsyncResult ar)
|
||||
{
|
||||
var rar = ar as UsbRecAsyncResult;
|
||||
|
||||
if (_comPort.IsOpen)
|
||||
{
|
||||
_comPort.Close();
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
_comPort.BaudRate = _baudRate;
|
||||
_comPort.DataBits = DATA_BITS;
|
||||
_comPort.StopBits = _stopBits;
|
||||
_comPort.Parity = _parity;
|
||||
_comPort.PortName = PortName;
|
||||
|
||||
try
|
||||
{
|
||||
_comPort.Open();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Connected = false;
|
||||
}
|
||||
|
||||
_Connected = true;
|
||||
|
||||
((ManualResetEvent)rar?.AsyncWaitHandle)?.Set();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Send
|
||||
|
||||
public IAsyncResult BeginSend(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
var rar = new UsbRecAsyncResult
|
||||
{
|
||||
AsyncState = state,
|
||||
AsyncWaitHandle = new ManualResetEvent(false),
|
||||
CompletedSynchronously = false,
|
||||
IsCompleted = false,
|
||||
buffer = buffer,
|
||||
Offset = offset,
|
||||
Size = size
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "WINUSBConnection.BeginSend: Unable to enqueue function"
|
||||
throw new Exception(DASResource.Strings.CDCUSBConnection_BeginSend_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult ar)
|
||||
{
|
||||
var rar = ar as UsbRecAsyncResult;
|
||||
if (null == rar)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
_comPort.Write(rar.buffer, 0, rar.Size);
|
||||
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return rar.Size;
|
||||
}
|
||||
|
||||
public Task<int> SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend)
|
||||
{
|
||||
return Task<int>.Factory.FromAsync(
|
||||
(callback, state) => BeginSend(sendBuffer, bufferStartOffset, bufferSizeToSend, callback, state),
|
||||
EndSend, state: null
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Receive
|
||||
|
||||
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
var rar = new UsbRecAsyncResult
|
||||
{
|
||||
AsyncState = state,
|
||||
AsyncWaitHandle = new ManualResetEvent(false),
|
||||
CompletedSynchronously = false,
|
||||
IsCompleted = false,
|
||||
buffer = buffer,
|
||||
Offset = offset,
|
||||
Size = size
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
// "WINUSBConnection.BeginReceive: Unable to enqueue function"
|
||||
throw new Exception(DASResource.Strings.CDCUSBConnection_BeginReceive_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
//private DateTime _lastHeartbeat;
|
||||
public int EndReceive(IAsyncResult ar)
|
||||
{
|
||||
var rar = ar as UsbRecAsyncResult;
|
||||
int bytesRead;
|
||||
|
||||
do
|
||||
{
|
||||
bytesRead = _comPort.Read(rar.buffer, 0, rar.Size);
|
||||
if (0 == bytesRead)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
} while (0 == bytesRead);
|
||||
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return bytesRead;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{3A40C700-1D50-4524-AFFB-0D59F0F0F9CB}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DTS.DASLib.Connection</RootNamespace>
|
||||
<AssemblyName>WINUSBConnection</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
<SccLocalPath>
|
||||
</SccLocalPath>
|
||||
<SccAuxPath>
|
||||
</SccAuxPath>
|
||||
<SccProvider>
|
||||
</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<NoWarn>0067</NoWarn>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<NoWarn>0067</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Compile Include="CDCUSBConnection.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="WINUSBDevice.cs" />
|
||||
<Compile Include="WINUSBDeviceApi.cs" />
|
||||
<Compile Include="WINUSBConnection.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Windows Installer 3.1</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\DTS.Common.DASResource\DTS.Common.DASResource.csproj">
|
||||
<Project>{f621ce48-bb4b-4cfc-a325-9410b721cc44}</Project>
|
||||
<Name>DTS.Common.DASResource</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
|
||||
<Project>{d6da1b74-c711-43c2-91b1-1908a8d04dbf}</Project>
|
||||
<Name>DTS.Common.Utilities</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\DTS.Common\DTS.Common.csproj">
|
||||
<Project>{F7A0804F-61A4-40AE-83D0-F1137622B592}</Project>
|
||||
<Name>DTS.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\USBFramework\DTS.Common.USBFramework.csproj">
|
||||
<Project>{30f9a58b-6808-4c93-a294-7267c3d2e7eb}</Project>
|
||||
<Name>DTS.Common.USBFramework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Design\DTS.Common.WINUSBConnectionClassDiagram.cd" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram MajorVersion="1" MinorVersion="1">
|
||||
<Class Name="DTS.Common.WINUSBConnection.UsbRecAsyncResult" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="2.25" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAIAAAAAgAAAAAAAEAAAAAAAABAQAABAAAAAgAA=</HashCode>
|
||||
<FileName>CDCUSBConnection.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Class Name="DTS.Common.WINUSBConnection.CDCUSBConnection" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="0.5" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AIAQhEIAACAQTEhAAOJAJgIBAgCiAFAgACAAAABCIEA=</HashCode>
|
||||
<FileName>CDCUSBConnection.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Class Name="DTS.Common.WINUSBConnection.WINUSBConnection" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="0.5" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAQhAIAACAQTEhCAAIAJgIAAAAgBEAAAAAAAABCIEA=</HashCode>
|
||||
<FileName>WINUSBConnection.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Class Name="DTS.Common.WINUSBConnection.WinUsbDevice" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="2.25" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>FhACEAQEACgQAgIAISCCBAAgIAQABICCADAACAAAwKg=</HashCode>
|
||||
<FileName>WINUSBDevice.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Font Name="Segoe UI" Size="9" />
|
||||
</ClassDiagram>
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("WINUSBConnection")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WINUSBConnection")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2008")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("F3C369E6-BFFB-41bc-B8E8-A31094CED447")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.06.0081")]
|
||||
[assembly: AssemblyFileVersion("1.06.0081")]
|
||||
@@ -0,0 +1,503 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
using DTS.Common.DASResource;
|
||||
using DTS.Common.USBFramework;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using DTS.Common.Interface.Connection;
|
||||
using DTS.Common.Classes.Connection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Common.WINUSBConnection
|
||||
{
|
||||
public class WINUSBConnection : IConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// returns true if the unit is currently soft disconnected
|
||||
/// </summary>
|
||||
public bool IsSoftDisconnected { get; private set; } = false;
|
||||
/// <summary>
|
||||
/// connect the unit
|
||||
/// :note does nothing for WINUSB as we have no disconnect option yet:
|
||||
/// </summary>
|
||||
public void SoftConnect()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// disconnect the unit
|
||||
/// :note does nothing for WINUSB as we have no disconnect option yet:
|
||||
/// </summary>
|
||||
public void SoftDisconnect()
|
||||
{
|
||||
|
||||
}
|
||||
void IConnection.KeepAliveErrorReceived()
|
||||
{
|
||||
|
||||
}
|
||||
public string GetConnectionData() { return ""; }
|
||||
private readonly DeviceManagement _wusbDeviceManagement;
|
||||
private WinUsbDevice _sliceDev;
|
||||
|
||||
public event EventHandler OnDisconnected;
|
||||
public double GetCurrentUploadRate() { return 0D; }
|
||||
public double GetCurrentDownloadRate() { return 0D; }
|
||||
|
||||
public bool Connected { get; private set; }
|
||||
|
||||
public string ConnectString { get; private set; }
|
||||
|
||||
public System.Net.Sockets.SocketFlags Flags { get; set; }
|
||||
|
||||
private class AUSBRecFix
|
||||
{
|
||||
public readonly AsyncCallback Callback;
|
||||
public WinUSBRecAsyncResult USBResult;
|
||||
|
||||
public AUSBRecFix(AsyncCallback callback, WinUSBRecAsyncResult usbResult)
|
||||
{
|
||||
Callback = callback;
|
||||
USBResult = usbResult;
|
||||
}
|
||||
}
|
||||
|
||||
public class WinUSBRecAsyncResult : IAsyncResult
|
||||
{
|
||||
public object AsyncState { get; set; }
|
||||
public WaitHandle AsyncWaitHandle { get; set; }
|
||||
public bool CompletedSynchronously { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public byte[] Buffer { get; set; }
|
||||
public int Offset { get; set; }
|
||||
public int Size { get; set; }
|
||||
|
||||
public WinUSBRecAsyncResult(object state)
|
||||
{
|
||||
AsyncState = state;
|
||||
AsyncWaitHandle = new ManualResetEvent(false);
|
||||
CompletedSynchronously = false;
|
||||
IsCompleted = false;
|
||||
Buffer = null;
|
||||
Offset = 0;
|
||||
Size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public WINUSBConnection()
|
||||
{
|
||||
Disposed = false;
|
||||
ConnectString = string.Empty;
|
||||
Connected = false;
|
||||
_wusbDeviceManagement = new DeviceManagement();
|
||||
_sliceDev = null;
|
||||
}
|
||||
|
||||
#region Exit handling
|
||||
|
||||
~WINUSBConnection()
|
||||
{
|
||||
Dispose(false);
|
||||
try
|
||||
{
|
||||
APILogger.Log("failed to dispose WINUSBConnection - finalizer is disposing now.");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
// Use SupressFinalize in case a subclass
|
||||
// of this type implements a finalizer.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected volatile bool Disposed;
|
||||
protected int Disposing;
|
||||
private readonly Mutex _usbConnectionMutex = new Mutex(false, "USBConnection");
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (Interlocked.Exchange(ref Disposing, 1) != 0) { return; }
|
||||
|
||||
// make sure we're not already disposed
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_wusbDeviceManagement != null)
|
||||
{
|
||||
_wusbDeviceManagement.Dispose();
|
||||
}
|
||||
DisposeSliceDev();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// we have to suppress all exceptions here
|
||||
}
|
||||
}
|
||||
Connected = false;
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
public void Create(string connectString)
|
||||
{
|
||||
//Create(connectString); this is recursive!
|
||||
}
|
||||
public void Create(string connectString, string hostIPAddress)
|
||||
{
|
||||
ConnectString = connectString;
|
||||
}
|
||||
|
||||
#region Disconnect
|
||||
|
||||
public IAsyncResult BeginDisconnect(bool reuseSocket,
|
||||
AsyncCallback cb,
|
||||
Object state)
|
||||
{
|
||||
if (_sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is null");
|
||||
}
|
||||
|
||||
if (cb == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.BeginDisconnect: callback is null");
|
||||
}
|
||||
|
||||
Connected = false;
|
||||
var rar = new WinUSBRecAsyncResult(state);
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_BeginDisconnect_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public void EndDisconnect(IAsyncResult asyncResult)
|
||||
{
|
||||
if (_sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is null");
|
||||
}
|
||||
|
||||
var rar = asyncResult as WinUSBRecAsyncResult;
|
||||
if (rar == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndDisconnect: argument is not a WinUSBRecAsyncResult");
|
||||
}
|
||||
|
||||
DisposeSliceDev();
|
||||
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
}
|
||||
private void DisposeSliceDev()
|
||||
{
|
||||
_usbConnectionMutex.WaitOne(1000);
|
||||
try
|
||||
{
|
||||
if (null != _sliceDev)
|
||||
{
|
||||
_sliceDev.Dispose();
|
||||
_sliceDev = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { APILogger.Log("could not release USB handle ", ex); }
|
||||
finally { _usbConnectionMutex.ReleaseMutex(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Accept
|
||||
|
||||
public IAsyncResult BeginAccept(AsyncCallback callback,
|
||||
Object state)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IConnection EndAccept(IAsyncResult asyncResult)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Listen
|
||||
|
||||
public void Listen(int backlog)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bind
|
||||
|
||||
public void Bind(int port)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Connect
|
||||
|
||||
public IAsyncResult BeginConnect(AsyncCallback cb, object state)
|
||||
{
|
||||
if (_sliceDev != null || Connected)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection.BeginConnect: already connected");
|
||||
}
|
||||
|
||||
if (cb == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.BeginConnect: callback is null");
|
||||
}
|
||||
|
||||
var rar = new WinUSBRecAsyncResult(state);
|
||||
var ausbrf = new AUSBRecFix(cb, rar);
|
||||
(ausbrf?.USBResult.AsyncWaitHandle as ManualResetEvent).Reset();
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, ausbrf))
|
||||
{
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_BeginConnect_Err1);
|
||||
}
|
||||
rar.AsyncWaitHandle.WaitOne();
|
||||
return rar;
|
||||
}
|
||||
|
||||
private void NetCallbackFix(object obj)
|
||||
{
|
||||
var ausbrf = obj as AUSBRecFix;
|
||||
try
|
||||
{
|
||||
ausbrf?.Callback(ausbrf.USBResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log("MessageBox", DASResource.Strings.Communication_WinUSBConnectionCallbackFailed, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
(ausbrf?.USBResult.AsyncWaitHandle as ManualResetEvent).Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void EndConnect(IAsyncResult ar)
|
||||
{
|
||||
_usbConnectionMutex.WaitOne();
|
||||
var rar = ar as WinUSBRecAsyncResult;
|
||||
try
|
||||
{
|
||||
if (_sliceDev != null || Connected)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection.EndConnect: already connected");
|
||||
}
|
||||
|
||||
if (rar == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndConnect: argument is not a WinUSBRecAsyncResult");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ConnectString))
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndConnect: ConnectString is invalid");
|
||||
}
|
||||
|
||||
_sliceDev = new WinUsbDevice();
|
||||
}
|
||||
catch (Exception ex) { APILogger.Log("exception in connecting USB", ex); throw; }
|
||||
finally { _usbConnectionMutex.ReleaseMutex(); }
|
||||
var success = _sliceDev.GetDeviceHandle(ConnectString);
|
||||
if (!success)
|
||||
{
|
||||
Connected = false;
|
||||
|
||||
DisposeSliceDev();
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
throw new NotConnectedException("WINUSBConnection.EndConnect: connect failed");
|
||||
}
|
||||
if (_sliceDev.InitializeDevice())
|
||||
{
|
||||
Connected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = DeviceManagementDeclarations.GetLastError();
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("WINUSBConnection.EndConnect: connect failed: ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
throw new NotConnectedException("WINUSBConnection.EndConnect: connect failed (" + error + ")");
|
||||
}
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Send
|
||||
|
||||
public IAsyncResult BeginSend(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
if (!Connected || _sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is not connected");
|
||||
}
|
||||
|
||||
if (buffer == null || buffer.Length == 0 || offset < 0 || size < 0 || offset + size > buffer.Length || cb == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndConnect: invalid parameters");
|
||||
}
|
||||
|
||||
var rar = new WinUSBRecAsyncResult(state)
|
||||
{
|
||||
Buffer = buffer,
|
||||
Offset = offset,
|
||||
Size = size
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_BeginSend_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult ar)
|
||||
{
|
||||
if (!Connected || _sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is not connected");
|
||||
}
|
||||
|
||||
var rar = ar as WinUSBRecAsyncResult;
|
||||
if (rar == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndSend: argument is not a WinUSBRecAsyncResult");
|
||||
}
|
||||
|
||||
var bytes = new byte[rar.Size];
|
||||
Buffer.BlockCopy(rar.Buffer, 0, bytes, 0, rar.Size);
|
||||
if (_sliceDev.MyDevInfo.UseHybridBulkIntMode)
|
||||
{
|
||||
if (!_sliceDev.SendViaInterruptTransfer(ref bytes, (uint)rar.Size))
|
||||
{
|
||||
Connected = false;
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_EndSend_Err1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_sliceDev.SendViaBulkTransfer(ref bytes, (uint)rar.Size))
|
||||
{
|
||||
Connected = false;
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_EndSend_Err1);
|
||||
}
|
||||
}
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return rar.Size;
|
||||
}
|
||||
|
||||
public Task<int> SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend)
|
||||
{
|
||||
return Task<int>.Factory.FromAsync(
|
||||
(callback, state) => BeginSend(sendBuffer, bufferStartOffset, bufferSizeToSend, callback, state),
|
||||
EndSend, state: null
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Receive
|
||||
|
||||
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
|
||||
AsyncCallback cb, object state)
|
||||
{
|
||||
if (!Connected || _sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is not connected");
|
||||
}
|
||||
|
||||
if (buffer == null || buffer.Length == 0 || offset < 0 || size < 0 || offset + size > buffer.Length || cb == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndConnect: invalid parameters");
|
||||
}
|
||||
|
||||
var rar = new WinUSBRecAsyncResult(state)
|
||||
{
|
||||
Buffer = buffer,
|
||||
Offset = offset,
|
||||
Size = size
|
||||
};
|
||||
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||||
{
|
||||
throw new Exception(DASResource.Strings.WINUSBConnection_BeginReceive_Err1);
|
||||
}
|
||||
return rar;
|
||||
}
|
||||
|
||||
public int EndReceive(IAsyncResult ar)
|
||||
{
|
||||
if (!Connected || _sliceDev == null)
|
||||
{
|
||||
throw new NotConnectedException("WINUSBConnection is not connected");
|
||||
}
|
||||
|
||||
var rar = ar as WinUSBRecAsyncResult;
|
||||
if (rar == null)
|
||||
{
|
||||
throw new ArgumentException("WINUSBConnection.EndReceive: argument is not a WinUSBRecAsyncResult");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
uint bytesRead = 0;
|
||||
var bytes = new byte[rar.Size];
|
||||
var success = true;
|
||||
while (success && 0 == bytesRead)
|
||||
{
|
||||
_sliceDev.ReadViaBulkTransfer(Convert.ToByte(_sliceDev.MyDevInfo.BulkInPipe),
|
||||
(uint)rar.Size,
|
||||
ref bytes,
|
||||
ref bytesRead,
|
||||
ref success);
|
||||
if (0 == bytesRead)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
Buffer.BlockCopy(bytes, 0, rar.Buffer, 0, (int)bytesRead);
|
||||
rar.Size = (int)bytesRead;
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return rar.Size;
|
||||
}
|
||||
// the connection is now closed
|
||||
Connected = false;
|
||||
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Connected = false;
|
||||
if (!(ex is NullReferenceException))
|
||||
{
|
||||
APILogger.LogString("WINUSBConnection.EndReceive: Exception caught");
|
||||
APILogger.LogException(ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,827 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Runtime.InteropServices;
|
||||
using DTS.Common.USBFramework;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.Common.WINUSBConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// Routines for the WinUsb driver supported by Windows Vista and Windows XP.
|
||||
/// </summary>
|
||||
///
|
||||
internal sealed partial class WinUsbDevice : IDisposable
|
||||
{
|
||||
private volatile bool _bDisposed;
|
||||
|
||||
~WinUsbDevice()
|
||||
{
|
||||
if (!_bDisposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
APILogger.Log("WinUSBDevice not disposed before finalizer. Disposing now");
|
||||
}
|
||||
catch { }
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
internal struct DevInfo
|
||||
{
|
||||
internal SafeFileHandle DeviceHandle;
|
||||
internal IntPtr WinUSBHandle;
|
||||
internal Byte BulkInPipe;
|
||||
internal Byte BulkOutPipe;
|
||||
internal Byte InterruptInPipe;
|
||||
internal Byte InterruptOutPipe;
|
||||
internal UInt32 DeviceSpeed;
|
||||
internal bool UseHybridBulkIntMode;
|
||||
}
|
||||
|
||||
internal DevInfo MyDevInfo;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_bDisposed) return;
|
||||
_bDisposed = true;
|
||||
CloseDeviceHandle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the device handle obtained with CreateFile and frees resources.
|
||||
/// </summary>
|
||||
///
|
||||
internal void CloseDeviceHandle()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MyDevInfo.WinUSBHandle != IntPtr.Zero)
|
||||
{
|
||||
WinUsb_Free(MyDevInfo.WinUSBHandle);
|
||||
MyDevInfo.WinUSBHandle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
if (MyDevInfo.DeviceHandle == null || MyDevInfo.DeviceHandle.IsInvalid) return;
|
||||
if (MyDevInfo.DeviceHandle.IsInvalid) return;
|
||||
MyDevInfo.DeviceHandle.Dispose();
|
||||
MyDevInfo.DeviceHandle = null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates a Control Read transfer. Data stage is device to host.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dataStage"> The received data. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
|
||||
internal bool Do_Control_Read_Transfer(ref byte[] dataStage)
|
||||
{
|
||||
// Vendor-specific request to an interface with device-to-host Data stage.
|
||||
WinUSBSetupPacket setupPacket;
|
||||
setupPacket.RequestType = 0XC1;
|
||||
|
||||
// The request number that identifies the specific request.
|
||||
setupPacket.Request = 2;
|
||||
|
||||
// Command-specific value to send to the device.
|
||||
setupPacket.Index = 0;
|
||||
|
||||
// Number of bytes in the request's Data stage.
|
||||
setupPacket.Length = Convert.ToUInt16(dataStage.Length);
|
||||
|
||||
// Command-specific value to send to the device.
|
||||
setupPacket.Value = 0;
|
||||
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// Initiates a control transfer.
|
||||
// paramaters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// WinUSBSetupPacket structure
|
||||
// Buffer to hold the returned Data-stage data.
|
||||
// Number of data bytes to read in the Data stage.
|
||||
// Number of bytes read in the Data stage.
|
||||
// Null pointer for non-overlapped.
|
||||
// returns
|
||||
// True on success.
|
||||
// ***
|
||||
|
||||
uint bytesReturned = 0;
|
||||
var success = WinUsb_ControlTransfer(MyDevInfo.WinUSBHandle,
|
||||
setupPacket,
|
||||
dataStage,
|
||||
Convert.ToUInt16(dataStage.Length),
|
||||
ref bytesReturned,
|
||||
IntPtr.Zero);
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates a Control Write transfer. Data stage is host to device.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dataStage"> The data to send. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
|
||||
internal bool Do_Control_Write_Transfer(byte[] dataStage)
|
||||
{
|
||||
// Vendor-specific request to an interface with host-to-device Data stage.
|
||||
WinUSBSetupPacket setupPacket;
|
||||
setupPacket.RequestType = 0X41;
|
||||
|
||||
// The request number that identifies the specific request.
|
||||
setupPacket.Request = 1;
|
||||
|
||||
// Command-specific value to send to the device.
|
||||
var index = Convert.ToUInt16(0);
|
||||
setupPacket.Index = index;
|
||||
|
||||
// Number of bytes in the request's Data stage.
|
||||
setupPacket.Length = Convert.ToUInt16(dataStage.Length);
|
||||
|
||||
// Command-specific value to send to the device.
|
||||
var value = Convert.ToUInt16(0);
|
||||
setupPacket.Value = value;
|
||||
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// Initiates a control transfer.
|
||||
// parameters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// WinUSBSetupPacket structure
|
||||
// Buffer containing the Data-stage data.
|
||||
// Number of data bytes to send in the Data stage.
|
||||
// Number of bytes sent in the Data stage.
|
||||
// Null pointer for non-overlapped.
|
||||
// Returns
|
||||
// True on success.
|
||||
// ***
|
||||
uint bytesReturned = 0;
|
||||
var success = WinUsb_ControlTransfer(MyDevInfo.WinUSBHandle,
|
||||
setupPacket,
|
||||
dataStage,
|
||||
Convert.ToUInt16(dataStage.Length),
|
||||
ref bytesReturned,
|
||||
IntPtr.Zero);
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests a handle with CreateFile.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="devicePathName"> Returned by SetupDiGetDeviceInterfaceDetail
|
||||
/// in an SP_DEVICE_INTERFACE_DETAIL_DATA structure. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// The handle.
|
||||
/// </returns>
|
||||
|
||||
internal bool GetDeviceHandle(string devicePathName)
|
||||
{
|
||||
var security = new FileIO.SECURITY_ATTRIBUTES
|
||||
{
|
||||
lpSecurityDescriptor = IntPtr.Zero,
|
||||
bInheritHandle = Convert.ToInt32(true)
|
||||
};
|
||||
|
||||
security.nLength = Marshal.SizeOf(security);
|
||||
|
||||
System.Diagnostics.Trace.Assert(null == MyDevInfo.DeviceHandle || MyDevInfo.DeviceHandle.IsClosed);
|
||||
// ***
|
||||
// API function
|
||||
// summary
|
||||
// Retrieves a handle to a device.
|
||||
// parameters
|
||||
// Device path name returned by SetupDiGetDeviceInterfaceDetail
|
||||
// Type of access requested (read/write).
|
||||
// FILE_SHARE attributes to allow other processes to access the device while this handle is open.
|
||||
// Security structure. Using Null for this may cause problems under Windows XP.
|
||||
// Creation disposition value. Use OPEN_EXISTING for devices.
|
||||
// Flags and attributes for files. The winsub driver requires FILE_FLAG_OVERLAPPED.
|
||||
// Handle to a template file. Not used.
|
||||
// Returns
|
||||
// A handle or INVALID_HANDLE_VALUE.
|
||||
// ***
|
||||
MyDevInfo.DeviceHandle = FileIO.CreateFile(devicePathName,
|
||||
FileIO.GENERIC_WRITE | FileIO.GENERIC_READ,
|
||||
FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE,
|
||||
ref security,
|
||||
FileIO.OPEN_EXISTING,
|
||||
FileIO.FILE_ATTRIBUTE_NORMAL | FileIO.FILE_FLAG_OVERLAPPED,
|
||||
0);
|
||||
|
||||
if (!MyDevInfo.DeviceHandle.IsInvalid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
FileIODeclarations.GetLastError();
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("GetDeviceHandle(", devicePathName, ") - error - ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a device interface and obtains information about it.
|
||||
/// Calls these winusb API functions:
|
||||
/// WinUsb_Initialize
|
||||
/// WinUsb_QueryInterfaceSettings
|
||||
/// WinUsb_QueryPipe
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
internal bool InitializeDevice()
|
||||
{
|
||||
USBInterfaceDescriptor ifaceDescriptor;
|
||||
ifaceDescriptor.bLength = 0;
|
||||
ifaceDescriptor.bDescriptorType = 0;
|
||||
ifaceDescriptor.bInterfaceNumber = 0;
|
||||
ifaceDescriptor.bAlternateSetting = 0;
|
||||
ifaceDescriptor.bNumEndpoints = 0;
|
||||
ifaceDescriptor.bInterfaceClass = 0;
|
||||
ifaceDescriptor.bInterfaceSubClass = 0;
|
||||
ifaceDescriptor.bInterfaceProtocol = 0;
|
||||
ifaceDescriptor.iInterface = 0;
|
||||
|
||||
WinUSBPipeInformation pipeInfo;
|
||||
pipeInfo.PipeTypes = 0;
|
||||
pipeInfo.PipeId = 0;
|
||||
pipeInfo.MaximumPacketSize = 0;
|
||||
pipeInfo.Interval = 0;
|
||||
|
||||
MyDevInfo.UseHybridBulkIntMode = false;
|
||||
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// get a handle for communications with a winusb device '
|
||||
// parameters
|
||||
// Handle returned by CreateFile.
|
||||
// Device handle to be returned.
|
||||
// returns
|
||||
// True on success.
|
||||
// ***
|
||||
var success = WinUsb_Initialize(MyDevInfo.DeviceHandle, ref MyDevInfo.WinUSBHandle);
|
||||
|
||||
if (!success) return false;
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// Get a structure with information about the device interface.
|
||||
// parameters
|
||||
// handle returned by WinUsb_Initialize
|
||||
// alternate interface setting number
|
||||
// USBInterfaceDescriptor structure to be returned.
|
||||
// returns
|
||||
// True on success.
|
||||
|
||||
success = WinUsb_QueryInterfaceSettings(MyDevInfo.WinUSBHandle,
|
||||
0,
|
||||
ref ifaceDescriptor);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Get the transfer type, endpoint number, and direction for the interface's
|
||||
// bulk and interrupt endpoints. Set pipe policies.
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// returns information about a USB pipe (endpoint address)
|
||||
// parameters
|
||||
// Handle returned by WinUsb_Initialize
|
||||
// Alternate interface setting number
|
||||
// Number of an endpoint address associated with the interface.
|
||||
// (The values count up from zero and are NOT the same as the endpoint address
|
||||
// in the endpoint descriptor.)
|
||||
// WinUSBPipeInformation structure to be returned
|
||||
// returns
|
||||
// True on success
|
||||
// ***
|
||||
var pipeTimeout = Convert.ToUInt32(0);
|
||||
for (var i = 0; i <= ifaceDescriptor.bNumEndpoints - 1; i++)
|
||||
{
|
||||
WinUsb_QueryPipe(MyDevInfo.WinUSBHandle,
|
||||
0,
|
||||
Convert.ToByte(i),
|
||||
ref pipeInfo);
|
||||
|
||||
if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeBulk && UsbEndpointDirectionIn(pipeInfo.PipeId))
|
||||
{
|
||||
MyDevInfo.BulkInPipe = pipeInfo.PipeId;
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.BulkInPipe,
|
||||
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
|
||||
Convert.ToByte(false));
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.BulkInPipe,
|
||||
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
|
||||
pipeTimeout);
|
||||
|
||||
}
|
||||
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeBulk && UsbEndpointDirectionOut(pipeInfo.PipeId))
|
||||
{
|
||||
MyDevInfo.BulkOutPipe = pipeInfo.PipeId;
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.BulkOutPipe,
|
||||
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
|
||||
Convert.ToByte(false));
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.BulkOutPipe,
|
||||
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
|
||||
pipeTimeout);
|
||||
|
||||
}
|
||||
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeInterrupt && UsbEndpointDirectionIn(pipeInfo.PipeId))
|
||||
{
|
||||
MyDevInfo.InterruptInPipe = pipeInfo.PipeId;
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.InterruptInPipe,
|
||||
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
|
||||
Convert.ToByte(false));
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.InterruptInPipe,
|
||||
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
|
||||
pipeTimeout);
|
||||
|
||||
}
|
||||
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeInterrupt && UsbEndpointDirectionOut(pipeInfo.PipeId))
|
||||
{
|
||||
MyDevInfo.InterruptOutPipe = pipeInfo.PipeId;
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.InterruptOutPipe,
|
||||
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
|
||||
Convert.ToByte(false));
|
||||
|
||||
SetPipePolicy
|
||||
(MyDevInfo.InterruptOutPipe,
|
||||
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
|
||||
pipeTimeout);
|
||||
|
||||
MyDevInfo.UseHybridBulkIntMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the current operating system Windows XP or later?
|
||||
/// The WinUSB driver requires Windows XP or later.
|
||||
/// </summary>
|
||||
///
|
||||
/// <returns>
|
||||
/// True if Windows XP or later, False if not.
|
||||
/// </returns>
|
||||
internal bool IsWindowsXpOrLater()
|
||||
{
|
||||
var myEnvironment = Environment.OSVersion;
|
||||
|
||||
// Windows XP is version 5.1.
|
||||
var versionXP = new Version(5, 1);
|
||||
|
||||
return myEnvironment.Version >= versionXP;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that corresponds to a USBDeviceSpeeds.
|
||||
/// </summary>
|
||||
internal bool QueryDeviceSpeed()
|
||||
{
|
||||
// ***
|
||||
// winusb function
|
||||
// summary
|
||||
// Get the device speed.
|
||||
// (Normally not required but can be nice to know.)
|
||||
// parameters
|
||||
// Handle returned by WinUsb_Initialize
|
||||
// Requested information type.
|
||||
// Number of bytes to read.
|
||||
// Information to be returned.
|
||||
// returns
|
||||
// True on success.
|
||||
// ***
|
||||
uint length = 1;
|
||||
var speed = new byte[1];
|
||||
var success = WinUsb_QueryDeviceInformation(MyDevInfo.WinUSBHandle,
|
||||
DEVICE_SPEED,
|
||||
ref length,
|
||||
ref speed[0]);
|
||||
|
||||
if (success)
|
||||
{
|
||||
MyDevInfo.DeviceSpeed = Convert.ToUInt32(speed[0]);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// see winerror.h, these are error codes that we can expect or have received from
|
||||
/// read pipe
|
||||
/// </summary>
|
||||
private const long ERROR_GEN_FAILURE = 31L;
|
||||
private const long ERROR_LOCK_VIOLATION = 33L;
|
||||
private const long ERROR_INVALID_HANDLE = 6L;
|
||||
private const long ERROR_OPERATION_ABORTED = 995L;
|
||||
private const long ERROR_IO_INCOMPLETE = 996L;
|
||||
private const long ERROR_IO_PENDING = 997L;
|
||||
private const long ERROR_NOT_ENOUGH_MEMORY = 8L;
|
||||
private const long ERROR_SEM_TIMEOUT = 121L;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to read data from a bulk IN endpoint.
|
||||
/// </summary>
|
||||
/// <param name="pipeId"> Endpoint address. </param>
|
||||
/// <param name="bytesToRead"> Number of bytes to read. </param>
|
||||
/// <param name="buffer"> Buffer for storing the bytes read. </param>
|
||||
/// <param name="bytesRead"> Number of bytes read. </param>
|
||||
/// <param name="success"> Success or failure status. </param>
|
||||
///
|
||||
internal void ReadViaBulkTransfer(byte pipeId,
|
||||
uint bytesToRead,
|
||||
ref byte[] buffer,
|
||||
ref uint bytesRead,
|
||||
ref bool success)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// Attempts to read data from a device interface.
|
||||
|
||||
// parameters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// Endpoint address.
|
||||
// Buffer to store the data.
|
||||
// Maximum number of bytes to return.
|
||||
// Number of bytes read.
|
||||
// Null pointer for non-overlapped.
|
||||
|
||||
// Returns
|
||||
// True on success.
|
||||
// ***
|
||||
|
||||
success = WinUsb_ReadPipe
|
||||
(MyDevInfo.WinUSBHandle,
|
||||
pipeId,
|
||||
buffer,
|
||||
bytesToRead,
|
||||
ref bytesRead,
|
||||
IntPtr.Zero);
|
||||
|
||||
if (success) return;
|
||||
long lastError = Marshal.GetLastWin32Error();
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
switch (w32.NativeErrorCode)
|
||||
{
|
||||
case (int)ERROR_IO_PENDING:
|
||||
case (int)ERROR_IO_INCOMPLETE:
|
||||
case (int)ERROR_OPERATION_ABORTED:
|
||||
break;
|
||||
default:
|
||||
APILogger.Log(string.Format("ReadViaBulkTransfer failed Message:{0} Error: {1}", w32.Message, w32.NativeErrorCode));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
catch { }
|
||||
switch (lastError)
|
||||
{
|
||||
case ERROR_GEN_FAILURE:
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe General failure - " + ERROR_GEN_FAILURE.ToString());
|
||||
break;
|
||||
case ERROR_LOCK_VIOLATION:
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Lock violation - " + ERROR_LOCK_VIOLATION.ToString());
|
||||
break;
|
||||
case ERROR_INVALID_HANDLE://do we clean up if the handle is invalid?
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Invalid Handle - " + ERROR_INVALID_HANDLE.ToString());
|
||||
break;
|
||||
case ERROR_IO_PENDING://this one should be okay
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe I/O Pending - " + ERROR_IO_PENDING.ToString());
|
||||
break;
|
||||
case ERROR_NOT_ENOUGH_MEMORY://this we'd probably never see
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Not Enough Memory - " + ERROR_NOT_ENOUGH_MEMORY.ToString());
|
||||
break;
|
||||
case ERROR_SEM_TIMEOUT:
|
||||
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Semaphore timeout - " + ERROR_SEM_TIMEOUT.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("ReadViaBulkTranser - error ", ex.Message, " - ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to read data from an interrupt IN endpoint.
|
||||
/// </summary>
|
||||
/// <param name="pipeId"> Endpoint address. </param>
|
||||
/// <param name="bytesToRead"> Number of bytes to read. </param>
|
||||
/// <param name="buffer"> Buffer for storing the bytes read. </param>
|
||||
/// <param name="bytesRead"> Number of bytes read. </param>
|
||||
/// <param name="success"> Success or failure status. </param>
|
||||
///
|
||||
internal void ReadViaInterruptTransfer(byte pipeId,
|
||||
uint bytesToRead,
|
||||
ref byte[] buffer,
|
||||
ref uint bytesRead,
|
||||
ref bool success)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// Attempts to read data from a device interface.
|
||||
|
||||
// parameters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// Endpoint address.
|
||||
// Buffer to store the data.
|
||||
// Maximum number of bytes to return.
|
||||
// Number of bytes read.
|
||||
// Null pointer for non-overlapped.
|
||||
|
||||
// Returns
|
||||
// True on success.
|
||||
// ***
|
||||
|
||||
success = WinUsb_ReadPipe(MyDevInfo.WinUSBHandle,
|
||||
pipeId,
|
||||
buffer,
|
||||
bytesToRead,
|
||||
ref bytesRead,
|
||||
IntPtr.Zero);
|
||||
|
||||
if (success) return;
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("ReadViaInterruptTransfer failed, ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("ReadViaInterruptTransfer failed, ", ex.Message, " - ", w32.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to send data via a bulk OUT endpoint.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="buffer"> Buffer containing the bytes to write. </param>
|
||||
/// <param name="bytesToWrite"> Number of bytes to write. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
internal bool SendViaBulkTransfer(ref byte[] buffer, uint bytesToWrite)
|
||||
{
|
||||
uint bytesWritten = 0;
|
||||
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// Attempts to write data to a device interface.
|
||||
|
||||
// parameters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// Endpoint address.
|
||||
// Buffer with data to write.
|
||||
// Number of bytes to write.
|
||||
// Number of bytes written.
|
||||
// IntPtr.Zero for non-overlapped I/O.
|
||||
|
||||
// Returns
|
||||
// True on success.
|
||||
// ***
|
||||
System.Threading.Thread.Sleep(5);
|
||||
var success = WinUsb_WritePipe
|
||||
(MyDevInfo.WinUSBHandle,
|
||||
MyDevInfo.BulkOutPipe,
|
||||
buffer,
|
||||
bytesToWrite,
|
||||
ref bytesWritten,
|
||||
IntPtr.Zero);
|
||||
|
||||
if (success) return true;
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("WritePipe failed, buffer[", BitConverter.ToString(buffer, 0, Convert.ToInt32(bytesToWrite)), "], bytes: ", bytesToWrite, ", ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to send data via an interrupt OUT endpoint.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="buffer"> Buffer containing the bytes to write. </param>
|
||||
/// <param name="bytesToWrite"> Number of bytes to write. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
internal bool SendViaInterruptTransfer(ref byte[] buffer, uint bytesToWrite)
|
||||
{
|
||||
uint bytesWritten = 0;
|
||||
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// Attempts to write data to a device interface.
|
||||
|
||||
// parameters
|
||||
// Device handle returned by WinUsb_Initialize.
|
||||
// Endpoint address.
|
||||
// Buffer with data to write.
|
||||
// Number of bytes to write.
|
||||
// Number of bytes written.
|
||||
// IntPtr.Zero for non-overlapped I/O.
|
||||
|
||||
// Returns
|
||||
// True on success.
|
||||
// ***
|
||||
|
||||
var success = WinUsb_WritePipe
|
||||
(MyDevInfo.WinUSBHandle,
|
||||
MyDevInfo.InterruptOutPipe,
|
||||
buffer,
|
||||
bytesToWrite,
|
||||
ref bytesWritten,
|
||||
IntPtr.Zero);
|
||||
|
||||
if (success) return true;
|
||||
try
|
||||
{
|
||||
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
APILogger.Log("SendViaInterruptTransfer failed - bytes[", BitConverter.ToString(buffer, 0, Convert.ToInt32(bytesToWrite)), "] - ", w32.Message);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets pipe policy.
|
||||
/// Used when the value parameter is a Byte (all except PIPE_TRANSFER_TIMEOUT).
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="pipeId"> Pipe to set a policy for. </param>
|
||||
/// <param name="policyType"> POLICY_TYPE member. </param>
|
||||
/// <param name="value"> Policy value. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
///
|
||||
private bool SetPipePolicy(byte pipeId, uint policyType, byte value)
|
||||
{
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// sets a pipe policy
|
||||
|
||||
// parameters
|
||||
// handle returned by WinUsb_Initialize
|
||||
// identifies the pipe
|
||||
// POLICY_TYPE member.
|
||||
// length of value in bytes
|
||||
// value to set for the policy.
|
||||
|
||||
// returns
|
||||
// True on success
|
||||
// ***
|
||||
|
||||
var success = WinUsb_SetPipePolicy
|
||||
(MyDevInfo.WinUSBHandle,
|
||||
pipeId,
|
||||
policyType,
|
||||
Convert.ToUInt32(1),
|
||||
ref value);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets pipe policy.
|
||||
/// Used when the value parameter is a UInt32 (PIPE_TRANSFER_TIMEOUT only).
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="pipeId"> Pipe to set a policy for. </param>
|
||||
/// <param name="policyType"> POLICY_TYPE member. </param>
|
||||
/// <param name="value"> Policy value. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True on success, False on failure.
|
||||
/// </returns>
|
||||
///
|
||||
private bool SetPipePolicy(byte pipeId, uint policyType, uint value)
|
||||
{
|
||||
// ***
|
||||
// winusb function
|
||||
|
||||
// summary
|
||||
// sets a pipe policy
|
||||
|
||||
// parameters
|
||||
// handle returned by WinUsb_Initialize
|
||||
// identifies the pipe
|
||||
// POLICY_TYPE member.
|
||||
// length of value in bytes
|
||||
// value to set for the policy.
|
||||
|
||||
// returns
|
||||
// True on success
|
||||
// ***
|
||||
|
||||
var success = WinUsb_SetPipePolicy1
|
||||
(MyDevInfo.WinUSBHandle,
|
||||
pipeId,
|
||||
policyType,
|
||||
Convert.ToUInt32(4),
|
||||
ref value);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the endpoint's direction IN (device to host)?
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="addr"> The endpoint address. </param>
|
||||
/// <returns>
|
||||
/// True if IN (device to host), False if OUT (host to device)
|
||||
/// </returns>
|
||||
private static bool UsbEndpointDirectionIn(int addr)
|
||||
{
|
||||
var usbEndpointDirectionInReturn = (addr & 0X80) == 0X80;
|
||||
return usbEndpointDirectionInReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the endpoint's direction OUT (host to device)?
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="addr"> The endpoint address. </param>
|
||||
///
|
||||
/// <returns>
|
||||
/// True if OUT (host to device, False if IN (device to host)
|
||||
/// </returns>
|
||||
private static bool UsbEndpointDirectionOut(int addr)
|
||||
{
|
||||
var usbEndpointDirectionOutReturn = (addr & 0X80) == 0;
|
||||
return usbEndpointDirectionOutReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTS.Common.WINUSBConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// These declarations are translated from the C declarations in various files
|
||||
/// in the Windows DDK. The files are:
|
||||
///
|
||||
/// winddk\6001\inc\api\usb.h
|
||||
/// winddk\6001\inc\api\usb100.h
|
||||
/// winddk\6001\inc\api\winusbio.h
|
||||
///
|
||||
/// (your home directory and release number may vary)
|
||||
/// </summary>
|
||||
|
||||
internal sealed partial class WinUsbDevice
|
||||
{
|
||||
internal const uint DEVICE_SPEED = 1;
|
||||
internal const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
|
||||
|
||||
internal enum PolicyType
|
||||
{
|
||||
ShortPacketTerminate = 1,
|
||||
AutoClearStall,
|
||||
PipeTransferTimeout,
|
||||
IgnoreShortPackets,
|
||||
AllowPartialReads,
|
||||
AutoFlush,
|
||||
RawIO,
|
||||
}
|
||||
|
||||
internal enum USBDPipeTypes
|
||||
{
|
||||
UsbdPipeTypeControl,
|
||||
UsbdPipeTypeIsochronous,
|
||||
UsbdPipeTypeBulk,
|
||||
UsbdPipeTypeInterrupt,
|
||||
}
|
||||
|
||||
internal enum USBDeviceSpeeds
|
||||
{
|
||||
UsbLowSpeed = 1,
|
||||
UsbFullSpeed,
|
||||
UsbHighSpeed,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct USBConfigurationDescriptor
|
||||
{
|
||||
internal byte bLength;
|
||||
internal byte bDescriptorType;
|
||||
internal ushort wTotalLength;
|
||||
internal byte bNumInterfaces;
|
||||
internal byte bConfigurationValue;
|
||||
internal byte iConfiguration;
|
||||
internal byte bmAttributes;
|
||||
internal byte MaxPower;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct USBInterfaceDescriptor
|
||||
{
|
||||
internal byte bLength;
|
||||
internal byte bDescriptorType;
|
||||
internal byte bInterfaceNumber;
|
||||
internal byte bAlternateSetting;
|
||||
internal byte bNumEndpoints;
|
||||
internal byte bInterfaceClass;
|
||||
internal byte bInterfaceSubClass;
|
||||
internal byte bInterfaceProtocol;
|
||||
internal byte iInterface;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct WinUSBPipeInformation
|
||||
{
|
||||
internal USBDPipeTypes PipeTypes;
|
||||
internal byte PipeId;
|
||||
internal ushort MaximumPacketSize;
|
||||
internal byte Interval;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct WinUSBSetupPacket
|
||||
{
|
||||
internal byte RequestType;
|
||||
internal byte Request;
|
||||
internal ushort Value;
|
||||
internal ushort Index;
|
||||
internal ushort Length;
|
||||
}
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_ControlTransfer(IntPtr interfaceHandle, WinUSBSetupPacket setupPacket, byte[] buffer, uint bufferLength, ref uint lengthTransferred, IntPtr overlapped);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_Free(IntPtr interfaceHandle);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_Initialize(SafeFileHandle deviceHandle, ref IntPtr interfaceHandle);
|
||||
|
||||
// Use this declaration to retrieve DEVICE_SPEED (the only currently defined InformationType).
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_QueryDeviceInformation(IntPtr interfaceHandle, uint informationType, ref uint bufferLength, ref byte buffer);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_QueryInterfaceSettings(IntPtr interfaceHandle, byte alternateInterfaceNumber, ref USBInterfaceDescriptor usbAltInterfaceDescriptor);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_QueryPipe(IntPtr interfaceHandle, byte alternateInterfaceNumber, byte pipeIndex, ref WinUSBPipeInformation pipeInformation);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_ReadPipe(IntPtr interfaceHandle, byte pipeId, byte[] buffer, uint bufferLength, ref uint lengthTransferred, IntPtr overlapped);
|
||||
|
||||
// Two declarations for WinUsb_SetPipePolicy.
|
||||
// Use this one when the returned Value is a Byte (all except PIPE_TRANSFER_TIMEOUT):
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_SetPipePolicy(IntPtr interfaceHandle, byte pipeId, uint policyType, uint valueLength, ref byte Value);
|
||||
|
||||
// Use this alias when the returned Value is a UInt32 (PIPE_TRANSFER_TIMEOUT only):
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true, EntryPoint = "WinUsb_SetPipePolicy")]
|
||||
internal static extern bool WinUsb_SetPipePolicy1(IntPtr interfaceHandle, byte pipeId, uint policyType, uint valueLength, ref uint Value);
|
||||
|
||||
[DllImport("winusb.dll", SetLastError = true)]
|
||||
internal static extern bool WinUsb_WritePipe(IntPtr interfaceHandle, Byte pipeId, byte[] buffer, uint bufferLength, ref uint lengthTransferred, IntPtr overlapped);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user