permission to call SetPrinter from C#

  • Thread starter Stephan Perschke
  • Start date
S

Stephan Perschke

Hi,

I try to change some printer settings using the win32 api OpenPrinter,
GetPrinter and SetPrinter. I copied most of the code from this or
related newsgroups. However, OpenPrinter, GetPrinter and ClosePrinter
works so far but I cannot change the printer settings. SetPrinter
returns the error code 5 ACCESS DENIED. I know I have to deal with the
desired access mask in the _DEFAULT_PRINTER structure when calling
OpenPrinter. Unfortunaltely the declaration of the DEFAULT_PRINTER
structure or class I found on the internet doesn't work with the
OpenPrinter function.

Here is the code (Warning: don't proceed with reading if you find bad code
offending! I'm not a programer and I'm new to C#)

public class Printer
{


[DllImport("winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=false,
CallingConvention=CallingConvention.StdCall )]
public static extern bool OpenPrinter(string pPrinterName,ref IntPtr
phPrinter, PRINTER_DEFAULTS pDefaults);


[DllImport("winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=true,
CallingConvention=CallingConvention.StdCall )]
public static extern bool ClosePrinter(IntPtr hPrinter);

[DllImport("winspool.drv", CharSet=CharSet.Auto)]
public static extern bool GetPrinterA(IntPtr hPrinter, int dwLevel,
IntPtr pPrinter, int cbBuf, ref int pcbNeeded);

[DllImport("WinSpool.drv",SetLastError=true)]
public static extern bool SetPrinter (IntPtr hPrinter, int dwLevel,
ref PRINTER_INFO_8 pPrinter, uint Command);

public struct PRINTER_DEFAULTS
{
public string pDataType;
public DEVMODE pDevMode;
public ACCESS_MASK DesiredAccess;
}

public struct PRINTER_DEFAULTS
{
public string pDataType;
public DEVMODE pDevMode;
public ACCESS_MASK DesiredAccess;
}
[Flags] public enum ACCESS_MASK

{

PRINTER_ACCESS_ADMINISTER = 4,

PRINTER_ACCESS_USE = 8,

PRINTER_ALL_ACCESS = 983052

}

}


class MyClass
{
[STAThread]
static void Main(string[] args)
{

int dwLevel = 8;
bool retVal = false;
printer.PRINTER_INFO_8 printerInfo = new printer.PRINTER_INFO_8();

System.IntPtr lhPrinter= new System.IntPtr();
printer.PRINTER_DEFAULTS pd = new printer.PRINTER_DEFAULTS();

pd.pDataType = "";
pd.pDevMode = new DEVMODE();
pd.DesiredAccess = printer.ACCESS_MASK.PRINTER_ALL_ACCESS;

retVal = printer.OpenPrinter("Printer", ref lhPrinter, pd);

.....

I appreciate your help!

- Stephan
 
M

Mattias Sjögren

Stephan,

Your system date seems to be wrong.

[DllImport("winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=false,
CallingConvention=CallingConvention.StdCall )]
public static extern bool OpenPrinter(string pPrinterName,ref IntPtr
phPrinter, PRINTER_DEFAULTS pDefaults);


[DllImport("winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=true,
CallingConvention=CallingConvention.StdCall )]
public static extern bool ClosePrinter(IntPtr hPrinter);

[DllImport("winspool.drv", CharSet=CharSet.Auto)]
public static extern bool GetPrinterA(IntPtr hPrinter, int dwLevel,
IntPtr pPrinter, int cbBuf, ref int pcbNeeded);

[DllImport("WinSpool.drv",SetLastError=true)]
public static extern bool SetPrinter (IntPtr hPrinter, int dwLevel,
ref PRINTER_INFO_8 pPrinter, uint Command);

I'd declare these as

[DllImport("winspool.drv", CharSet=CharSet.Auto)]
public static extern bool OpenPrinter(string pPrinterName, out IntPtr
phPrinter, ref PRINTER_DEFAULTS pDefaults);

[DllImport("winspool.drv")]
public static extern bool ClosePrinter(IntPtr hPrinter);

[DllImport("winspool.drv", CharSet=CharSet.Auto)]
public static extern bool GetPrinter(IntPtr hPrinter, int dwLevel,
IntPtr pPrinter, int cbBuf, out int pcbNeeded);

[DllImport("winspool.drv", CharSet=CharSet.Auto)]
public static extern bool SetPrinter(IntPtr hPrinter, int dwLevel, ref
PRINTER_INFO_8 pPrinter, uint Command);

Note that the pDefaults parameter of OpenPrinter must be ref if
PRINTER_DEFAULTS is a struct.

public struct PRINTER_DEFAULTS
{
public string pDataType;
public DEVMODE pDevMode;
public ACCESS_MASK DesiredAccess;
}

Make this

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct PRINTER_DEFAULTS
{
public string pDataType;
public IntPtr pDevMode;
public ACCESS_MASK DesiredAccess;
}

pDevMode is a *pointer to* a DEVMODE structure.

pd.DesiredAccess = printer.ACCESS_MASK.PRINTER_ALL_ACCESS;

Preferrably you should open the printer handle with the least possible
access rights. It makes it more likely that your code will work also
for non-admins. If SetPrinter is all you want to do, I believe
PRINTER_ACCESS_ADMINISTER should be enough.



Mattias
 
G

Guest

I have a similar issue..

I actually followed the method described at http://support.microsoft.com/default.aspx?scid=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.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 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","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, 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...

//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
 
M

Mattias Sjögren

Joseph,
And I still get the "Error 5: Access is denied." error from the "setprinter" call.

I'm surprised that you don't get an error earlier.

In the OpenPrinter declaration, the PRINTER_DEFAULTS parameter should
be passed by ref.

And the DocumentProperties return type should be an int, not a long.

Also, you seem to be forgetting to free the memory you allocate with
Marshal.AllocCoTaskMem.



Mattias
 
G

Guest

Thanks for the suggestions
I made the changes and ran it again and now I seem to get passed the "Access denied" error however it now comes up with a different error

"Error 1797: The printer driver is unknown.\r\n

Could it be the way I have declared my structures or API's?
 
M

Mattias Sjögren

Joseph,
I made the changes and ran it again and now I seem to get passed the "Access denied" error however it now comes up with a different error:

Which call is it that's failing?



Mattias
 
C

castex

fail the call to Set printer,return o and the error is 1797:The printer
driver is unknown
 
C

castex

fail the call to Set printer,return o and the error is 1797:The printer
driver is unknown
 

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