Вопрос по winapi – Определение того, какое имя принтера соответствует какому идентификатору устройства

10

Моя цель - открыть принтер, подключенный через USB, используяCreateFile (а потом выпуститьWriteFiles иReadFiles).

Если бы принтер был LPT, я бы просто сделалCreateFile("LPT1:", ...). Но для USB-принтеров есть специальный путь к устройству, который необходимо передать вCreateFile чтобы открыть этот принтер.

Это путь к устройству, как я смог найти, получается черезSetupDiGetClassDevs ->SetupDiEnumDeviceInterfaces ->SetupDiGetDeviceInterfaceDetail ->DevicePath member и выглядит так:

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

Все хорошо, но то, что я имею в качестве ввода, - это удобочитаемое имя принтера, как видно изDevices and Printers.SetupDi* функции, кажется, не используют это, они работают только на идентификаторы экземпляра устройства. Так что вопрос сейчас Как получить идентификатор экземпляра устройства из имени принтера, который можно передатьOpenPrinter.

Нетрудно заметить, что часть GUID, описанная выше, являетсяGUID_DEVINTERFACE_USBPRINT, а также\\?\usb исправлено, поэтому единственное, что меня действительно интересует, этоvid_0a5f&pid_0027#46a072900549#. Этот путь я могу легко найти вручную в диалоговом окне свойств принтера:

Перейти к устройствам и принтерам
Щелкните правой кнопкой мыши на принтере
Свойства
Переключиться на вкладку «Оборудование»
Выберите печатающее устройство, например ZDesigner LP2844-Z
Свойства
Переключиться на вкладку «Сведения»
Выберите «Родитель» из выпадающего списка.

Но я понятия не имею, как это сделать программно, при условии, что единственное, что было указано, - это имя принтера, отображаемое на панели «Устройство и принтеры».

P.S. 1: я не заинтересован в открытии принтера сOpenPrinter и затем с помощьюWritePrinter / ReadPrinter. Это было сделано, работает отлично, но теперь цель другая.

P.S. 2: У меня все будет в порядке с более простым способом преобразования читаемого имени принтера во что-то, что можно передать вCreateFile.

P.S. 3:Этот вопро, на который я отправилотве, очень связано с тем, что я в конечном итоге хочу сделать.

P.S. 4: Обратный путь тоже хорошо: Если можно получить читаемое имя изSP_DEVINFO_DATA структура, это тоже будет ответом, хотя и менее удобный.

@ STATUS_ACCESS_DENIED Да, очевидно. Найденный путь сразу передается вCreateFile. GSerg
Достаточно справедливо - я выхожу из своей глубины здесь ... Я думаю, что вы, вероятно, хотите использоватьPnP Configuration Manager Функции, напримерCM_Get_Device_ID (в худшем случае вы можете использоватьCM_Get_Device_ID_List и перебирайте, пока не найдете нужный принтер), но нам придется ждать кого-то, кто лучше знает API, чтобы помочь больше. eggyal
@ eggyal Не знал об этом, но похоже, что это идентификаторы моделей, а не идентификаторы устройств (у нас на одном компьютере установлено несколько идентичных принтеров). Кроме,CreateFile терпит неудачу при представлении с"USBPRINT\Zebra_TechnologiesZT9FD5". GSerg
Я, наверное, тупой, но ты не можешь использоватьUSBPRINT идентификаторы? eggyal
Полагаю, нужно учитывать только подключенные принтеры? 0xC0000022L

Ваш Ответ

5   ответов
4

Пожалуйста, подтвердите, чтоSYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData - это поддерживаемый путь, а не просто так в текущей реализации, с учетом будущих изменений.

Есть небольшая проблема с выравниванием структуры, для которой я разместил отдельный вопрос.

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);
    }

}

Использование

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
    }
}
Я попробовал ваш образец, и он дошел до:SetupDIEnumDeviceInfo(hdi, 0, ref printer_data) и это возвращает ложь. Я звонилMarshal.GetLastWin32Error() и мой код ошибки 259. Я в тупике. Мой принтер подключен к USB и отображается в реестре. Mike
Приведенный выше код должен работать для принтеров, работающих по принципу «подключи и работай» (большинство потребительских принтеров). В моем случае принтер не был установлен через PnP и, соответственно, ключ PNPData не существовал. Следует также отметить, что подобный поиск в жестком ключе реестра не рекомендуется, так как организация реестра может быть изменена. Однако я еще не нашел функции API для установления связи между именем принтера в устройствах и принтерах и его фактическим устройством. walkingTarget
1

если это поможет ...

    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();
    }

У вас нет USB-принтера для тестирования, но он предоставляет информацию, которую вы ищете (в том числе для USB-устройств) ...

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'

Также для URI замените '\' на '#' в URI, который вы намереваетесь создать.

так .

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

Становится

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

====

Поскольку GSerg указал, что класс Win32_Printer помогает с приведенным выше кодом, но не предоставляет идентификатор устройства.

Но если я использую класс Win32_Printer и распечатываю свойство «PortName», то для установленных принтеров будет указан порт / имя файла, который я могу использовать с CreateFile () и открыть устройство.

например.

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


Name         : Fax
Description  :
DeviceID     : Fax
PNPDeviceID  :
PortName  : SHRFAX:

Вот, запись в «XPSPORT:» или «SHRFAX:» отправляет данные на принтер. Что это делает для вашего USB-принтера?

Да, я пробовал\\.\USB002 а также\\?\USB002. Принтер не является общим и не будет. GSerg
Помогает ли свойство PortName? chkdsk
Это многообещающе; однако, это не помогает в настоящее время.Name он дает не имя принтера, это имя драйвера. Итак, еще раз, у меня нет возможности узнать, правильный ли это принтер или нет. И если я вместо этого запроситьWin32_Printer, тогдаPNPDeviceID пусто, аDeviceID бесполезен (совпадает с именем принтера). GSerg
Нет,PortName не помогает. ЭтоUSB002, а такжеCreateFile не может открыть это. Напротив, если бы порт был "LPT1:", это бы успешно. Если вы знаете, как сделатьCreateFile открыть что-то вродеUSB002, это определенно будет ответом. GSerg
Ты пробовал .. \\. \ USB002 ?? Также запомнилось, что если принтер является общим, то вы можете напрямую открыть принтер по имени: «\\ machine-name \ printer-name». chkdsk
1

WinObj от Microsoft, чтобы получить конкретное имя устройства.http: //technet.microsoft.com/en-us/sysinternals/bb896657.asp Это быстро даст вам правильное имя устройства для использования сCreateFile для прямой записи на USB-принтер или просто для прямой записи на USB-адаптер принтера с выходом параллельного порта старой школы для пользовательских схем!

Чтобы открыть порт, связанный с конкретным принтером, вам может потребоваться использоватьntcreatefile. ИспользоватьEnumPrintersункция @ для возвратаprinter_info_2 структура, содержащая имя порта для каждого принтера. Это имя порта можно затем открыть с помощьюntcreatefile (внутренняя версия NT дляCreateFile), что объясняется здесь:http: //msdn.microsoft.com/en-us/library/bb432380 (v = vs.85) .aspx

Почему это работает? Существует три уровня пространства имен в именах файлов / устройств Windows NT и имя порта, полученное изEnumPrinters можно открыть только с помощьюntcreatefile потому что это только в пространстве имен NT. Для некоторых устройств может существовать эквивалентная ссылка на пространство имен win32 и окольные способы сопоставления их с именем принтера, но это сложно, как другие показали в предыдущих ответах.

ПроверьтеGlobal?? папка вWinObjнструмент @, чтобы показать символические ссылки между пространством имен win32 и пространством имен NT на вашем компьютере. Старая школаCOM1, COM2, LPT1 и т. д. Имена устройств также являются символическими ссылками пространства имен Windows NT. Google "Win32 NT пространство имен" для более подробного объяснения. (Извините, но как новый пользователь я могу опубликовать только 2 гиперссылки.)

0

но я не думаю, что попытка сгенерировать идентификатор устройства по названию - это путь. Тем не менее, вы можете

ИспользуйтеEnumPrinters и читатьPRINTER_INFO_2 structure, чтобы получить имя драйвера, а затем прочитать сведения о драйвере из реестра, как в этот пример.

Сгенерируйте имя для себя, узнав подробности о принтере из PRINTER INFO структура и правильно его построить. Видетьhttp: //msdn.microsoft.com/en-us/library/windows/hardware/ff553356 (v = vs.85) .aspx для подробностей.

РЕДАКТИРОВАТЬ Вы можете получить имена + идентификаторы экземпляров устройств из реестра:

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

Речь идет не о C ++, я доволен любым языком (и на самом деле я использую C # для этого проекта), я просто выбрал C ++ как самый простой способ начать играть с API и структурами без необходимости объявлять Атрибуты PInvoke и т. Д. Имя драйвера бесполезно, поскольку оно совпадает с теми же принтерами, которые я установил. Если вы посмотрите на мой собственный ответ к этому вопросу я уже делаю нечто похожее на то, на что вы ссылались, но я не уверен, поддерживается ли чтение реестра таким способом (в отличие от реализации). GSerg
Я на самом деле говорю, что вам даже не нужно использовать API-вызовы CreateFile и OpenPrinter для получения информации о принтере, когда есть WMI. Chibueze Opata
Хм, если ты делаешь C #, тогда зачем нужны вызовы API? Chibueze Opata
Как еще ты собираешься получить эту информацию? Сначала преобразуйте идентификатор устройства повторного использования в идентификатор родительского устройства, затем идентификатор родительского устройства в путь интерфейса usbprint? .NET Framework, кажется, не предоставляет пути. GSerg
Мне не нужна информация о принтере. Мне нужна только информация о принтере, чтобы открыть этот принтер с помощьюCreateFile. Нельзя обойтись без подсистемы печати .NET, потому что она открывает принтер другим способом, через спулер. WMI не предоставляет необходимую информацию, так как я заметил в комментарии к ответу ПрашантГупты. GSerg
3

Попробуй это (код Python):

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

Похожие вопросы