C# 即使队列已重命名,如何在Windows上唯一标识打印队列?

C# 即使队列已重命名,如何在Windows上唯一标识打印队列?,c#,windows,printing,C#,Windows,Printing,如何唯一可靠地标识给定服务器上的Windows打印队列,包括跨打印队列重命名 我想处理如下情况: Jdoe创建了一个打印机 我的程序在某个时刻收集打印机A上的信息 Jdoe将打印机A重命名为打印机AA 我的程序在某个时刻再次收集AA打印机上的信息 如何判断打印机A和打印机AA是同一台打印机(名称已更改) 我想在支持Windows XP/2003及更高版本的C#中实现这一点 我尝试过的事情: 在Windows 8/Server 2012上,我似乎可以通过WMI查看CIM_LogicalDevice

如何唯一可靠地标识给定服务器上的Windows打印队列,包括跨打印队列重命名

我想处理如下情况:

  • Jdoe创建了一个打印机
  • 我的程序在某个时刻收集打印机A上的信息
  • Jdoe将打印机A重命名为打印机AA
  • 我的程序在某个时刻再次收集AA打印机上的信息
  • 如何判断打印机A和打印机AA是同一台打印机(名称已更改)

    我想在支持Windows XP/2003及更高版本的C#中实现这一点

    我尝试过的事情:

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

    我还研究了Win32_Pentity类(不包含Windows 8之前的打印机)和Win32_Printer类(不包含名称以外的任何ID)

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


    打印后台处理程序API和GDI打印API似乎都是通过名称来标识队列的,因此我在那里没有找到任何有用的东西。

    以下是我在一个旧项目中使用的一些代码,用于确定打印机名称和驱动程序名称(以及打印机的一些其他详细信息)。我不确定匹配的司机姓名对你来说是否足够。我知道我已经在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;
    }
    
    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;
        }
    }
    
    编辑:为了它的价值,我只是通过这段代码看看我是否能找到任何独特的'看。。。对于我来说,这些值中的大多数为null或空白,但有几点:

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

    不确定这是否有帮助…

    您需要使用的是setup类中的以下函数

    下面是我编写的一个粗略的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);
    }
    
    #包括
    #包括
    #包括
    #pragma注释(lib,“setupapi.lib”)
    无效PrinterID(参考GUID类GUID)
    {
    HDEVINFO HDEVINFO=SetupDiGetClassDevs(&ClassGuid,NULL,NULL,DIGCF_PRESENT);
    if(hDevInfo==无效的句柄值)
    {
    wprintf(L“无法获取设备:%d\n”,GetLastError());
    返回;
    }
    int-idx=0;
    DWORD errorVal=错误\成功;
    while(true)
    {
    SP_DEVINFO_DATA DEVINFO={};
    WCHAR regProp[512];
    devinfo.cbSize=sizeof(devinfo数据);
    if(!SetupDiEnumDeviceInfo(hDevInfo、idx和devinfo数据))
    {
    errorVal=GetLastError();
    打破
    }
    如果(!SetupDiGetDeviceRegistryProperty(
    hDevInfo,
    &DevInfo数据,
    SPDRP_FRIENDLYNAME,
    无效的
    (PBYTE)regProp,
    sizeof(regProp),
    空)
    {
    errorVal=GetLastError();
    打破
    }
    wprintf(L“友好名称=%s\n”,regProp);
    如果(!SetupDiGetDeviceRegistryProperty(
    hDevInfo,
    &DevInfo数据,
    SPDRP_HARDWAREID,
    无效的
    (PBYTE)regProp,
    sizeof(regProp),
    空)
    {
    errorVal=GetLastError();
    打破
    }
    //hardwareId是reg_multi_sz
    //打印此设备的所有硬件ID
    PWCHAR pId=regProp;
    做
    {
    wprintf(L“HardwareId=%s\n”,pId);
    pId+=wcslen(pId)+1;
    }而(pId[0]!=0);
    //指向下一个设备
    idx++;
    }
    如果(errorVal!=错误\u无\u更多项目)
    {
    printf(“错误:%d\n”,errorVal);
    }
    SetupDiDestroyDeviceInfo列表(hDevInfo);
    }
    int main()
    {
    //{4d36e979-e325-11ce-bfc1-08002be10318}
    静态常量GUID PrinterClass=
    {0x4d36e979,0xe325,0x11ce,{0xbf,0xc1,0x08,0x00,0x2b,0xe1,0x03,0x18};
    PrinterId(PrinterClass);
    //L“{1ed2bbf9-11f0-4084-b21f-ad83a8e6dcdc}”
    静态常量GUID打印队列=
    {0x1ed2bbf9、0x11f0、0x4084、{0xb2、0x1f、0xad、0x83、0xa8、0xe6、0xdc、0xdc};
    PrinterId(PrinterQueue);
    }
    
    发生这种情况时,您是想接收某种类型的事件通知,还是只是查询它并获取最新的队列?另外,请原谅我的无知,但当您说“打印队列名称”时,这与“设备和打印机”下的打印设备名称相同,对吗?@AdamPlocher不需要通知。我希望能够定期查询并识别打印机AA实际上是名称已更改的打印机A,并且能够识别打印到打印机A的文档1已发送到与发送到打印机AA的文档2相同的打印机(重命名后)。另外,要回答你第二次评论中的问题,是的,我们在谈论同一件事。哦,明白了。我有一些列出打印机名称和驱动程序名称的代码。匹配驱动程序名称(不应更改)是否足以确定它们是相同的设备?如果你愿意的话,我可以把它贴在回复中…谢谢你的回复。我知道你要做什么,但我认为很多情况下,它无法可靠地处理。办公室中有多台打印机使用同一个驱动程序的情况并不少见,我认为它不会处理像6台使用同一驱动程序的打印机同时被重命名(例如,为了反映新的命名方案),或者一台打印机被删除后又添加了一台新打印机的情况