using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; class PrintUsb { static readonly Guid PrinterIface = new Guid("{28d78fad-5a12-11d1-ae5b-0000f803a8c2}"); const uint GENERIC_WRITE = 0x40000000; const uint FILE_SHARE_READ = 1; const uint FILE_SHARE_WRITE = 2; const uint OPEN_EXISTING = 3; const uint FILE_ATTRIBUTE_NORMAL = 0x80; const int DIGCF_PRESENT = 0x2; const int DIGCF_DEVICEINTERFACE = 0x10; const int INVALID_HANDLE_VALUE = -1; [StructLayout(LayoutKind.Sequential)] struct SP_DEVINFO_DATA { public int cbSize; public Guid ClassGuid; public int DevInst; public IntPtr Reserved; } [StructLayout(LayoutKind.Sequential)] struct SP_DEVICE_INTERFACE_DATA { public int cbSize; public Guid InterfaceClassGuid; public int Flags; public IntPtr Reserved; } [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, string enumerator, IntPtr hwndParent, int flags); [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool SetupDiEnumDeviceInterfaces(IntPtr infoSet, IntPtr devInfo, ref Guid ifaceClass, int memberIndex, ref SP_DEVICE_INTERFACE_DATA ifaceData); [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr infoSet, ref SP_DEVICE_INTERFACE_DATA ifaceData, IntPtr detailData, int detailDataSize, out int requiredSize, IntPtr devInfoData); [DllImport("setupapi.dll", SetLastError = true)] static extern bool SetupDiDestroyDeviceInfoList(IntPtr infoSet); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr CreateFile(string name, uint access, uint share, IntPtr sec, uint creation, uint flags, IntPtr tpl); [DllImport("kernel32.dll", SetLastError = true)] static extern bool WriteFile(IntPtr h, byte[] buf, int count, out int written, IntPtr ovl); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr h); static string LabelFor(string path) { string lower = path.ToLowerInvariant(); int vid = lower.IndexOf("vid_"); int pid = lower.IndexOf("&pid_"); if (vid >= 0 && pid >= 0) return "USB Printer " + lower.Substring(vid + 4, 4).ToUpperInvariant() + " / " + lower.Substring(pid + 5, 4).ToUpperInvariant(); return "USB Printer"; } static List EnumeratePrinterPaths() { var results = new List(); Guid ifaceGuid = PrinterIface; IntPtr infoSet = SetupDiGetClassDevs(ref ifaceGuid, null, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (infoSet.ToInt64() == INVALID_HANDLE_VALUE) return results; var ifaceData = new SP_DEVICE_INTERFACE_DATA(); ifaceData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA)); int idx = 0; while (SetupDiEnumDeviceInterfaces(infoSet, IntPtr.Zero, ref ifaceGuid, idx, ref ifaceData)) { idx++; int req = 0; SetupDiGetDeviceInterfaceDetail(infoSet, ref ifaceData, IntPtr.Zero, 0, out req, IntPtr.Zero); if (req <= 0) continue; IntPtr buf = Marshal.AllocHGlobal(req); try { Marshal.WriteInt32(buf, 0, 8); // sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) header if (SetupDiGetDeviceInterfaceDetail(infoSet, ref ifaceData, buf, req, out req, IntPtr.Zero)) { string path = Marshal.PtrToStringAuto(new IntPtr(buf.ToInt64() + 4)); if (!string.IsNullOrEmpty(path)) results.Add(path); } } finally { Marshal.FreeHGlobal(buf); } } SetupDiDestroyDeviceInfoList(infoSet); return results; } static int Main(string[] args) { if (args.Length < 1) { Console.Error.WriteLine("usage: print-usb --list | print-usb [devicePath]"); return 2; } if (args[0] == "--list") { var devices = EnumeratePrinterPaths(); Console.Out.WriteLine("["); for (int i = 0; i < devices.Count; i++) { string label = LabelFor(devices[i]); string comma = i < devices.Count - 1 ? "," : ""; Console.Out.WriteLine(" {\"path\":\"" + devices[i].Replace("\\", "\\\\") + "\",\"label\":\"" + label + "\"}" + comma); } Console.Out.WriteLine("]"); return 0; } byte[] data; try { data = Convert.FromBase64String(args[0]); } catch (Exception e) { Console.Error.WriteLine("ERR base64: " + e.Message); return 3; } string target = args.Length > 1 ? args[1] : null; var paths = EnumeratePrinterPaths(); Console.Error.WriteLine("printer devices: " + paths.Count); foreach (string p in paths) Console.Error.WriteLine(" " + p); IntPtr h = IntPtr.Zero; foreach (string p in paths) { if (target != null && !p.Equals(target, StringComparison.OrdinalIgnoreCase)) continue; if (target == null && !p.ToLowerInvariant().Contains("usb")) continue; h = CreateFile(p, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (h.ToInt64() != INVALID_HANDLE_VALUE) { Console.Error.WriteLine("using: " + p); break; } Console.Error.WriteLine("open fail err=" + Marshal.GetLastWin32Error() + " : " + p); } if (h.ToInt64() == INVALID_HANDLE_VALUE) { Console.Error.WriteLine("ERR printer not found/not writable"); return 4; } try { int written; if (!WriteFile(h, data, data.Length, out written, IntPtr.Zero)) { Console.Error.WriteLine("ERR write:" + Marshal.GetLastWin32Error()); return 5; } Console.Out.WriteLine("OK:" + written); return 0; } finally { CloseHandle(h); } } }