G
Guest
I was wondering if someone could lend me a hand with a C# problem I am having
I am trying to use the “setPrinter†api to change the duplex setting (under printing preferences on printer context menu) so that I can send a document to the printer in duplex mode
These are my declarations
[DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
internal static extern Int32 GetLastError()
[DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
static extern bool ClosePrinter(IntPtr hPrinter)
[DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
private static extern int DocumentProperties (IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode)
[DllImport("winspool.Drv", EntryPoint="GetPrinterA", SetLastError=true, CharSet=CharSet.Ansi
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded)
[DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd)
[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command)
// Wrapper for Win32 message formatter
[DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)
private unsafe static exter
int FormatMessage( int dwFlags
ref IntPtr pMessageSource
int dwMessageID
int dwLanguageID
ref string lpBuffer
int nSize
IntPtr* pArguments)
[StructLayout(LayoutKind.Sequential)
private struct PRINTER_DEFAULT
public int pDatatype
public int pDevMode
public int DesiredAccess
[StructLayout(LayoutKind.Sequential)
private struct PRINTER_INFO_
[MarshalAs(UnmanagedType.LPStr)] public string pServerName;
[MarshalAs(UnmanagedType.LPStr)] public string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)] public string pShareName;
[MarshalAs(UnmanagedType.LPStr)] public string pPortName;
[MarshalAs(UnmanagedType.LPStr)] public string pDriverName;
[MarshalAs(UnmanagedType.LPStr)] public string pComment;
[MarshalAs(UnmanagedType.LPStr)] public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)] public string pSepFile;
[MarshalAs(UnmanagedType.LPStr)] public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)] public string pDatatype;
[MarshalAs(UnmanagedType.LPStr)] public string pParameters;
public IntPtr pSecurityDescriptor;
public Int32 Attributes;
public Int32 Priority;
public Int32 DefaultPriority;
public Int32 StartTime;
public Int32 UntilTime;
public Int32 Status;
public Int32 cJobs;
public Int32 AveragePPM;
private const short CCDEVICENAME = 32
private const short CCFORMNAME = 32
[StructLayout(LayoutKind.Sequential)]
private struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName
public short dmSpecVersion
public short dmDriverVersion
public short dmSize
public short dmDriverExtra
public int dmFields
public short dmOrientation
public short dmPaperSize
public short dmPaperLength
public short dmPaperWidth
public short dmScale
public short dmCopies
public short dmDefaultSource
public short dmPrintQuality
public short dmColor
public short dmDuplex
public short dmYResolution
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
public const int DM_DUPLEX = 0x1000;
public const int DM_IN_BUFFER = 8;
public const int DM_OUT_BUFFER = 2;
public const int PRINTER_ACCESS_ADMINISTER = 0x4;
public const int PRINTER_ACCESS_USE = 0x8;
public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
public const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
And here is my method:
public bool SetPrinterDuplex(string sPrinterName, int nDuplexSetting)
{
IntPtr hPrinter;
PRINTER_DEFAULTS pd = new PRINTER_DEFAULTS();
PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
DEVMODE dm;
IntPtr ptrDM;
IntPtr ptrPrinterInfo;
int sizeOfDevMode = 0;
int lastError;
byte[] yDevModeData;
byte[] yPInfoMemory;
int nBytesNeeded;
int nRet;
System.Int32 nJunk;
//On Error GoTo cleanup
if ((nDuplexSetting < 1) || (nDuplexSetting > 3) )
{
throw new ArgumentOutOfRangeException("nDuplexSetting","nDuplexSetting is incorrect.");
}
else
{
//if no printername provided, check if there is a default printer and use it instead
if (sPrinterName.Trim() == "")
{
PrintDocument printDocument1 = new PrintDocument();
sPrinterName = printDocument1.PrinterSettings.PrinterName;// + "\0";
}
//open the printer
pd.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, ref pd));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
return false;
}
//get the size of the Printer Info structure
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
return false;
}
// Allocate enough space for PRINTER_INFO_2...
ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);
// The second GetPrinter fills in all the current settings, so all you
// need to do is modify what you're interested in...
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
return false;
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));
if (pinfo.pDevMode == IntPtr.Zero)
{
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
IntPtr ptrZero = IntPtr.Zero;
//get the size of the devmode structure
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrZero, ref ptrZero, 0);
if (nRet <= 0)
{
return false;
}
ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
ref ptrZero, DM_OUT_BUFFER);
if ((nRet < 0) || (ptrDM == IntPtr.Zero))
{
//Cannot get the DEVMODE structure.
return false;
}
pinfo.pDevMode = ptrDM;
}
dm = (DEVMODE)Marshal.PtrToStructure(pinfo.pDevMode, typeof(DEVMODE));
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
//You cannot modify the duplex flag for this printer
//because it does not support duplex or the driver does not support setting
//it from the Windows API.
return false;
}
//update fields
dm.dmDuplex = (short)nDuplexSetting;
dm.dmFields = DM_DUPLEX;
Marshal.StructureToPtr(dm,pinfo.pDevMode,true);
//pinfo.pDevMode = ptrDM;
pinfo.pSecurityDescriptor = IntPtr.Zero;
//update driver dependent part of the DEVMODE
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pinfo.pDevMode
, ref pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));
if (nRet < 0)
{
//Unable to set duplex setting to this printer.
return false;
}
Marshal.StructureToPtr(pinfo,ptrPrinterInfo,true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
//Unable to set shared printer settings.
lastError = Marshal.GetLastWin32Error();
string myErrMsg = GetErrorMessage(lastError);
return false;
}
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
return Convert.ToBoolean(nRet);
}//End SetPrinterDuplex
It fails at the “setPrinter†with the following error "Error 1797: The printer driver is unknown.\r\n".
By the way, the code works in VB6 but I don’t want it to work in VB6, I want it to work in C#!!
Any help is appreciated.
I am trying to use the “setPrinter†api to change the duplex setting (under printing preferences on printer context menu) so that I can send a document to the printer in duplex mode
These are my declarations
[DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
internal static extern Int32 GetLastError()
[DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
static extern bool ClosePrinter(IntPtr hPrinter)
[DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
private static extern int DocumentProperties (IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode)
[DllImport("winspool.Drv", EntryPoint="GetPrinterA", SetLastError=true, CharSet=CharSet.Ansi
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded)
[DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)
static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd)
[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command)
// Wrapper for Win32 message formatter
[DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)
private unsafe static exter
int FormatMessage( int dwFlags
ref IntPtr pMessageSource
int dwMessageID
int dwLanguageID
ref string lpBuffer
int nSize
IntPtr* pArguments)
[StructLayout(LayoutKind.Sequential)
private struct PRINTER_DEFAULT
public int pDatatype
public int pDevMode
public int DesiredAccess
[StructLayout(LayoutKind.Sequential)
private struct PRINTER_INFO_
[MarshalAs(UnmanagedType.LPStr)] public string pServerName;
[MarshalAs(UnmanagedType.LPStr)] public string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)] public string pShareName;
[MarshalAs(UnmanagedType.LPStr)] public string pPortName;
[MarshalAs(UnmanagedType.LPStr)] public string pDriverName;
[MarshalAs(UnmanagedType.LPStr)] public string pComment;
[MarshalAs(UnmanagedType.LPStr)] public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)] public string pSepFile;
[MarshalAs(UnmanagedType.LPStr)] public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)] public string pDatatype;
[MarshalAs(UnmanagedType.LPStr)] public string pParameters;
public IntPtr pSecurityDescriptor;
public Int32 Attributes;
public Int32 Priority;
public Int32 DefaultPriority;
public Int32 StartTime;
public Int32 UntilTime;
public Int32 Status;
public Int32 cJobs;
public Int32 AveragePPM;
private const short CCDEVICENAME = 32
private const short CCFORMNAME = 32
[StructLayout(LayoutKind.Sequential)]
private struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName
public short dmSpecVersion
public short dmDriverVersion
public short dmSize
public short dmDriverExtra
public int dmFields
public short dmOrientation
public short dmPaperSize
public short dmPaperLength
public short dmPaperWidth
public short dmScale
public short dmCopies
public short dmDefaultSource
public short dmPrintQuality
public short dmColor
public short dmDuplex
public short dmYResolution
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
public const int DM_DUPLEX = 0x1000;
public const int DM_IN_BUFFER = 8;
public const int DM_OUT_BUFFER = 2;
public const int PRINTER_ACCESS_ADMINISTER = 0x4;
public const int PRINTER_ACCESS_USE = 0x8;
public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
public const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
And here is my method:
public bool SetPrinterDuplex(string sPrinterName, int nDuplexSetting)
{
IntPtr hPrinter;
PRINTER_DEFAULTS pd = new PRINTER_DEFAULTS();
PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
DEVMODE dm;
IntPtr ptrDM;
IntPtr ptrPrinterInfo;
int sizeOfDevMode = 0;
int lastError;
byte[] yDevModeData;
byte[] yPInfoMemory;
int nBytesNeeded;
int nRet;
System.Int32 nJunk;
//On Error GoTo cleanup
if ((nDuplexSetting < 1) || (nDuplexSetting > 3) )
{
throw new ArgumentOutOfRangeException("nDuplexSetting","nDuplexSetting is incorrect.");
}
else
{
//if no printername provided, check if there is a default printer and use it instead
if (sPrinterName.Trim() == "")
{
PrintDocument printDocument1 = new PrintDocument();
sPrinterName = printDocument1.PrinterSettings.PrinterName;// + "\0";
}
//open the printer
pd.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, ref pd));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
return false;
}
//get the size of the Printer Info structure
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
return false;
}
// Allocate enough space for PRINTER_INFO_2...
ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);
// The second GetPrinter fills in all the current settings, so all you
// need to do is modify what you're interested in...
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
return false;
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));
if (pinfo.pDevMode == IntPtr.Zero)
{
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
IntPtr ptrZero = IntPtr.Zero;
//get the size of the devmode structure
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrZero, ref ptrZero, 0);
if (nRet <= 0)
{
return false;
}
ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
ref ptrZero, DM_OUT_BUFFER);
if ((nRet < 0) || (ptrDM == IntPtr.Zero))
{
//Cannot get the DEVMODE structure.
return false;
}
pinfo.pDevMode = ptrDM;
}
dm = (DEVMODE)Marshal.PtrToStructure(pinfo.pDevMode, typeof(DEVMODE));
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
//You cannot modify the duplex flag for this printer
//because it does not support duplex or the driver does not support setting
//it from the Windows API.
return false;
}
//update fields
dm.dmDuplex = (short)nDuplexSetting;
dm.dmFields = DM_DUPLEX;
Marshal.StructureToPtr(dm,pinfo.pDevMode,true);
//pinfo.pDevMode = ptrDM;
pinfo.pSecurityDescriptor = IntPtr.Zero;
//update driver dependent part of the DEVMODE
nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pinfo.pDevMode
, ref pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));
if (nRet < 0)
{
//Unable to set duplex setting to this printer.
return false;
}
Marshal.StructureToPtr(pinfo,ptrPrinterInfo,true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
//Unable to set shared printer settings.
lastError = Marshal.GetLastWin32Error();
string myErrMsg = GetErrorMessage(lastError);
return false;
}
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
return Convert.ToBoolean(nRet);
}//End SetPrinterDuplex
It fails at the “setPrinter†with the following error "Error 1797: The printer driver is unknown.\r\n".
By the way, the code works in VB6 but I don’t want it to work in VB6, I want it to work in C#!!
Any help is appreciated.