I have a similar issue..
I actually followed the method described at
http://support.microsoft.com/default...kb;en-us;14028
However my version is in C# using the normal "SAFE" calls i.e. Marshal.whateve
And I still get the "Error 5: Access is denied." error from the "setprinter" call
I have Administrator rights with my local login on my Windows 2000 pc.
I'd really appreciate it if anyone has any suggestions or can see something that I am blatantly doing wrong. =
My Code is below. Everything seems to work until I get to the "SetPrinter" call
I haven't cleaned up the code i.e. No code to free up allocated memory yet
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 long DocumentProperties (IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, IntPtr pDevModeInput, IntPtr 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, 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.Aut o)
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 the actual logic:
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 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","nDup lexSetting 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, 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(ptrPrinterI nfo, typeof(PRINTER_INFO_2));
if (pinfo.pDevMode == IntPtr.Zero)
{
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
//get the size of the devmode structure
nRet = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (nRet <= 0)
{
return false;
}
ptrDM = Marshal.AllocCoTaskMem(nRet);
nRet = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
IntPtr.Zero, (IntPtr)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 = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pinfo.pDevMode
, pinfo.pDevMode, (IntPtr)(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