如何唯一可靠地识别给定服务器上的 Windows 打印队列,包括跨打印队列重命名?
我想处理如下情况:
- Jdoe 创建打印机 A
- 我的程序在某个时候收集了打印机 A 的信息
- Jdoe 将打印机 A 重命名为打印机 AA
- 我的程序在某个时候再次收集有关打印机 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 似乎都通过名称来识别队列,所以我没有发现任何有用的东西。
最佳答案
这是我在旧项目中使用的一些代码,用于确定打印机名称和驱动程序名称(以及打印机的其他一些详细信息)。我不确定匹配驱动程序名称是否对您足够。我知道我已经在 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;
}
}
编辑:为了它的值(value),我只是逐步浏览这段代码,看看我是否能找到任何独特的东西......这些值中的大多数对我来说都是 null 或空白,但这里有一些东西:
Printer Name: XPS Card Printer - NEW
Driver Name: XPS Card Printer
Port Name: USB DXP01 Port
Attributes: 2624
DevMode: 3562584
不确定这是否有帮助...
关于c# - 即使队列已重命名,如何在 Windows 上唯一标识打印队列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15960424/