【问题标题】:How can I uniquely identify a print queue on Windows even if the queue is renamed?即使队列已重命名,如何在 Windows 上唯一标识打印队列?
【发布时间】:2013-04-11 23:09:46
【问题描述】:

我如何才能唯一且可靠地识别给定服务器上的 Windows 打印队列,包括跨打印队列重命名?

我想处理如下情况:

  1. Jdoe 创建打印机 A
  2. 我的程序有时会收集有关打印机 A 的信息
  3. Jdoe 将打印机 A 重命名为打印机 AA
  4. 我的程序有时会再次收集有关打印机 AA 的信息

如何判断打印机 A 和打印机 AA 是同一台打印机(更改了名称)?

我想在支持 Windows XP/2003 及更高版本的 C# 中执行此操作。

我尝试过的事情:

在 Windows 8/Server 2012 上,我似乎可以通过 WMI 查看 CIM_LogicalDevice->DeviceID 来执行此操作,这似乎在重命名时保持一致,但在早期版本的操作系统中,此属性仅包含队列名称并在队列已重命名。

我还查看了 Win32_PnPEntity 类(不包含 Windows 8 之前的打印机)和 Win32_Printer 类(不包含名称以外的任何类型的 ID)。

在注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers[打印机名称] 有一个包含 GUID 的 QueueInstanceId 字段,但仅在 Windows 8/Server 2012 中。它在早期的操作系统中不存在。

打印后台处理程序 API 和 GDI 打印 API 似乎都通过名称来识别队列,所以我没有发现任何有用的东西。

【问题讨论】:

  • 发生这种情况时您是想接收某种事件通知,还是只是查询它并获取最新的队列?
  • 另外,请原谅我的无知,但是当您说“打印队列名称”时,这与设备和打印机下的打印设备名称相同,对吧?
  • @AdamPlocher 不需要通知。我希望能够定期查询并识别打印机 AA 实际上是更改名称的打印机 A,并且能够执行诸如识别打印到打印机 A 的文档 1 被发送到与发送的文档 2 相同的打印机到打印机 AA(重命名后)。另外,要回答您第二条评论中的问题,是的,我们在谈论同一件事。
  • 哦,明白了。我有一些列出打印机名称和驱动程序名称的代码。匹配驱动程序名称(不应更改)是否足以确定它们是相同的设备?如果您愿意,我可以将其发布在答案中...

标签: c# windows printing


【解决方案1】:

这是我在旧项目中使用的一些代码,用于确定打印机名称和驱动程序名称(以及打印机的其他一些详细信息)。我不确定匹配驱动程序名称是否足以满足您的需求。我知道我已经在 Win7 上测试过了,而且我有 80% 的把握在 XP 上测试过(这是不久前的事了):

返回系统上打印机名称/驱动程序名称的辅助方法:

public static List<PrinterDriverItem> GetCardPrinterList()
{
    List<PrinterDriverItem> returnValue = new List<PrinterDriverItem>();

    uint cbNeeded = 0;
    uint cReturned = 0;
    bool ret = Win32PrintApi.EnumPrinters(Win32PrintApi.PRINTER_ENUM_LOCAL, null, 2, IntPtr.Zero, 0, ref cbNeeded, ref cReturned);

    IntPtr addr = Marshal.AllocHGlobal((int)cbNeeded);
    ret = Win32PrintApi.EnumPrinters(Win32PrintApi.PRINTER_ENUM_LOCAL, null, 2, addr, cbNeeded, ref cbNeeded, ref cReturned);

    if (ret)
    {
        Win32PrintApi.PRINTER_INFO_2[] printerInfo = new Win32PrintApi.PRINTER_INFO_2[cReturned];
        int offset = addr.ToInt32();

        for (int i = 0; i < cReturned; i++)
        {
            printerInfo[i].pServerName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pPrinterName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pShareName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pPortName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pDriverName = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pComment = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pLocation = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pDevMode = Marshal.ReadIntPtr(new IntPtr(offset));
            offset += 4;
            printerInfo[i].pSepFile = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pPrintProcessor = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pDatatype = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pParameters = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(new IntPtr(offset)));
            offset += 4;
            printerInfo[i].pSecurityDescriptor = Marshal.ReadIntPtr(new IntPtr(offset));
            offset += 4;
            printerInfo[i].Attributes = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].Priority = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].DefaultPriority = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].StartTime = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].UntilTime = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].Status = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].cJobs = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;
            printerInfo[i].AveragePPM = (uint)Marshal.ReadInt32(new IntPtr(offset));
            offset += 4;

            returnValue.Add(new PrinterDriverItem() { PrinterName = printerInfo[i].pPrinterName, DriverName = printerInfo[i].pDriverName });
        }

    }

    Marshal.FreeHGlobal(addr);

    return returnValue;
}

Win32PrintApi 类:

public class Win32PrintApi
{
    public const int PRINTER_ENUM_LOCAL = 0x00000002;

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool EnumPrinters(int Flags, string Name, uint Level, IntPtr pPrinterEnum, uint cbBuf, ref uint pcbNeeded, ref uint pcReturned);

    [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_INFO_2
    {
        public string pServerName;
        public string pPrinterName;
        public string pShareName;
        public string pPortName;
        public string pDriverName;
        public string pComment;
        public string pLocation;
        public IntPtr pDevMode;
        public string pSepFile;
        public string pPrintProcessor;
        public string pDatatype;
        public string pParameters;
        public IntPtr pSecurityDescriptor;
        public uint Attributes;
        public uint Priority;
        public uint DefaultPriority;
        public uint StartTime;
        public uint UntilTime;
        public uint Status;
        public uint cJobs;
        public uint AveragePPM;
    }
}

编辑:对于它的价值,我只是逐步浏览了这段代码,看看我是否能找到任何独特的外观......这些值中的大多数对我来说都是空或空白,但这里有一些东西:

Printer Name: XPS Card Printer - NEW 
Driver Name: XPS Card Printer
Port Name: USB DXP01 Port
Attributes: 2624
DevMode: 3562584

不确定这是否会有所帮助...

【讨论】:

  • 感谢您的回复。我可以看到您将要处理的问题,但是我认为在很多情况下它无法可靠地处理。在办公室中使用同一个驱动程序的多台打印机并不少见,我认为它不会处理像使用同一个驱动程序的 6 台打印机同时重命名的情况(例如,以反映新的命名方案),或者删除了一台打印机,然后使用相同的驱动程序添加了一台新打印机。您也许可以通过查看端口使其更可靠,但这正是我希望避免的那种兔子洞。 +1 虽然。
  • 啊,是的,没错...删除和读取将显示为同一个驱动程序,并且肯定有多个设备具有相同的驱动程序名称...嗯...
  • 我想知道 PRINTER_INFO_2 中的其他值是什么。我只使用了 PrinterName 和 PrinterDriver。 Pinvoke.net 有结构,但没有真正的文档。如果您可以跟踪在添加新打印机时创建的某种 GUID,那就太好了。 pinvoke.net/default.aspx/Structures/PRINTER_INFO_2.html
  • 对我的帖子做了一个小更新(在底部)。不确定它是否会对您有所帮助,但可能值得一试......
  • 根据msdn.microsoft.com/en-us/library/windows/hardware/… 上的“重复安装检测”,Windows PnP 管理器使用驱动程序和端口名称来确定以前的安装。因此,假设您可以使用相同的策略跟踪打印机可能是合理的。
【解决方案2】:

您需要使用的是安装类中的以下功能。

这是我编写的粗略示例 Cpp 代码。无论打印机是本地/网络/重定向的rdp打印机,即使名称不同,硬件ID也始终相同。

#include <Windows.h>
#include <stdio.h>
#include <SetupAPI.h>
#pragma comment(lib, "setupapi.lib")

void PrintPrinterIds(REFGUID ClassGuid)
{
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&ClassGuid, NULL, NULL, DIGCF_PRESENT);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        wprintf(L"Cannot get devices : %d\n", GetLastError());
        return;
    }

    int idx = 0;
    DWORD errorVal = ERROR_SUCCESS;
    while (true)
    {
        SP_DEVINFO_DATA devInfoData = {};
        WCHAR regProp[512];
        devInfoData.cbSize = sizeof(devInfoData);

        if (!SetupDiEnumDeviceInfo(hDevInfo, idx, &devInfoData))
        {
            errorVal = GetLastError();
            break;
        }

        if (!SetupDiGetDeviceRegistryProperty(
            hDevInfo,
            &devInfoData,
            SPDRP_FRIENDLYNAME,
            NULL,
            (PBYTE)regProp,
            sizeof(regProp),
            NULL))
        {
            errorVal = GetLastError();
            break;
        }

        wprintf(L"Friendly name = %s\n", regProp);

        if (!SetupDiGetDeviceRegistryProperty(
            hDevInfo,
            &devInfoData,
            SPDRP_HARDWAREID,
            NULL,
            (PBYTE)regProp,
            sizeof(regProp),
            NULL))
        {
            errorVal = GetLastError();
            break;
        }

        // hardwareId is reg_multi_sz
        // Print all of the hardware ids for this device
        PWCHAR pId = regProp;
        do
        {
            wprintf(L"HardwareId = %s\n", pId);
            pId += wcslen(pId) + 1;
        } while (pId[0] != 0);

        // Point to next device
        idx++;
    }

    if (errorVal != ERROR_NO_MORE_ITEMS)
    {
        printf("Error : %d\n", errorVal);
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
}

int main()
{
    // {4d36e979-e325-11ce-bfc1-08002be10318}
    static const GUID PrinterClass =
    { 0x4d36e979, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
    PrintPrinterIds(PrinterClass);

    // L"{1ed2bbf9-11f0-4084-b21f-ad83a8e6dcdc}"
    static const GUID PrinterQueue =
    { 0x1ed2bbf9, 0x11f0, 0x4084, { 0xb2, 0x1f, 0xad, 0x83, 0xa8, 0xe6, 0xdc, 0xdc } };
    PrintPrinterIds(PrinterQueue);
}

【讨论】:

  • 谢谢,这是我们不知道的事情,看起来很有希望。
猜你喜欢
  • 1970-01-01
  • 2014-04-12
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多