Changes using Winspool.drv in Windows 7 64 bit from Windows XP 32 bit

5.9k views Asked by At

I have some C# code (VS2010;fx2) which is used to carry out printer functions. This code works fine in Windows XP environment. Changing to Windows 7, it no longer works correctly.

The first different behaviour is that the GetPrinterNames() method now only returns local printers. As you can see, the flags are set to include NETWORK printers also. I've tried different flags, but with no success.

Is there a different library I should be referencing in Windows 7 / 64 bit version?

Printer helper class with code shown below:

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
    }

EDIT: Code edited to reduce size (areas of less interest removed).

2

There are 2 answers

3
Dan OConnell On

According to msdn notes for EnumPrinters, your code only works if the 3rd paramater is 1

from EnumPrinters....

PRINTER_ENUM_NETWORK The function enumerates network printers in the computer's domain. This value is valid only if Level is 1.

1
Lakerfield On

On 64 bit windows machine is the exception an OverflowException (Arithmetic operation resulted in an overflow.) Caused by converting a pointer in a 32 bits integer instead of a 64 bits integer.

int offset = pAddr.ToInt32();

Fix it with

long offset = pAddr.ToInt64();

The complete working code, checked on 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);