Figuring which printer name corresponds to which device ID

前端 未结 5 1224
没有蜡笔的小新
没有蜡笔的小新 2020-12-10 04:59

My goal is to open a printer connected via USB using the CreateFile (and then issue some WriteFiles and ReadFiles).

If the pri

相关标签:
5条回答
  • 2020-12-10 05:22

    Use WinObj from Microsoft to get the specific device name. http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx . This will quickly get you the proper device name to use with CreateFile to write directly to your USB printer or simply writing directly to a USB printer adapter with old school parallel port output for custom circuitry!

    To open the port associated with a specific printer, you may need to use ntcreatefile. Use the EnumPrinters function to return a printer_info_2 structure containing the port name for each printer. This port name can then be opened with ntcreatefile (an NT internal version of CreateFile) which is explained here: http://msdn.microsoft.com/en-us/library/bb432380(v=vs.85).aspx

    Why does this work? There are three namespace levels in windows NT file/device names and the port name retrieved from EnumPrinters can only be opened with ntcreatefile because it is only in the NT namespace. There may be an equivalent win32 namespace link for certain devices and roundabout ways to match them with a printer name but this is difficult as others have shown in prior answers.

    Check out the Global?? folder in WinObj tool to show the symbolic links between win32 namespace and NT namespace on your machine. The old school COM1, COM2, LPT1, etc. device names are simply windows NT namespace symbolic links as well. Google "win32 nt namespace" for a more detailed explanation. (Sorry, but as a new user, I can only post 2 hyperlinks.)

    0 讨论(0)
  • 2020-12-10 05:25

    Try this ... let me know if this helps ...

        static void Main(string[] args)
        {
            ManagementObjectSearcher s = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity");
            foreach (ManagementObject device in s.Get())
            {
                // Try Name, Caption and/or Description (they seem to be same most of the time).
                string Name = (string)device.GetPropertyValue("Name");
    
                // >>>>>>>>>>>>>>>>>>>> Query String ...
                if (Name == "O2Micro Integrated MMC/SD controller")
                {
                    /*
                     class Win32_PnPEntity : CIM_LogicalDevice
                    {
                      uint16   Availability;
                      string   Caption;
                      string   ClassGuid;
                      string   CompatibleID[];
                      uint32   ConfigManagerErrorCode;
                      boolean  ConfigManagerUserConfig;
                      string   CreationClassName;
                      string   Description;
                      string   DeviceID;
                      boolean  ErrorCleared;
                      string   ErrorDescription;
                      string   HardwareID[];
                      datetime InstallDate;
                      uint32   LastErrorCode;
                      string   Manufacturer;
                      string   Name;
                      string   PNPDeviceID;
                      uint16   PowerManagementCapabilities[];
                      boolean  PowerManagementSupported;
                      string   Service;
                      string   Status;
                      uint16   StatusInfo;
                      string   SystemCreationClassName;
                      string   SystemName;
                    };
                    */
    
                    try
                    {
                        Console.WriteLine("Name         : {0}", Name);
                        Console.WriteLine("DeviceID     : {0}", device.GetPropertyValue("DeviceID"));
                        Console.WriteLine("PNPDeviceID  : {0}", device.GetPropertyValue("PNPDeviceID"));
                        Console.WriteLine("ClassGuid    : {0}", device.GetPropertyValue("ClassGuid"));
                        Console.WriteLine("HardwareID   :\n{0}", JoinStrings(device.GetPropertyValue("HardwareID") as string[]));
                        Console.WriteLine("CompatibleID :\n{0}", JoinStrings(device.GetPropertyValue("CompatibleID") as string[]));
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("ERROR: {0}", e.Message);
                    }
                }
            }
        }
    
        static string JoinStrings(string[] sarray)
        {
            StringBuilder b = new StringBuilder();
            if (sarray != null)
            {
                foreach (string s in sarray)
                    b.Append("        '" + s + "'\n");
            }
            return b.ToString();
        }
    

    Don't have a USB printer to test against, but this provides the information you are looking for (including for USB devices)...

    Description  : O2Micro Integrated MMC/SD controller
    DeviceID     : PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05\4&26B31A7F&0&00E5
    PNPDeviceID  : PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05\4&26B31A7F&0&00E5
    ClassGuid    : {4d36e97b-e325-11ce-bfc1-08002be10318}
    HardwareID   :
            'PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05'
            'PCI\VEN_1217&DEV_8221&SUBSYS_04931028'
            'PCI\VEN_1217&DEV_8221&CC_080501'
            'PCI\VEN_1217&DEV_8221&CC_0805'
    
    CompatibleID :         'PCI\VEN_1217&DEV_8221&REV_05'
            'PCI\VEN_1217&DEV_8221'
            'PCI\VEN_1217&CC_080501'
            'PCI\VEN_1217&CC_0805'
            'PCI\VEN_1217'
            'PCI\CC_080501'
            'PCI\CC_0805'
    

    Also, for a URI, change the '\'s to '#'s in the URI you are intending of building.

    so ..

    usb\vid_0a5f&pid_0027\46a072900549\{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
    

    becomes

    usb#vid_0a5f&pid_0027#46a072900549#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
    

    ====

    As GSerg pointed out that Win32_Printer Class helps with the above code, but doesn't provide the device id.

    But if I use Win32_Printer class and print out the "PortName" property, that, for the printers I have installed gives be a port/filename that I can use with CreateFile() and open the device.

    e.g.:

    Name         : Microsoft XPS Document Writer
    Description  :
    DeviceID     : Microsoft XPS Document Writer
    PNPDeviceID  :
    PortName  : XPSPort:
    
    
    Name         : Fax
    Description  :
    DeviceID     : Fax
    PNPDeviceID  :
    PortName  : SHRFAX:
    

    Here, writing to "XPSPORT:" or "SHRFAX:" sends data to the printer. What does this do for your USB printer?

    0 讨论(0)
  • 2020-12-10 05:25

    I am not really a c++ guy, but I don't really think trying to generate the device id from the name is the way to go. However, you can

    1. Use EnumPrinters and read the PRINTER_INFO_2 structure to get the driver name, and then read the driver details from registry like in this example.

    2. Generate the name for yourself by finding out the printer details possibly from the PRINTER INFO structures and constructing it correctly. See http://msdn.microsoft.com/en-us/library/windows/hardware/ff553356(v=vs.85).aspx for details.

    EDIT: You can actually get names + device instance ids of printers from registry:

    HKLM\System\CurrentControlSet\Control\Print\Printers\

    0 讨论(0)
  • 2020-12-10 05:39

    Below is what I finally have been able to come up with.

    Please confirm that SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData is a supported path, and not just happens to be there in the current implementation, subject to future changes.

    There's a little problem with structure alignment, for which I've posted a separate question.

    public static class UsbPrinterResolver
    {
    
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct SP_DEVINFO_DATA
        {
            public uint cbSize;
            public Guid ClassGuid;
            public uint DevInst;
            public IntPtr Reserved;
        }
    
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct SP_DEVICE_INTERFACE_DATA
        {
            public uint cbSize;
            public Guid InterfaceClassGuid;
            public uint Flags;
            public IntPtr Reserved;
        }
    
    
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
        private struct SP_DEVICE_INTERFACE_DETAIL_DATA  // Only used for Marshal.SizeOf. NOT!
        {
            public uint cbSize;
            public char DevicePath;
        }
    
    
        [DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false, ExactSpelling = true)]
        private static extern uint CM_Get_Parent(out uint pdnDevInst, uint dnDevInst, uint ulFlags);
    
        [DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false)]
        private static extern uint CM_Get_Device_ID(uint dnDevInst, string Buffer, uint BufferLen, uint ulFlags);
    
        [DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false, ExactSpelling = true)]
        private static extern uint CM_Get_Device_ID_Size(out uint pulLen, uint dnDevInst, uint ulFlags);
    
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetupDiGetClassDevs([In(), MarshalAs(UnmanagedType.LPStruct)] System.Guid ClassGuid, string Enumerator, IntPtr hwndParent, uint Flags);
    
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);
    
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, [In()] ref SP_DEVINFO_DATA DeviceInfoData, [In(), MarshalAs(UnmanagedType.LPStruct)] System.Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
    
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, [In()] ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, out uint RequiredSize, IntPtr DeviceInfoData);
    
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        private static extern int SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
    
        private const uint DIGCF_PRESENT = 0x00000002U;
        private const uint DIGCF_DEVICEINTERFACE = 0x00000010U;
        private const int ERROR_INSUFFICIENT_BUFFER = 122;
        private const uint CR_SUCCESS = 0;
    
        private const int FILE_SHARE_READ = 1;
        private const int FILE_SHARE_WRITE = 2;
        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;
        private const int OPEN_EXISTING = 3;
    
        private static readonly Guid GUID_PRINTER_INSTALL_CLASS = new Guid(0x4d36e979, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
        private static readonly Guid GUID_DEVINTERFACE_USBPRINT = new Guid(0x28d78fad, 0x5a12, 0x11D1, 0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2);
        private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    
    
        private static string GetPrinterRegistryInstanceID(string PrinterName) {
            if (string.IsNullOrEmpty(PrinterName)) throw new ArgumentNullException("PrinterName");
    
            const string key_template = @"SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData";
    
            using (var hk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
                                string.Format(key_template, PrinterName),
                                Microsoft.Win32.RegistryKeyPermissionCheck.Default,
                                System.Security.AccessControl.RegistryRights.QueryValues
                            )
                   )
            {
    
                if (hk == null) throw new ArgumentOutOfRangeException("PrinterName", "This printer does not have PnP data.");
    
                return (string)hk.GetValue("DeviceInstanceId");
            }
        }
    
        private static string GetPrinterParentDeviceId(string RegistryInstanceID) {
            if (string.IsNullOrEmpty(RegistryInstanceID)) throw new ArgumentNullException("RegistryInstanceID");
    
            IntPtr hdi = SetupDiGetClassDevs(GUID_PRINTER_INSTALL_CLASS, RegistryInstanceID, IntPtr.Zero, DIGCF_PRESENT);
            if (hdi.Equals(INVALID_HANDLE_VALUE)) throw new System.ComponentModel.Win32Exception();
    
            try
            {
                SP_DEVINFO_DATA printer_data = new SP_DEVINFO_DATA();
                printer_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
    
                if (SetupDiEnumDeviceInfo(hdi, 0, ref printer_data) == 0) throw new System.ComponentModel.Win32Exception();   // Only one device in the set
    
                uint cmret = 0;
    
                uint parent_devinst = 0;
                cmret = CM_Get_Parent(out parent_devinst, printer_data.DevInst, 0);
                if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
    
    
                uint parent_device_id_size = 0;
                cmret = CM_Get_Device_ID_Size(out parent_device_id_size, parent_devinst, 0);
                if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get size of the device ID of the parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
    
                parent_device_id_size++;  // To include the null character
    
                string parent_device_id = new string('\0', (int)parent_device_id_size);
                cmret = CM_Get_Device_ID(parent_devinst, parent_device_id, parent_device_id_size, 0);
                if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get device ID of the parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
    
                return parent_device_id;
            }
            finally
            {
                SetupDiDestroyDeviceInfoList(hdi);
            }
        }
    
        private static string GetUSBInterfacePath(string SystemDeviceInstanceID) {
            if (string.IsNullOrEmpty(SystemDeviceInstanceID)) throw new ArgumentNullException("SystemDeviceInstanceID");
    
            IntPtr hdi = SetupDiGetClassDevs(GUID_DEVINTERFACE_USBPRINT, SystemDeviceInstanceID, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
            if (hdi.Equals(INVALID_HANDLE_VALUE)) throw new System.ComponentModel.Win32Exception();
    
            try
            {
                SP_DEVINFO_DATA device_data = new SP_DEVINFO_DATA();
                device_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
    
                if (SetupDiEnumDeviceInfo(hdi, 0, ref device_data) == 0) throw new System.ComponentModel.Win32Exception();  // Only one device in the set
    
                SP_DEVICE_INTERFACE_DATA interface_data = new SP_DEVICE_INTERFACE_DATA();
                interface_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
    
                if (SetupDiEnumDeviceInterfaces(hdi, ref device_data, GUID_DEVINTERFACE_USBPRINT, 0, ref interface_data) == 0) throw new System.ComponentModel.Win32Exception();   // Only one interface in the set
    
    
                // Get required buffer size
                uint required_size = 0;
                SetupDiGetDeviceInterfaceDetail(hdi, ref interface_data, IntPtr.Zero, 0, out required_size, IntPtr.Zero);
    
                int last_error_code = Marshal.GetLastWin32Error();
                if (last_error_code != ERROR_INSUFFICIENT_BUFFER) throw new System.ComponentModel.Win32Exception(last_error_code);
    
                IntPtr interface_detail_data = Marshal.AllocCoTaskMem((int)required_size);
    
                try
                {
    
                    // FIXME, don't know how to calculate the size.
                    // See https://stackoverflow.com/questions/10728644/properly-declare-sp-device-interface-detail-data-for-pinvoke
    
                    switch (IntPtr.Size)
                    {
                        case 4:
                            Marshal.WriteInt32(interface_detail_data, 4 + Marshal.SystemDefaultCharSize);
                            break;
                        case 8:
                            Marshal.WriteInt32(interface_detail_data, 8);
                            break;
    
                        default:
                            throw new NotSupportedException("Architecture not supported.");
                    }
    
                    if (SetupDiGetDeviceInterfaceDetail(hdi, ref interface_data, interface_detail_data, required_size, out required_size, IntPtr.Zero) == 0) throw new System.ComponentModel.Win32Exception();
    
                    // TODO: When upgrading to .NET 4, replace that with IntPtr.Add
                    return Marshal.PtrToStringAuto(new IntPtr(interface_detail_data.ToInt64() + Marshal.OffsetOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA), "DevicePath").ToInt64()));
    
                }
                finally
                {
                    Marshal.FreeCoTaskMem(interface_detail_data);
                }
            }
            finally
            {
                SetupDiDestroyDeviceInfoList(hdi);
            }
        }
    
    
        public static string GetUSBPath(string PrinterName) {
            return GetUSBInterfacePath(GetPrinterParentDeviceId(GetPrinterRegistryInstanceID(PrinterName)));
        }
    
        public static Microsoft.Win32.SafeHandles.SafeFileHandle OpenUSBPrinter(string PrinterName) {
            return new Microsoft.Win32.SafeHandles.SafeFileHandle(CreateFile(GetUSBPath(PrinterName), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero), true);
        }
    
    }
    

    Usage:

    using (var sh = UsbPrinterResolver.OpenUSBPrinter("Zebra Large"))
    {
        using (var f = new System.IO.FileStream(sh, System.IO.FileAccess.ReadWrite))
        {
            // Read from and write to the stream f
        }
    }
    
    0 讨论(0)
  • 2020-12-10 05:39

    Try this (Python code):

    import _winreg
    
    HKLM = _winreg.HKEY_LOCAL_MACHINE
    
    #------------------------------------------------------------------------------
    def getDevicePath(printerName):
        key = _winreg.OpenKey(HKLM,
            r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\%s" \
            % printerName)
    
        value =_winreg.QueryValueEx(key, "Port")[0]
        assert value.startswith("USB"), \
               "Port does not start with 'USB': %s" % value
    
        printerPortNumber = int(value.replace(u"USB", u""))
    
        key = _winreg.OpenKey(HKLM,
                r"SYSTEM\CurrentControlSet\Control\DeviceClasses" \
                r"\{28d78fad-5a12-11d1-ae5b-0000f803a8c2}")
    
        idx = 0
        devicePath = None
        while True:
            try:
                subKeyName = _winreg.EnumKey(key, idx)
                subKey = _winreg.OpenKey(key, subKeyName)
    
                try:
                    subSubKey = _winreg.OpenKey(subKey, r"#\Device Parameters")
                    baseName = _winreg.QueryValueEx(subSubKey, "Base Name")[0]
                    portNumber = _winreg.QueryValueEx(subSubKey, "Port Number")[0]
                    if baseName == "USB" and portNumber == printerPortNumber:
                        devicePath = subKeyName.replace("##?#USB", r"\\?\usb")
                        break
    
                except WindowsError:
                    continue
    
                finally:
                    idx += 1
    
            except WindowsError:
                break
    
        return devicePath
    
    0 讨论(0)
提交回复
热议问题