【问题标题】:Figuring which printer name corresponds to which device ID确定哪个打印机名称对应于哪个设备 ID
【发布时间】:2012-05-18 11:39:29
【问题描述】:

我的目标是使用CreateFile 打开通过USB 连接的打印机(然后发出一些WriteFiles 和ReadFiles)。

如果打印机是 LPT 打印机,我会简单地使用 CreateFile("LPT1:", ...)。但是对于 USB 打印机,必须将一个特殊的设备路径传递给 CreateFile 才能打开该打印机。

此设备路径 as I was able to find 是通过 SetupDiGetClassDevs -> SetupDiEnumDeviceInterfaces -> SetupDiGetDeviceInterfaceDetail -> DevicePath member 检索到的,如下所示:

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

一切都很好,但我输入的是人类可读的打印机名称,如Devices and Printers 所示。 SetupDi* 函数似乎没有使用它,它们只在 device instance IDs 上运行。所以现在的问题是如何从将传递给OpenPrinter的打印机名称中获取设备实例ID

不难看出,上面的GUID部分是GUID_DEVINTERFACE_USBPRINT,而\\?\usb是固定的,所以我真正感兴趣的只有vid_0a5f&pid_0027#46a072900549#。这个路径我可以很容易地在打印机属性对话框中手动查找:

转到设备和打印机
右键单击打印机
属性
切换到硬件选项卡
选择打印设备,例如 ZDesigner LP2844-Z
属性
切换到详细信息选项卡
从下拉列表中选择“父母”。

但我不知道如何以编程方式执行此操作,只要给出的唯一内容是在“设备和打印机”面板中看到的打印机名称。


附: 1:我没有兴趣用OpenPrinter打开打印机然后使用WritePrinter/ReadPrinter。已经完成,工作正常,但现在目标不同了。

附: 2:我可以用一种更简单的方法将可读的打印机名称转换为可以传递给CreateFile的名称

附: 3:This question,我已经发布了answer,这与我最终想要做的事情非常相关。

附: 4:反之亦然:如果可以从SP_DEVINFO_DATA结构中获得可读的名字,那也是答案,虽然不太方便。

【问题讨论】:

  • 我可能很笨,但你能不能不用USBPRINT 标识符?
  • @eggyal 不知道这些,但它们似乎是型号标识符而不是设备标识符(我们在一台机器上安装了几台相同的打印机)。此外,CreateFile 在出现 "USBPRINT\Zebra_TechnologiesZT9FD5" 时会失败。
  • 很公平 - 我在这里超出了我的深度......我想你可能想使用PnP Configuration Manager Functions,例如CM_Get_Device_ID(最坏的情况下你可以使用CM_Get_Device_ID_List和循环直到你找到你想要的打印机),但我们必须等待更了解 API 的人来提供更多帮助。
  • 我想应该只计算连接的打印机?
  • @STATUS_ACCESS_DENIED 是的,很明显。找到的路径立即传递给CreateFile

标签: winapi printing createfile


【解决方案1】:

下面是我终于想出来的。

请确认SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData 是受支持的路径,并且不只是在当前实现中恰好存在,未来可能会发生变化。

结构对齐有点问题,我发了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);
    }

}

用法:

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) 并且返回 false。我打电话给Marshal.GetLastWin32Error(),我的错误代码是259。我很难过。我的打印机已插入 USB,并显示在注册表中。
  • 上面的代码应该适用于即插即用安装的打印机(大多数消费者打印机)。在我的情况下,打印机不是通过 PnP 安装的,因此 PNPData 密钥不存在。还应该注意的是,不建议使用这样的硬链接注册表项查找,因为注册表组织可能会发生变化。但是,我还没有找到一个 API 函数来建立设备和打印机中的打印机名称与其实际设备之间的关系。
【解决方案2】:

试试这个(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

【讨论】:

    【解决方案3】:

    试试这个...如果有帮助请告诉我...

        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 类有助于处理上述代码,但不提供设备 ID。

    但是,如果我使用 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 打印机有什么作用?

    【讨论】:

    • 这是有希望的;但是,到目前为止,它没有帮助。它给出的Name 不是打印机的名称,而是驱动程序的名称。再说一次,我无法知道那是否是正确的打印机。如果我改为查询Win32_Printer,则PNPDeviceID 为空白,DeviceID 无用(与打印机名称相同)。
    • PortName 属性有帮助吗?
    • 不,PortName 没有帮助。它是USB002,而CreateFile 无法打开它。相反,如果端口是“LPT1:”,它会成功。如果你知道如何让CreateFile 打开类似USB002 的东西,那肯定是答案。
    • 你试过.. \\.\USB002 ??还记得如果打印机是共享的,那么你可以直接按名称打开打印机:“\\machine-name\printer-name”。
    • 是的,我试过\\.\USB002\\?\USB002。打印机未共享,也不会共享。
    【解决方案4】:

    使用来自 Microsoft 的 WinObj 获取特定设备名称。 http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx 。这将很快为您提供正确的设备名称,以便与 CreateFile 一起使用,以直接写入您的 USB 打印机,或直接写入带有老式并行端口输出的 USB 打印机适配器,用于自定义电路!

    要打开与特定打印机关联的端口,您可能需要使用ntcreatefile。使用EnumPrinters 函数返回一个printer_info_2 结构,其中包含每台打印机的端口名称。然后可以使用ntcreatefileCreateFile 的 NT 内部版本)打开此端口名称,此处解释为:http://msdn.microsoft.com/en-us/library/bb432380(v=vs.85).aspx

    为什么会这样? Windows NT 文件/设备名称中有三个命名空间级别,从EnumPrinters 检索的端口名称只能用ntcreatefile 打开,因为它只在NT 命名空间中。某些设备可能有一个等效的 win32 命名空间链接,以及将它们与打印机名称匹配的迂回方式,但这很困难,因为其他人在之前的答案中已经显示了这一点。

    查看WinObj 工具中的Global?? 文件夹,以显示您机器上的win32 命名空间和NT 命名空间之间的符号链接。老式的COM1COM2LPT1 等设备名称也只是简单的 Windows NT 命名空间符号链接。谷歌“win32 nt 命名空间”以获得更详细的解释。 (对不起,作为一个新用户,我只能发布2个超链接。)

    【讨论】:

      【解决方案5】:

      我不是真正的 C++ 人,但我并不认为尝试从名称生成设备 ID 是可行的方法。但是,你可以

      1. 使用EnumPrinters 并读取PRINTER_INFO_2 structure 以获取驱动程序名称,然后从注册表中读取驱动程序详细信息,如this example

      2. 通过可能从PRINTER INFO structures 中找出打印机详细信息并正确构造它,为自己生成名称。详情请见http://msdn.microsoft.com/en-us/library/windows/hardware/ff553356(v=vs.85).aspx

      编辑:您实际上可以从注册表中获取打印机的名称 + 设备实例 ID:

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

      【讨论】:

      • 这与 C++ 无关,我对任何语言都很满意(事实上,我在这个项目中使用了 C#),我只是选择了 C++ 作为开始使用 API 的最简单方法,并且结构而无需声明 PInvoke 属性等。驱动程序名称是无用的,因为它与我安装的相同打印机相同。如果您查看my own answer 这个问题,我已经在做与您链接的内容类似的事情,但我不确定是否支持以这种方式读取注册表(而不是依赖于实现)。跨度>
      • 嗯,如果你在做C#,那么API调用需要什么?
      • 您还要如何获得这些信息?先把redistry设备ID翻译成父设备ID,再把父设备ID翻译成usbprint接口路径? .NET 框架似乎没有提供方法。
      • 我实际上是说当有WMI 时,您甚至不需要使用 CreateFile 和 OpenPrinter api 调用来获取打印机信息。
      • 我不需要打印机信息本身。我只需要打印机信息即可使用CreateFile 打开该打印机。使用 .NET 打印子系统是不可能的,因为它通过后台处理程序以不同的方式打开打印机。 WMI 没有提供所需的信息,因为我在对 PrashantGupta 的回答的评论中have noted
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-26
      • 2011-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多