Windows 7 64位中使用Winspool.drv的变化与Windows XP 32位相比

5
我有一些C#代码(VS2010;fx2),用于执行打印机功能。这段代码在Windows XP环境下运行良好。但是在切换到Windows 7后,它不再正确工作。
第一个不同的行为是GetPrinterNames()方法现在仅返回本地打印机。如您所见,标志设置为包括网络打印机。我尝试了不同的标志,但没有成功。
在Windows 7 / 64位版本中,我应该引用不同的库吗?
以下是打印机助手类和代码:
internal class Printers
{

    ...

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool EnumPrintersW(Int32 flags, [MarshalAs(UnmanagedType.LPTStr)] string printerName,
         Int32 level, IntPtr buffer, Int32 bufferSize, out Int32 requiredBufferSize,
         out Int32 numPrintersReturned);

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

    ...

    ...

    public static string[] GetPrinterNames()
    {
        List<string> returnVal = new List<string>();
        foreach(PRINTER_INFO_2 info in enumPrinters(PrinterEnumFlags.PRINTER_ENUM_LOCAL | PrinterEnumFlags.PRINTER_ENUM_NETWORK))
        {
            returnVal.Add(info.pPrinterName);
        }
        return returnVal.ToArray();
    }

...

    private static PRINTER_INFO_2[] enumPrinters(PrinterEnumFlags Flags)
    {
        uint cbNeeded = 0;
        uint cReturned = 0;
        if (EnumPrinters(Flags, null, 2, IntPtr.Zero, 0, ref cbNeeded, ref cReturned))
        {
            return null;
        }
        int lastWin32Error = Marshal.GetLastWin32Error();
        if (lastWin32Error == ERROR_INSUFFICIENT_BUFFER)
        {
            IntPtr pAddr = Marshal.AllocHGlobal((int)cbNeeded);
            if (EnumPrinters(Flags, null, 2, pAddr, cbNeeded, ref cbNeeded, ref cReturned))
            {
                PRINTER_INFO_2[] printerInfo2 = new PRINTER_INFO_2[cReturned];
                int offset = pAddr.ToInt32();
                Type type = typeof(PRINTER_INFO_2);
                int increment = Marshal.SizeOf(type);
                for (int i = 0; i < cReturned; i++)
                {
                    printerInfo2[i] = (PRINTER_INFO_2)Marshal.PtrToStructure(new IntPtr(offset), type);
                    offset += increment;
                }
                Marshal.FreeHGlobal(pAddr);
                return printerInfo2;
            }
            lastWin32Error = Marshal.GetLastWin32Error();
        }
        throw new System.ComponentModel.Win32Exception(lastWin32Error);
    }

    ...

    [FlagsAttribute]
    enum PrinterEnumFlags
    {
        PRINTER_ENUM_DEFAULT = 0x00000001,
        PRINTER_ENUM_LOCAL = 0x00000002,
        PRINTER_ENUM_CONNECTIONS = 0x00000004,
        PRINTER_ENUM_FAVORITE = 0x00000004,
        PRINTER_ENUM_NAME = 0x00000008,
        PRINTER_ENUM_REMOTE = 0x00000010,
        PRINTER_ENUM_SHARED = 0x00000020,
        PRINTER_ENUM_NETWORK = 0x00000040,
        PRINTER_ENUM_EXPAND = 0x00004000,
        PRINTER_ENUM_CONTAINER = 0x00008000,
        PRINTER_ENUM_ICONMASK = 0x00ff0000,
        PRINTER_ENUM_ICON1 = 0x00010000,
        PRINTER_ENUM_ICON2 = 0x00020000,
        PRINTER_ENUM_ICON3 = 0x00040000,
        PRINTER_ENUM_ICON4 = 0x00080000,
        PRINTER_ENUM_ICON5 = 0x00100000,
        PRINTER_ENUM_ICON6 = 0x00200000,
        PRINTER_ENUM_ICON7 = 0x00400000,
        PRINTER_ENUM_ICON8 = 0x00800000,
        PRINTER_ENUM_HIDE = 0x01000000
    }

编辑:对代码进行了编辑以减小大小(移除了较不重要的部分)。


2
你觉得能不能把范围缩小一点?你想让我们查看的代码太多了。 - John Saunders
嗨John。我已经删除了我认为是无关的代码部分。感兴趣的方法是GetPrinterNames() - 它依赖于enumPrinters()。其他函数也不起作用 - 但我怀疑它们都是同样的原因。提前致谢。 - Jayden
2个回答

2
在64位Windows机器上,异常是由于将指针转换为32位整数而不是64位整数引起的OverflowException(算术运算导致溢出)。
int offset = pAddr.ToInt32();

使用以下方法进行修复:

long offset = pAddr.ToInt64();

完整的工作代码,在 Windows 10 x64 上进行了检查。

public static string[] GetPrinterNames()
{
    var results = new List<string>();
    foreach (PRINTER_INFO_2 info in enumPrinters(PrinterEnumFlags.PRINTER_ENUM_LOCAL | PrinterEnumFlags.PRINTER_ENUM_NETWORK))
        results.Add(info.pPrinterName);
    return results.ToArray();
}

private static PRINTER_INFO_2[] enumPrinters(PrinterEnumFlags Flags)
{
    uint cbNeeded = 0;
    uint cReturned = 0;
    if (EnumPrinters(Flags, null, 2, IntPtr.Zero, 0, ref cbNeeded, ref cReturned))
    {
        return null;
    }
    int lastWin32Error = Marshal.GetLastWin32Error();
    if (lastWin32Error == ERROR_INSUFFICIENT_BUFFER)
    {
        IntPtr pAddr = Marshal.AllocHGlobal((int)cbNeeded);
        if (EnumPrinters(Flags, null, 2, pAddr, cbNeeded, ref cbNeeded, ref cReturned))
        {
            PRINTER_INFO_2[] printerInfo2 = new PRINTER_INFO_2[cReturned];
            long offset = pAddr.ToInt64();
            Type type = typeof(PRINTER_INFO_2);
            int increment = Marshal.SizeOf(type);
            for (int i = 0; i < cReturned; i++)
            {
                printerInfo2[i] = (PRINTER_INFO_2)Marshal.PtrToStructure(new IntPtr(offset), type);
                offset += increment;
            }
            Marshal.FreeHGlobal(pAddr);
            return printerInfo2;
        }
        lastWin32Error = Marshal.GetLastWin32Error();
    }
    throw new System.ComponentModel.Win32Exception(lastWin32Error);
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct PRINTER_INFO_2
{
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pServerName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pPrinterName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pShareName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pPortName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pDriverName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pComment;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pLocation;
    public IntPtr pDevMode;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pSepFile;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pPrintProcessor;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pDatatype;
    [MarshalAs(UnmanagedType.LPTStr)]
    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;
}

const int ERROR_INSUFFICIENT_BUFFER = 122;

[FlagsAttribute]
enum PrinterEnumFlags
{
    PRINTER_ENUM_DEFAULT = 0x00000001,
    PRINTER_ENUM_LOCAL = 0x00000002,
    PRINTER_ENUM_CONNECTIONS = 0x00000004,
    PRINTER_ENUM_FAVORITE = 0x00000004,
    PRINTER_ENUM_NAME = 0x00000008,
    PRINTER_ENUM_REMOTE = 0x00000010,
    PRINTER_ENUM_SHARED = 0x00000020,
    PRINTER_ENUM_NETWORK = 0x00000040,
    PRINTER_ENUM_EXPAND = 0x00004000,
    PRINTER_ENUM_CONTAINER = 0x00008000,
    PRINTER_ENUM_ICONMASK = 0x00ff0000,
    PRINTER_ENUM_ICON1 = 0x00010000,
    PRINTER_ENUM_ICON2 = 0x00020000,
    PRINTER_ENUM_ICON3 = 0x00040000,
    PRINTER_ENUM_ICON4 = 0x00080000,
    PRINTER_ENUM_ICON5 = 0x00100000,
    PRINTER_ENUM_ICON6 = 0x00200000,
    PRINTER_ENUM_ICON7 = 0x00400000,
    PRINTER_ENUM_ICON8 = 0x00800000,
    PRINTER_ENUM_HIDE = 0x01000000
}

[DllImport("winspool.drv", SetLastError = true)]
static extern bool EnumPrintersW(Int32 flags, [MarshalAs(UnmanagedType.LPTStr)] string printerName,
                 Int32 level, IntPtr buffer, Int32 bufferSize, out Int32 requiredBufferSize,
                 out Int32 numPrintersReturned);

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

哇,感谢您对五年前的问题进行跟进。不幸的是,我不再拥有适合测试的项目或操作系统的访问权限。 - Jayden

1
根据 EnumPrinters 的 MSDN 注释,你的代码只有在第三个参数为 1 的情况下才能工作。
从 EnumPrinters....
PRINTER_ENUM_NETWORK 该函数枚举计算机域中的网络打印机。仅当级别为 1 时,此值才有效。

将该值更改为1似乎只会引发异常。在Windows XP下运行此代码(参数为2)完全正常,如预期的那样。只有在64位Windows 7上运行时才会出现问题。 - Jayden
它会引发什么异常? - Dan OConnell
如何设置有效的一级? - SwR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接