Created
February 8, 2024 18:19
-
-
Save markgodiy/9fdacc9f05c1122a64a0029db117f6ce to your computer and use it in GitHub Desktop.
ZebraUSBSteam.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // The provided code consists of several C# classes and namespaces that work together to facilitate USB communication with Zebra printers from a .NET application. Below is a brief overview of each component: | |
| // ZebraUsbStream.cs: Defines a ZebraUsbStream class that extends the System.IO.Stream class, specifically for communicating with Zebra printers via USB. It uses an instance of UsbPrinterConnector to handle the low-level USB operations. The class overrides several members of the Stream class, such as Read(), Write(), and Flush(), to utilize the underlying USB connection. It also manages connection state and timeouts. | |
| // FileIO.cs: Contains the FileIO class, which is a utility class providing P/Invoke signatures for various Windows API functions related to file and device I/O operations. This includes methods for creating files (CreateFile), writing to files (WriteFile), and reading from files (ReadFile), among others. These functions are used for low-level access to the USB devices. | |
| // PrinterConnector.cs: Introduces an abstract PrinterConnector class that serves as a base for any type of printer connection. It defines common properties and methods such as IsConnected, ReadTimeout, WriteTimeout, and abstract methods for sending and reading data. This class is designed to be extended by specific connector implementations like UsbPrinterConnector. | |
| // SetupApi.cs: Contains the SetupApi class which provides P/Invoke signatures and structures for using the Windows Setup API. These functions are used to enumerate devices, get device information, and interact with device interfaces, which are essential for discovering and managing USB printer connections. | |
| // UsbPrinterConnector.cs: Implements the PrinterConnector abstract class to provide functionality specific to USB-connected printers. It includes methods for enumerating USB devices, establishing connections, and reading/writing data. The core of its functionality revolves around the Windows API for working with files and devices, as it opens a connection to the printer using the CreateFile method and performs I/O operations with ReadFile and WriteFile. It also manages connection state and ensures that data transfer is performed correctly over USB. | |
| // Each class is designed to encapsulate specific aspects of the process of communicating with printers via USB, from discovering devices and opening connections to performing read/write operations. The code is structured to be modular, allowing for easier maintenance and the potential to extend functionality (e.g., adding support for other connection types besides USB). | |
| //------------------------------- | |
| // ZebraUsbStream.cs | |
| //------------------------------- | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| using System.IO; | |
| using Zebra.Printing; | |
| namespace RfidPrinter | |
| { | |
| /// | |
| /// Stream subclass which incorporates low-level USB access to Zebra printers | |
| /// | |
| public class ZebraUsbStream : Stream | |
| { | |
| UsbPrinterConnector usb; | |
| public ZebraUsbStream(string port) | |
| { | |
| usb = new UsbPrinterConnector(port); | |
| usb.IsConnected = true; | |
| base.ReadTimeout = usb.ReadTimeout; | |
| base.WriteTimeout = usb.WriteTimeout; | |
| } | |
| public ZebraUsbStream() | |
| { | |
| System.Collections.Specialized.NameValueCollection devs = | |
| UsbPrinterConnector.EnumDevices(true, true, false); | |
| if (devs.Count < 1) | |
| throw new Exception("No Zebra printers found"); | |
| usb = new UsbPrinterConnector(devs[0].ToString()); | |
| usb.IsConnected = true; | |
| } | |
| public override bool CanRead | |
| { | |
| get { return true; } | |
| } | |
| public override bool CanSeek | |
| { | |
| get { return false; } | |
| } | |
| public override bool CanWrite | |
| { | |
| get { return true; } | |
| } | |
| public override bool CanTimeout | |
| { | |
| get { return true; } | |
| } | |
| public override void Flush() | |
| { | |
| ; | |
| } | |
| public override long Length | |
| { | |
| get { throw new NotSupportedException(); } | |
| } | |
| public override long Position | |
| { | |
| get | |
| { | |
| throw new NotSupportedException(); | |
| } | |
| set | |
| { | |
| throw new NotSupportedException(); | |
| } | |
| } | |
| public override int Read(byte[] buffer, int offset, int count) | |
| { | |
| return usb.Read(buffer, offset, count); | |
| } | |
| public override long Seek(long offset, SeekOrigin origin) | |
| { | |
| throw new NotSupportedException(); | |
| } | |
| public override void SetLength(long value) | |
| { | |
| throw new NotSupportedException(); | |
| } | |
| public override void Write(byte[] buffer, int offset, int count) | |
| { | |
| usb.Send(buffer, offset, count); | |
| } | |
| protected override void Dispose(bool disposing) | |
| { | |
| base.Dispose(disposing); | |
| usb.IsConnected = false; | |
| } | |
| public override void Close() | |
| { | |
| base.Close(); | |
| if (usb.IsConnected) | |
| usb.IsConnected = false; | |
| } | |
| public override int ReadTimeout | |
| { | |
| get | |
| { | |
| return usb.ReadTimeout; | |
| } | |
| set | |
| { | |
| usb.ReadTimeout = value; | |
| } | |
| } | |
| public override int WriteTimeout | |
| { | |
| get | |
| { | |
| return usb.WriteTimeout; | |
| } | |
| set | |
| { | |
| usb.WriteTimeout = value; | |
| } | |
| } | |
| } | |
| } | |
| //------------------------------- | |
| //FileIO.cs | |
| //------------------------------- | |
| #region License | |
| /* --------------------------------------------------------------------------- | |
| * Creative Commons License | |
| * http://creativecommons.org/licenses/by/2.5/au/ | |
| * | |
| * Attribution 2.5 Australia | |
| * | |
| * You are free: | |
| * | |
| * - to copy, distribute, display, and perform the work | |
| * - to make derivative works | |
| * - to make commercial use of the work | |
| * | |
| * Under the following conditions: | |
| * | |
| * Attribution: You must attribute the work in the manner specified by the | |
| * author or licensor. | |
| * | |
| * For any reuse or distribution, you must make clear to others the license | |
| * terms of this work. Any of these conditions can be waived if you get | |
| * permission from the copyright holder. Your fair use and other rights | |
| * are in no way affected by the above. | |
| * | |
| * This is a human-readable summary of the Legal Code (the full license). | |
| * http://creativecommons.org/licenses/by/2.5/au/legalcode | |
| * ------------------------------------------------------------------------ */ | |
| /* --------------------------------------------------------------------------- | |
| * Special Note | |
| * | |
| * A special mention and thanks to the contributions of several parties in | |
| * blogging and publishing this complex API. | |
| * | |
| * P/Invoke .NET - http://www.pinvoke.net | |
| * | |
| * MSDN Magazine | |
| * | |
| * ------------------------------------------------------------------------ */ | |
| #endregion License | |
| // $Header: /cvsroot/z-bar/msvs/zbar/Zebra.Printing/fileio.cs,v 1.4 2006/11/16 10:55:04 vinorodrigues Exp $ | |
| #if WindowsCE || PocketPC | |
| #define WINCE | |
| #endif | |
| using System; | |
| using System.Runtime.InteropServices; | |
| using System.Threading; | |
| #if WINCE | |
| #error This module is not intended for Mobile platform | |
| #endif | |
| namespace Zebra.Printing | |
| { | |
| internal class FileIO | |
| { | |
| internal const int INVALID_HANDLE_VALUE = -1; | |
| internal const int ERROR_FILE_NOT_FOUND = 2; | |
| internal const int ERROR_INVALID_NAME = 123; | |
| internal const int ERROR_ACCESS_DENIED = 5; | |
| internal const int ERROR_IO_PENDING = 997; | |
| internal const int ERROR_IO_INCOMPLETE = 996; | |
| internal class NullClass | |
| { | |
| public NullClass() | |
| { | |
| throw new Exception("Cannot create instance of NullClass"); | |
| } | |
| } | |
| #region CreateFile | |
| [Flags] | |
| internal enum FileAccess : uint // from winnt.h | |
| { | |
| GENERIC_READ = 0x80000000, | |
| GENERIC_WRITE = 0x40000000, | |
| GENERIC_EXECUTE = 0x20000000, | |
| GENERIC_ALL = 0x10000000 | |
| } | |
| [Flags] | |
| internal enum FileShareMode : uint // from winnt.h | |
| { | |
| FILE_SHARE_READ = 0x00000001, | |
| FILE_SHARE_WRITE = 0x00000002, | |
| FILE_SHARE_DELETE = 0x00000004 | |
| } | |
| internal enum FileCreationDisposition : uint // from winbase.h | |
| { | |
| CREATE_NEW = 1, | |
| CREATE_ALWAYS = 2, | |
| OPEN_EXISTING = 3, | |
| OPEN_ALWAYS = 4, | |
| TRUNCATE_EXISTING = 5 | |
| } | |
| [Flags] | |
| internal enum FileAttributes : uint // from winnt.h | |
| { | |
| FILE_ATTRIBUTE_READONLY = 0x00000001, | |
| FILE_ATTRIBUTE_HIDDEN = 0x00000002, | |
| FILE_ATTRIBUTE_SYSTEM = 0x00000004, | |
| FILE_ATTRIBUTE_DIRECTORY = 0x00000010, | |
| FILE_ATTRIBUTE_ARCHIVE = 0x00000020, | |
| FILE_ATTRIBUTE_DEVICE = 0x00000040, | |
| FILE_ATTRIBUTE_NORMAL = 0x00000080, | |
| FILE_ATTRIBUTE_TEMPORARY = 0x00000100, | |
| FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200, | |
| FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400, | |
| FILE_ATTRIBUTE_COMPRESSED = 0x00000800, | |
| FILE_ATTRIBUTE_OFFLINE = 0x00001000, | |
| FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000, | |
| FILE_ATTRIBUTE_ENCRYPTED = 0x00004000, | |
| // from winbase.h | |
| FILE_FLAG_WRITE_THROUGH = 0x80000000, | |
| FILE_FLAG_OVERLAPPED = 0x40000000, | |
| FILE_FLAG_NO_BUFFERING = 0x20000000, | |
| FILE_FLAG_RANDOM_ACCESS = 0x10000000, | |
| FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000, | |
| FILE_FLAG_DELETE_ON_CLOSE = 0x04000000, | |
| FILE_FLAG_BACKUP_SEMANTICS = 0x02000000, | |
| FILE_FLAG_POSIX_SEMANTICS = 0x01000000, | |
| FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000, | |
| FILE_FLAG_OPEN_NO_RECALL = 0x00100000, | |
| FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 | |
| } | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| internal static extern IntPtr CreateFile( | |
| string lpFileName, | |
| FileAccess dwDesiredAccess, | |
| FileShareMode dwShareMode, | |
| IntPtr lpSecurityAttributes, | |
| FileCreationDisposition dwCreationDisposition, | |
| FileAttributes dwFlagsAndAttributes, | |
| IntPtr hTemplateFile); | |
| #endregion CreateFile | |
| #region CloseHandle | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool CloseHandle(IntPtr hObject); | |
| #endregion CloseHandle | |
| #region GetOverlappedResult | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool GetOverlappedResult( | |
| IntPtr hFile, | |
| /* IntPtr */ ref System.Threading.NativeOverlapped lpOverlapped, | |
| out uint nNumberOfBytesTransferred, | |
| bool bWait); | |
| #endregion GetOverlappedResult | |
| #region WriteFile | |
| [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteFile")] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool WriteFile0( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)] | |
| byte[] lpBuffer, | |
| uint nNumberOfBytesToWrite, | |
| out uint lpNumberOfBytesWritten, | |
| NullClass lpOverlapped); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool WriteFile( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, | |
| uint nNumberOfBytesToWrite, | |
| out uint lpNumberOfBytesWritten, | |
| [In] ref System.Threading.NativeOverlapped lpOverlapped); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| internal static extern int WriteFileEx( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, | |
| int nNumberOfBytesToWrite, | |
| [In] ref System.Threading.NativeOverlapped lpOverlapped, | |
| [MarshalAs(UnmanagedType.FunctionPtr)] IOCompletionCallback callback | |
| ); | |
| #endregion WriteFile | |
| #region ReadFile | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool ReadFile( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)][Out] byte[] lpBuffer, | |
| uint nNumberOfBytesToRead, | |
| out uint lpNumberOfBytesRead, | |
| [In] ref System.Threading.NativeOverlapped lpOverlapped); | |
| [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "ReadFile")] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static extern bool ReadFile0( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)][Out] byte[] lpBuffer, | |
| uint nNumberOfBytesToRead, | |
| out uint lpNumberOfBytesRead, | |
| NullClass lpOverlapped); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| internal static extern int ReadFileEx( | |
| IntPtr hFile, | |
| [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, | |
| int nNumberOfBytesToRead, | |
| [In] ref System.Threading.NativeOverlapped lpOverlapped, | |
| [MarshalAs(UnmanagedType.FunctionPtr)] IOCompletionCallback callback); | |
| #endregion ReadFile | |
| } | |
| } | |
| //------------------------------- | |
| // PrinterConnector.cs | |
| //------------------------------- | |
| #region License | |
| /* --------------------------------------------------------------------------- | |
| * Creative Commons License | |
| * http://creativecommons.org/licenses/by/2.5/au/ | |
| * | |
| * Attribution 2.5 Australia | |
| * | |
| * You are free: | |
| * | |
| * - to copy, distribute, display, and perform the work | |
| * - to make derivative works | |
| * - to make commercial use of the work | |
| * | |
| * Under the following conditions: | |
| * | |
| * Attribution: You must attribute the work in the manner specified by the | |
| * author or licensor. | |
| * | |
| * For any reuse or distribution, you must make clear to others the license | |
| * terms of this work. Any of these conditions can be waived if you get | |
| * permission from the copyright holder. Your fair use and other rights | |
| * are in no way affected by the above. | |
| * | |
| * This is a human-readable summary of the Legal Code (the full license). | |
| * http://creativecommons.org/licenses/by/2.5/au/legalcode | |
| * ------------------------------------------------------------------------ */ | |
| #endregion License | |
| // $Header: /cvsroot/z-bar/msvs/zbar/Zebra.Printing/Connect.cs,v 1.7 2006/11/16 10:55:04 vinorodrigues Exp $ | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| namespace Zebra.Printing | |
| { | |
| public abstract class PrinterConnector | |
| { | |
| protected abstract void SetConnected(bool value); | |
| protected abstract bool GetConnected(); | |
| public bool IsConnected | |
| { | |
| get { return GetConnected(); } | |
| set { SetConnected(value); } | |
| } | |
| public static readonly int DefaultReadTimeout = 200; | |
| private int readTimeout = DefaultReadTimeout; | |
| public int ReadTimeout | |
| { | |
| get { return readTimeout; } | |
| set { readTimeout = value; } | |
| } | |
| public static readonly int DefaultWriteTimeout = 200; | |
| private int writeTimeout = DefaultWriteTimeout; | |
| public int WriteTimeout | |
| { | |
| get { return writeTimeout; } | |
| set { writeTimeout = value; } | |
| } | |
| /* public bool Connect() | |
| { | |
| SetConnected(true); | |
| return GetConnected(); | |
| } */ | |
| /* public void Disconnect() | |
| { | |
| SetConnected(false); | |
| } */ | |
| public int Send(byte[] buffer) | |
| { | |
| return Send(buffer, 0, buffer.Length); | |
| } | |
| public abstract bool BeginSend(); | |
| public abstract void EndSend(); | |
| public abstract int Send(byte[] buffer, int offset, int count); | |
| public virtual bool CanRead() | |
| { | |
| return false; | |
| } | |
| /// | |
| /// Reads data from the incomming connection. | |
| /// | |
| /// populated data buffer or null if empty/unsuccessful | |
| /// Number of bytes read or -1 if unsuccessful | |
| public abstract int Read(byte[] buffer, int offset, int count); | |
| } | |
| } | |
| //------------------------------- | |
| // SetupApi.cs | |
| //------------------------------- | |
| #region License | |
| /* --------------------------------------------------------------------------- | |
| * Creative Commons License | |
| * http://creativecommons.org/licenses/by/2.5/au/ | |
| * | |
| * Attribution 2.5 Australia | |
| * | |
| * You are free: | |
| * | |
| * - to copy, distribute, display, and perform the work | |
| * - to make derivative works | |
| * - to make commercial use of the work | |
| * | |
| * Under the following conditions: | |
| * | |
| * Attribution: You must attribute the work in the manner specified by the | |
| * author or licensor. | |
| * | |
| * For any reuse or distribution, you must make clear to others the license | |
| * terms of this work. Any of these conditions can be waived if you get | |
| * permission from the copyright holder. Your fair use and other rights | |
| * are in no way affected by the above. | |
| * | |
| * This is a human-readable summary of the Legal Code (the full license). | |
| * http://creativecommons.org/licenses/by/2.5/au/legalcode | |
| * ------------------------------------------------------------------------ */ | |
| /* --------------------------------------------------------------------------- | |
| * Special Note | |
| * | |
| * A special mention and thanks to the contributions of several parties in | |
| * blogging and publishing this complex API. Converting to C# was not easy! | |
| * | |
| * The "setupapi.h" file from the Microsoft DDK for Windows XP SP2 | |
| * | |
| * Peter Skarpetis for this blog "GETTING A HANDLE ON USBPRINT.SYS" | |
| * http://blog.peter.skarpetis.com | |
| * | |
| * The Code Project - http://www.codeproject.com | |
| * | |
| * P/Invoke .NET - http://www.pinvoke.net | |
| * | |
| * ------------------------------------------------------------------------ */ | |
| #endregion License | |
| // $Header: /cvsroot/z-bar/msvs/zbar/Zebra.Printing/setupapi.cs,v 1.4 2006/12/28 10:54:21 vinorodrigues Exp $ | |
| #if WindowsCE || PocketPC | |
| #define WINCE | |
| #endif | |
| #if WINCE | |
| #error This module is not intended for Mobile platform | |
| #endif | |
| using System; | |
| using System.Text; | |
| using System.Runtime.InteropServices; | |
| namespace Zebra.Printing | |
| { | |
| internal class SetupApi | |
| { | |
| #region Consts | |
| internal const UInt32 DIGCF_DEFAULT = 0x00000001; // only valid with DIGCF_DEVICEINTERFACE | |
| internal const UInt32 DIGCF_PRESENT = 0x00000002; | |
| internal const UInt32 DIGCF_ALLCLASSES = 0x00000004; | |
| internal const UInt32 DIGCF_PROFILE = 0x00000008; | |
| internal const UInt32 DIGCF_DEVICEINTERFACE = 0x00000010; | |
| internal const UInt32 SPDRP_DEVICEDESC = 0x00000000; // DeviceDesc (R/W) | |
| internal const UInt32 SPDRP_HARDWAREID = 0x00000001; // HardwareID (R/W) | |
| internal const UInt32 SPDRP_COMPATIBLEIDS = 0x00000002; // CompatibleIDs (R/W) | |
| internal const UInt32 SPDRP_UNUSED0 = 0x00000003; // unused | |
| internal const UInt32 SPDRP_SERVICE = 0x00000004; // Service (R/W) | |
| // internal const UInt32 SPDRP_UNUSED1 = 0x00000005; // unused | |
| // internal const UInt32 SPDRP_UNUSED2 = 0x00000006; // unused | |
| internal const UInt32 SPDRP_CLASS = 0x00000007; // Class (R--tied to ClassGUID) | |
| internal const UInt32 SPDRP_CLASSGUID = 0x00000008; // ClassGUID (R/W) | |
| internal const UInt32 SPDRP_DRIVER = 0x00000009; // Driver (R/W) | |
| internal const UInt32 SPDRP_CONFIGFLAGS = 0x0000000A; // ConfigFlags (R/W) | |
| internal const UInt32 SPDRP_MFG = 0x0000000B; // Mfg (R/W) | |
| internal const UInt32 SPDRP_FRIENDLYNAME = 0x0000000C; // FriendlyName (R/W) | |
| internal const UInt32 SPDRP_LOCATION_INFORMATION = 0x0000000D; // LocationInformation (R/W) | |
| internal const UInt32 SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E; // PhysicalDeviceObjectName (R) | |
| internal const UInt32 SPDRP_CAPABILITIES = 0x0000000F; // Capabilities (R) | |
| internal const UInt32 SPDRP_UI_NUMBER = 0x00000010; // UiNumber (R) | |
| internal const UInt32 SPDRP_UPPERFILTERS = 0x00000011; // UpperFilters (R/W) | |
| internal const UInt32 SPDRP_LOWERFILTERS = 0x00000012; // LowerFilters (R/W) | |
| internal const UInt32 SPDRP_BUSTYPEGUID = 0x00000013; // BusTypeGUID (R) | |
| internal const UInt32 SPDRP_LEGACYBUSTYPE = 0x00000014; // LegacyBusType (R) | |
| internal const UInt32 SPDRP_BUSNUMBER = 0x00000015; // BusNumber (R) | |
| internal const UInt32 SPDRP_ENUMERATOR_NAME = 0x00000016; // Enumerator Name (R) | |
| internal const UInt32 SPDRP_SECURITY = 0x00000017; // Security (R/W, binary form) | |
| internal const UInt32 SPDRP_SECURITY_SDS = 0x00000018; // Security (W, SDS form) | |
| internal const UInt32 SPDRP_DEVTYPE = 0x00000019; // Device Type (R/W) | |
| internal const UInt32 SPDRP_EXCLUSIVE = 0x0000001A; // Device is exclusive-access (R/W) | |
| internal const UInt32 SPDRP_CHARACTERISTICS = 0x0000001B; // Device Characteristics (R/W) | |
| internal const UInt32 SPDRP_ADDRESS = 0x0000001C; // Device Address (R) | |
| internal const UInt32 SPDRP_UI_NUMBER_DESC_FORMAT = 0x0000001D; // UiNumberDescFormat (R/W) | |
| internal const UInt32 SPDRP_DEVICE_POWER_DATA = 0x0000001E; // Device Power Data (R) | |
| internal const UInt32 SPDRP_REMOVAL_POLICY = 0x0000001F; // Removal Policy (R) | |
| internal const UInt32 SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x00000020; // Hardware Removal Policy (R) | |
| internal const UInt32 SPDRP_REMOVAL_POLICY_OVERRIDE = 0x00000021; // Removal Policy Override (RW) | |
| internal const UInt32 SPDRP_INSTALL_STATE = 0x00000022; // Device Install State (R) | |
| internal const UInt32 SPDRP_MAXIMUM_PROPERTY = 0x00000023; // Upper bound on ordinals | |
| #endregion Consts | |
| #region Structs | |
| [StructLayout(LayoutKind.Sequential)] | |
| internal struct SP_DEVINFO_DATA | |
| { | |
| /// | |
| /// Size of structure in bytes | |
| /// | |
| public Int32 cbSize; | |
| /// | |
| /// GUID of the device interface class | |
| /// | |
| public Guid classGuid; | |
| /// | |
| /// Handle to this device instance | |
| /// | |
| public Int32 devInst; | |
| /// | |
| /// Reserved; do not use. | |
| /// | |
| public UIntPtr reserved; | |
| }; | |
| [StructLayout(LayoutKind.Sequential)] | |
| internal struct SP_DEVICE_INTERFACE_DATA | |
| { | |
| /// | |
| /// Size of the structure, in bytes | |
| /// | |
| public Int32 cbSize; | |
| /// | |
| /// GUID of the device interface class | |
| /// | |
| public Guid interfaceClassGuid; | |
| /// | |
| /// | |
| /// | |
| public UInt32 flags; | |
| /// | |
| /// Reserved; do not use. | |
| /// | |
| public UIntPtr reserved; | |
| }; | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)] | |
| public struct SP_DEVICE_INTERFACE_DETAIL_DATA | |
| { | |
| public UInt32 cbSize; | |
| [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] // will never be more than 256 in length | |
| public string devicePath; | |
| } | |
| public class NullClass | |
| { | |
| public NullClass() | |
| { | |
| throw new Exception("Cannot create instance of NullClass"); | |
| } | |
| } | |
| #endregion Structs | |
| #region DLLImports | |
| [DllImport("setupapi.dll", CharSet = CharSet.Auto)] | |
| internal static extern IntPtr SetupDiGetClassDevs( | |
| ref Guid ClassGuid, | |
| [MarshalAs(UnmanagedType.LPTStr)] | |
| String Enumerator, | |
| IntPtr hwndParent, | |
| UInt32 Flags); | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] // from PInvoke.net | |
| internal static extern UInt16 SetupDiDestroyDeviceInfoList(IntPtr hDevInfo); | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] // from PInvoke.net | |
| internal static extern Boolean SetupDiEnumDeviceInterfaces( | |
| IntPtr hDevInfo, | |
| ref SP_DEVINFO_DATA devInfo, | |
| ref Guid interfaceClassGuid, | |
| UInt32 memberIndex, | |
| ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
| internal static extern Boolean SetupDiEnumDeviceInterfaces( | |
| IntPtr hDevInfo, | |
| int zero_devInfo, // used for 0 (Zero) | |
| ref Guid interfaceClassGuid, | |
| UInt32 memberIndex, | |
| ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, EntryPoint = "SetupDiGetDeviceInterfaceDetail")] | |
| internal static extern Boolean SetupDiGetDeviceInterfaceDetail0( | |
| IntPtr hDevInfo, | |
| ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, | |
| ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, // OPTIONAL | |
| UInt32 deviceInterfaceDetailDataSize, | |
| /* out UInt32 */ NullClass requiredSize, // OPTIONAL | |
| ref SP_DEVINFO_DATA deviceInfoData); // OPTIONAL | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, EntryPoint = "SetupDiGetDeviceInterfaceDetail")] | |
| internal static extern Boolean SetupDiGetDeviceInterfaceDetail0( | |
| IntPtr hDevInfo, | |
| ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, | |
| NullClass deviceInterfaceDetailData, // OPTIONAL | |
| UInt32 deviceInterfaceDetailDataSize, | |
| out UInt32 requiredSize, // OPTIONAL | |
| NullClass deviceInfoData); // OPTIONAL | |
| /* [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
| internal static extern Boolean SetupDiGetDeviceInterfaceDetail( | |
| IntPtr hDevInfo, | |
| ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, | |
| Int32 zero_deviceInterfaceDetailData, // used for 0 (Zero) | |
| UInt32 zero_deviceInterfaceDetailDataSize, | |
| out UInt32 requiredSize, | |
| Int32 zero_deviceInfoData); // used for 0 (Zero) */ // KEEP | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, EntryPoint = "SetupDiGetDeviceRegistryProperty")] | |
| internal static extern Boolean SetupDiGetDeviceRegistryProperty0( | |
| IntPtr hDevInfo, | |
| ref SP_DEVINFO_DATA deviceInfoData, | |
| UInt32 property, | |
| /* out IntPtr */ NullClass propertyRegDataType, // OPTIONAL | |
| StringBuilder propertyBuffer, | |
| UInt32 propertyBufferSize, | |
| /* out IntPtr */ NullClass requiredSize); // OPTIONAL | |
| [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, EntryPoint = "SetupDiGetDeviceRegistryProperty")] // overloaded for null refs | |
| internal static extern Boolean SetupDiGetDeviceRegistryProperty0( | |
| IntPtr hDevInfo, | |
| ref SP_DEVINFO_DATA deviceInfoData, | |
| UInt32 property, | |
| out IntPtr propertyRegDataType, // OPTIONAL | |
| NullClass propertyBuffer, | |
| UInt32 propertyBufferSize, // set to 0 (Zero) | |
| out UInt32 requiredSize); // OPTIONAL | |
| #endregion DLLImports | |
| } | |
| } | |
| //------------------------------- | |
| // UsbPrinterconnector.cs | |
| //------------------------------- | |
| #region License | |
| /* --------------------------------------------------------------------------- | |
| * Creative Commons License | |
| * http://creativecommons.org/licenses/by/2.5/au/ | |
| * | |
| * Attribution 2.5 Australia | |
| * | |
| * You are free: | |
| * | |
| * - to copy, distribute, display, and perform the work | |
| * - to make derivative works | |
| * - to make commercial use of the work | |
| * | |
| * Under the following conditions: | |
| * | |
| * Attribution: You must attribute the work in the manner specified by the | |
| * author or licensor. | |
| * | |
| * For any reuse or distribution, you must make clear to others the license | |
| * terms of this work. Any of these conditions can be waived if you get | |
| * permission from the copyright holder. Your fair use and other rights | |
| * are in no way affected by the above. | |
| * | |
| * This is a human-readable summary of the Legal Code (the full license). | |
| * http://creativecommons.org/licenses/by/2.5/au/legalcode | |
| * ------------------------------------------------------------------------ */ | |
| /* --------------------------------------------------------------------------- | |
| * Special Note | |
| * | |
| * A special mention and thanks to the contributions of several parties in | |
| * blogging and publishing this complex API. Converting to C# was not easy! | |
| * | |
| * The "setupapi.h" file from the Microsoft DDK for Windows XP SP2 | |
| * | |
| * Peter Skarpetis for this blog "GETTING A HANDLE ON USBPRINT.SYS" | |
| * http://blog.peter.skarpetis.com | |
| * | |
| * The Code Project - http://www.codeproject.com | |
| * | |
| * P/Invoke .NET - http://www.pinvoke.net | |
| * | |
| * ------------------------------------------------------------------------ */ | |
| #endregion License | |
| // $Header: /cvsroot/z-bar/msvs/zbar/Zebra.Printing/ConUsb.cs,v 1.7 2006/12/28 10:54:21 vinorodrigues Exp $ | |
| #if WindowsCE || PocketPC | |
| #define WINCE | |
| #endif | |
| #if WINCE | |
| #error This module is not intended for Mobile platform | |
| #endif | |
| using System; | |
| using System.Text; | |
| using System.Runtime.InteropServices; | |
| using System.ComponentModel; | |
| using System.Collections.Specialized; | |
| using System.IO; | |
| using Microsoft.Win32; | |
| using System.Threading; | |
| namespace Zebra.Printing | |
| { | |
| /// | |
| /// Connector leveraging usbmon.dll/usbprint.sys. | |
| /// Note: This cannot be used in more than one process at a time, so if the spooler is running you cannot use it. | |
| /// | |
| public class UsbPrinterConnector : PrinterConnector | |
| { | |
| #region EnumDevices | |
| static Guid GUID_DEVICEINTERFACE_USBPRINT = new Guid( | |
| 0x28d78fad, 0x5a12, 0x11D1, | |
| 0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2); | |
| // static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); | |
| public static NameValueCollection EnumDevices() | |
| { | |
| return EnumDevices(true); | |
| } | |
| public static NameValueCollection EnumDevices(bool presentOnly) | |
| { | |
| return EnumDevices(presentOnly, true); | |
| } | |
| public static NameValueCollection EnumDevices(bool presentOnly, bool zebraOnly) | |
| { | |
| return EnumDevices(presentOnly, zebraOnly, true); | |
| } | |
| public static NameValueCollection EnumDevices(bool PresentOnly, bool ZebraOnly, bool fullDetail) | |
| { | |
| NameValueCollection res = new NameValueCollection(); | |
| String name, path, desc, port; | |
| Guid intfce; | |
| IntPtr devs; | |
| SetupApi.SP_DEVINFO_DATA devinfo = new SetupApi.SP_DEVINFO_DATA(); | |
| SetupApi.SP_DEVICE_INTERFACE_DATA devinterface = new SetupApi.SP_DEVICE_INTERFACE_DATA(); | |
| SetupApi.SP_DEVICE_INTERFACE_DETAIL_DATA interface_detail; | |
| UInt32 devcount; | |
| UInt32 size; | |
| RegistryKey regKey, subKey; | |
| intfce = GUID_DEVICEINTERFACE_USBPRINT; | |
| UInt32 flags = SetupApi.DIGCF_DEVICEINTERFACE; | |
| if (PresentOnly) | |
| flags |= SetupApi.DIGCF_PRESENT; | |
| devs = SetupApi.SetupDiGetClassDevs(ref intfce, | |
| null, | |
| IntPtr.Zero, | |
| flags); | |
| if (devs == (IntPtr)FileIO.INVALID_HANDLE_VALUE) | |
| return null; | |
| devcount = 0; | |
| devinterface.cbSize = Marshal.SizeOf(typeof(SetupApi.SP_DEVICE_INTERFACE_DATA)); | |
| while (SetupApi.SetupDiEnumDeviceInterfaces( | |
| devs, | |
| 0, | |
| ref intfce, | |
| devcount, | |
| ref devinterface)) | |
| { | |
| devcount++; | |
| SetupApi.SetupDiGetDeviceInterfaceDetail0( | |
| devs, | |
| ref devinterface, | |
| null, | |
| 0, | |
| out size, | |
| null); | |
| if ((size > 0) && | |
| (size <= (Marshal.SizeOf(typeof(SetupApi.SP_DEVICE_INTERFACE_DETAIL_DATA))) - sizeof(UInt32))) | |
| { | |
| interface_detail = new SetupApi.SP_DEVICE_INTERFACE_DETAIL_DATA(); | |
| interface_detail.cbSize = (UInt32)(sizeof(UInt32) + sizeof(Char)); // Wow! This is a gotcha! | |
| devinfo = new SetupApi.SP_DEVINFO_DATA(); | |
| devinfo.cbSize = Marshal.SizeOf(typeof(SetupApi.SP_DEVINFO_DATA)); | |
| if (SetupApi.SetupDiGetDeviceInterfaceDetail0( | |
| devs, | |
| ref devinterface, | |
| ref interface_detail, | |
| size, | |
| null, | |
| ref devinfo)) | |
| { | |
| path = interface_detail.devicePath.ToString(); | |
| name = GetDeviceRegistryProperty(devs, ref devinfo, SetupApi.SPDRP_LOCATION_INFORMATION); | |
| if (fullDetail) | |
| { | |
| desc = ""; | |
| port = ""; | |
| if (path.StartsWith("\\\\?\\")) | |
| { | |
| string key = "##?#" + path.Substring(4); | |
| try | |
| { | |
| regKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\" + | |
| GUID_DEVICEINTERFACE_USBPRINT.ToString("B")); | |
| try | |
| { | |
| subKey = regKey.OpenSubKey(key); | |
| if (subKey != null) | |
| { | |
| subKey = subKey.OpenSubKey("#\\Device Parameters"); | |
| desc = subKey.GetValue("Port Description").ToString(); | |
| port = subKey.GetValue("Port Number").ToString(); | |
| subKey.Close(); | |
| } | |
| } | |
| finally | |
| { | |
| regKey.Close(); | |
| } | |
| } | |
| catch | |
| { | |
| // do nothing | |
| } | |
| } | |
| if (ZebraOnly && (!desc.StartsWith("Zebra"))) | |
| continue; | |
| res.Add(name, path); | |
| res.Add(name, desc); | |
| res.Add(name, port); | |
| } | |
| else | |
| res.Add(name, path); | |
| } | |
| else | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| } | |
| } | |
| SetupApi.SetupDiDestroyDeviceInfoList(devs); | |
| return res; | |
| } | |
| private static String GetDeviceRegistryProperty(IntPtr hDevInfo, | |
| ref SetupApi.SP_DEVINFO_DATA deviceInfoData, | |
| UInt32 property) | |
| { | |
| IntPtr dataType = IntPtr.Zero; | |
| uint size; | |
| StringBuilder buffer; | |
| SetupApi.SetupDiGetDeviceRegistryProperty0( | |
| hDevInfo, | |
| ref deviceInfoData, | |
| property, | |
| out dataType, | |
| null, | |
| 0, | |
| out size); | |
| if (size > 0) | |
| { | |
| buffer = new StringBuilder((int)size); | |
| if (SetupApi.SetupDiGetDeviceRegistryProperty0( | |
| hDevInfo, | |
| ref deviceInfoData, | |
| property, | |
| null, | |
| buffer, | |
| size, | |
| null)) | |
| { | |
| return buffer.ToString(); | |
| } | |
| else | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| } | |
| else | |
| return String.Empty; | |
| } | |
| #endregion EnumDevices | |
| private string interfaceName; | |
| private IntPtr usbHandle = IntPtr.Zero; | |
| public static readonly uint ReadBufferSize = 512; | |
| private byte[] readBuffer; | |
| /// | |
| /// Constructor | |
| /// | |
| /// | |
| public UsbPrinterConnector(string InterfaceName) | |
| : base() | |
| { | |
| if (!InterfaceName.StartsWith("\\")) | |
| { | |
| NameValueCollection plist = EnumDevices(true, false, false); | |
| if (plist.GetValues(InterfaceName) != null) | |
| InterfaceName = plist.GetValues(InterfaceName)[0]; | |
| else | |
| throw new Exception("Cannot locate USB device"); | |
| } | |
| this.interfaceName = InterfaceName; | |
| } | |
| /// | |
| /// Destructor | |
| /// | |
| ~UsbPrinterConnector() | |
| { | |
| SetConnected(false); | |
| } | |
| protected override void SetConnected(bool value) | |
| { | |
| if (value) | |
| { | |
| if ((int)usbHandle > 0) | |
| SetConnected(false); | |
| /* C++ Decl. | |
| usbHandle = CreateFile( | |
| interfacename, | |
| GENERIC_WRITE, | |
| FILE_SHARE_READ, | |
| NULL, | |
| OPEN_ALWAYS, | |
| FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, | |
| NULL); | |
| */ | |
| usbHandle = FileIO.CreateFile( | |
| interfaceName, | |
| FileIO.FileAccess.GENERIC_WRITE | FileIO.FileAccess.GENERIC_READ, | |
| FileIO.FileShareMode.FILE_SHARE_READ, | |
| IntPtr.Zero, | |
| FileIO.FileCreationDisposition.OPEN_ALWAYS, | |
| FileIO.FileAttributes.FILE_ATTRIBUTE_NORMAL | | |
| FileIO.FileAttributes.FILE_FLAG_SEQUENTIAL_SCAN | | |
| FileIO.FileAttributes.FILE_FLAG_OVERLAPPED, | |
| IntPtr.Zero); | |
| if ((int)usbHandle <= 0) | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| } | |
| else | |
| if ((int)usbHandle > 0) | |
| { | |
| FileIO.CloseHandle(usbHandle); | |
| usbHandle = IntPtr.Zero; | |
| } | |
| } | |
| protected override bool GetConnected() | |
| { | |
| return ((int)usbHandle > 0); | |
| } | |
| public override bool BeginSend() | |
| { | |
| return GetConnected(); | |
| } | |
| public override void EndSend() | |
| { | |
| // do nothing | |
| } | |
| public override int Send(byte[] buffer, int offset, int count) | |
| { | |
| // USB 1.1 WriteFile maximum block size is 4096 | |
| uint size; | |
| byte[] bytes; | |
| if (!GetConnected()) | |
| throw new Exception("Not connected"); | |
| if (count > 4096) | |
| { | |
| throw new NotImplementedException(); // TODO: Copy byte array loop | |
| } | |
| else | |
| { | |
| bytes = new byte[count]; | |
| Array.Copy(buffer, offset, bytes, 0, count); | |
| ManualResetEvent wo = new ManualResetEvent(false); | |
| NativeOverlapped ov = new NativeOverlapped(); | |
| // ov.OffsetLow = 0; ov.OffsetHigh = 0; | |
| ov.EventHandle = wo.Handle; | |
| if (!FileIO.WriteFile(usbHandle, bytes, (uint)count, out size, ref ov)) | |
| { | |
| if (Marshal.GetLastWin32Error() == FileIO.ERROR_IO_PENDING) | |
| wo.WaitOne(WriteTimeout, false); | |
| else | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| } | |
| FileIO.GetOverlappedResult(usbHandle, ref ov, out size, true); | |
| return (int)size; | |
| } | |
| } | |
| public override bool CanRead() | |
| { | |
| return true; | |
| } | |
| public override int Read(byte[] buffer, int offset, int count) | |
| { | |
| // USB 1.1 ReadFile in block chunks of 64 bytes | |
| // USB 2.0 ReadFile in block chunks of 512 bytes | |
| uint read; | |
| if (readBuffer == null) | |
| readBuffer = new byte[ReadBufferSize]; | |
| AutoResetEvent sg = new AutoResetEvent(false); | |
| NativeOverlapped ov = new NativeOverlapped(); | |
| ov.OffsetLow = 0; | |
| ov.OffsetHigh = 0; | |
| ov.EventHandle = sg.Handle; | |
| if (!FileIO.ReadFile(usbHandle, readBuffer, ReadBufferSize, out read, ref ov)) | |
| { | |
| if (Marshal.GetLastWin32Error() == FileIO.ERROR_IO_PENDING) | |
| sg.WaitOne(ReadTimeout, false); | |
| else | |
| throw new Win32Exception(Marshal.GetLastWin32Error()); | |
| } | |
| FileIO.GetOverlappedResult(usbHandle, ref ov, out read, true); | |
| Array.Copy(readBuffer, 0, buffer, offset, read); | |
| return (int)read; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment