Getting list of printer paper names / sizes: calling DeviceCapabilities from C#

B

Bruce Wood

I'm having a devil of a time calling DeviceCapabilities() in order to
get the list of paper names / codes / sizes for a printer. Here is my
code and the input it produces:

[DllImport("winspool.drv", SetLastError=true)]
static extern Int32 DeviceCapabilities(
[MarshalAs(UnmanagedType.LPTStr)] string device,
[MarshalAs(UnmanagedType.LPTStr)] string port,
Int16 capability,
out IntPtr outputBuffer,
[MarshalAs(UnmanagedType.LPStruct)] DEVMODE deviceMode);

[DllImport("winspool.drv", SetLastError=true)]
static extern Int32 DeviceCapabilities(
[MarshalAs(UnmanagedType.LPTStr)] string device,
[MarshalAs(UnmanagedType.LPTStr)] string port,
Int16 capability,
out IntPtr outputBuffer,
IntPtr deviceMode);

[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);

public static PaperInfo[] GetDefinedPapers(string printerName)
{
PRINTER_INFO_5 info5;
int requiredSize;
int numPrinters;
bool foundPrinter = EnumPrintersW(PRINTER_ENUM_LOCAL |
PRINTER_ENUM_CONNECTIONS, printerName, 5, (IntPtr)null, 0, out
requiredSize, out numPrinters);
Console.WriteLine("Required size is: {0}", requiredSize);
int info5Size = requiredSize;
IntPtr info5Ptr = Marshal.AllocHGlobal(info5Size);
try
{
foundPrinter = EnumPrintersW(PRINTER_ENUM_LOCAL |
PRINTER_ENUM_CONNECTIONS, printerName, 5, info5Ptr, info5Size, out
requiredSize, out numPrinters);
Console.WriteLine("Size: {0}, required size: {1}, num printers:
{2}", info5Size, requiredSize, numPrinters);
string port = null;
for (int i = 0; i < numPrinters; i++)
{
info5 = (PRINTER_INFO_5)Marshal.PtrToStructure((IntPtr)((i *
Marshal.SizeOf(typeof(PRINTER_INFO_5))) + (int)info5Ptr),
typeof(PRINTER_INFO_5));
if (info5.PrinterName == printerName)
{
port = info5.PortName;
}
Console.WriteLine("Printer: '{0}', Port:'{1}'", info5.PrinterName,
info5.PortName);
}
IntPtr buffer;
int numNames = DeviceCapabilities(printerName, port, DC_PAPERNAMES,
out buffer, (IntPtr)null);
if (numNames < 0)
{
int errorCode = GetLastError();
IntPtr bufferPtr;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, (IntPtr)null, errorCode,
GetUserDefaultLangID(), out bufferPtr, 0, (IntPtr)null);
string errorMessage = Marshal.PtrToStringUni(bufferPtr);
Console.WriteLine("Number of names = {2}: {0} / {1}", errorCode,
errorMessage, numNames);
return new PaperInfo[0];
}
string[] names = new string[numNames];
for (int i = 0; i < numNames; i++)
{
names = Marshal.PtrToStringAuto((IntPtr)((i * 64) +
(int)buffer), 64);
}
int numPapers = DeviceCapabilities(printerName, port, DC_PAPERS,
out buffer, (IntPtr)null);
if (numPapers < 0)
{
Console.WriteLine("No papers");
return new PaperInfo[0];
}
short[] kinds = new short[numPapers];
for (int i = 0; i < numPapers; i++)
{
kinds = Marshal.ReadInt16(buffer, i * 16);
}
int numSizes = DeviceCapabilities(printerName, port, DC_PAPERSIZE,
out buffer, (IntPtr)null);
if (numSizes < 0)
{
Console.WriteLine("No sizes");
return new PaperInfo[0];
}
int[] widths = new int[numSizes];
int[] heights = new int[numSizes];
for (int i = 0; i < numSizes; i++)
{
widths = Marshal.ReadInt32(buffer, i * 64);
heights = Marshal.ReadInt32(buffer, i * 64 + 32);
}

int finalSize = Math.Min(Math.Min(numNames, numPapers), numSizes);
PaperInfo[] result = new PaperInfo[finalSize];
for (int i = 0; i < finalSize; i++)
{
result = new PaperInfo(names, kinds, widths,
heights);
}
return result;
}
finally
{
Marshal.FreeHGlobal(info5Ptr);
}
}

The output from this method is:

Required size is: 444
Size: 444, required size: 444, num printers: 4
Printer: 'Zebra IS', Port:'IP_xxx.xxx.xxx.xxx'
Printer: 'Zebra 105SL (200dpi)', Port:'IP_yyy.yyy.yyy.yyy'
Printer: 'Microsoft Office Live Meeting Document Writer',
Port:'Microsoft Office Live Meeting Document Writer Port:'
Printer: 'Main IS', Port:'zzz.zzz.zzz.zzz'
Number of names = -1: 126 / The specified module could not be found.

Does anyone know why the call to DeviceCapabilities() fails with a
LastError indicating "The specified module could not be found"? Do I
have to load the device driver first, or something? The printer and
port name I am passing to DeviceCapabilities is the correct one.
 
G

Guest

Hi Bruce

Have been successful in fixing the problem
I am also having the same problem with devicecapabilities.
Can you let me know what you found.

thanks.
Iyyengar.

Bruce Wood said:
I'm having a devil of a time calling DeviceCapabilities() in order to
get the list of paper names / codes / sizes for a printer. Here is my
code and the input it produces:

[DllImport("winspool.drv", SetLastError=true)]
static extern Int32 DeviceCapabilities(
[MarshalAs(UnmanagedType.LPTStr)] string device,
[MarshalAs(UnmanagedType.LPTStr)] string port,
Int16 capability,
out IntPtr outputBuffer,
[MarshalAs(UnmanagedType.LPStruct)] DEVMODE deviceMode);

[DllImport("winspool.drv", SetLastError=true)]
static extern Int32 DeviceCapabilities(
[MarshalAs(UnmanagedType.LPTStr)] string device,
[MarshalAs(UnmanagedType.LPTStr)] string port,
Int16 capability,
out IntPtr outputBuffer,
IntPtr deviceMode);

[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);

public static PaperInfo[] GetDefinedPapers(string printerName)
{
PRINTER_INFO_5 info5;
int requiredSize;
int numPrinters;
bool foundPrinter = EnumPrintersW(PRINTER_ENUM_LOCAL |
PRINTER_ENUM_CONNECTIONS, printerName, 5, (IntPtr)null, 0, out
requiredSize, out numPrinters);
Console.WriteLine("Required size is: {0}", requiredSize);
int info5Size = requiredSize;
IntPtr info5Ptr = Marshal.AllocHGlobal(info5Size);
try
{
foundPrinter = EnumPrintersW(PRINTER_ENUM_LOCAL |
PRINTER_ENUM_CONNECTIONS, printerName, 5, info5Ptr, info5Size, out
requiredSize, out numPrinters);
Console.WriteLine("Size: {0}, required size: {1}, num printers:
{2}", info5Size, requiredSize, numPrinters);
string port = null;
for (int i = 0; i < numPrinters; i++)
{
info5 = (PRINTER_INFO_5)Marshal.PtrToStructure((IntPtr)((i *
Marshal.SizeOf(typeof(PRINTER_INFO_5))) + (int)info5Ptr),
typeof(PRINTER_INFO_5));
if (info5.PrinterName == printerName)
{
port = info5.PortName;
}
Console.WriteLine("Printer: '{0}', Port:'{1}'", info5.PrinterName,
info5.PortName);
}
IntPtr buffer;
int numNames = DeviceCapabilities(printerName, port, DC_PAPERNAMES,
out buffer, (IntPtr)null);
if (numNames < 0)
{
int errorCode = GetLastError();
IntPtr bufferPtr;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, (IntPtr)null, errorCode,
GetUserDefaultLangID(), out bufferPtr, 0, (IntPtr)null);
string errorMessage = Marshal.PtrToStringUni(bufferPtr);
Console.WriteLine("Number of names = {2}: {0} / {1}", errorCode,
errorMessage, numNames);
return new PaperInfo[0];
}
string[] names = new string[numNames];
for (int i = 0; i < numNames; i++)
{
names = Marshal.PtrToStringAuto((IntPtr)((i * 64) +
(int)buffer), 64);
}
int numPapers = DeviceCapabilities(printerName, port, DC_PAPERS,
out buffer, (IntPtr)null);
if (numPapers < 0)
{
Console.WriteLine("No papers");
return new PaperInfo[0];
}
short[] kinds = new short[numPapers];
for (int i = 0; i < numPapers; i++)
{
kinds = Marshal.ReadInt16(buffer, i * 16);
}
int numSizes = DeviceCapabilities(printerName, port, DC_PAPERSIZE,
out buffer, (IntPtr)null);
if (numSizes < 0)
{
Console.WriteLine("No sizes");
return new PaperInfo[0];
}
int[] widths = new int[numSizes];
int[] heights = new int[numSizes];
for (int i = 0; i < numSizes; i++)
{
widths = Marshal.ReadInt32(buffer, i * 64);
heights = Marshal.ReadInt32(buffer, i * 64 + 32);
}

int finalSize = Math.Min(Math.Min(numNames, numPapers), numSizes);
PaperInfo[] result = new PaperInfo[finalSize];
for (int i = 0; i < finalSize; i++)
{
result = new PaperInfo(names, kinds, widths,
heights);
}
return result;
}
finally
{
Marshal.FreeHGlobal(info5Ptr);
}
}

The output from this method is:

Required size is: 444
Size: 444, required size: 444, num printers: 4
Printer: 'Zebra IS', Port:'IP_xxx.xxx.xxx.xxx'
Printer: 'Zebra 105SL (200dpi)', Port:'IP_yyy.yyy.yyy.yyy'
Printer: 'Microsoft Office Live Meeting Document Writer',
Port:'Microsoft Office Live Meeting Document Writer Port:'
Printer: 'Main IS', Port:'zzz.zzz.zzz.zzz'
Number of names = -1: 126 / The specified module could not be found.

Does anyone know why the call to DeviceCapabilities() fails with a
LastError indicating "The specified module could not be found"? Do I
have to load the device driver first, or something? The printer and
port name I am passing to DeviceCapabilities is the correct one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top