C# 即使队列已重命名,如何在Windows上唯一标识打印队列?
如何唯一可靠地标识给定服务器上的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
打印后台处理程序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类中的以下函数
#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台使用同一驱动程序的打印机同时被重命名(例如,为了反映新的命名方案),或者一台打印机被删除后又添加了一台新打印机的情况