虽然说.NET为我们提供了很多打印设置的功能,比如PrintDialog。
可是有的功能是没有实现的,比如PrintDialog的窗体中,按下“打印机”后,弹出来的打印机设置窗口。
会发现,这个打印机设置会根据不同的打印机而不同。
而它更改的,是打印机的默认设置。
怎么实现这个功能呢?
这个难题让我在网上寻觅了一个星期。
也没有找到实现这个功能的代码。
要么就是只能弹出这个对话框,不能保存修改。
要么就是只能直接修改设置,不能弹出对话框。
经过一番摸索和尝试。我终于得出了以下的一个类。调用这个类唯一的公共方法,ChangePrinterSetting(string PrinterName),即可以修改相应名字的打印机默认设置。VS2005下调试通过。希望对需要的朋友有所帮助。
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace PrinterSetting
{
//written by fujie
public class PrinterSetting
{
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public int pDatatype;
public int pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential)]
struct PRINTER_INFO_2
{
[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;
}
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
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 = 32)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
#region ■变量_____________________________________________________________
private IntPtr hPrinter = new System.IntPtr();
private PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
private PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
private DEVMODE dm;
private IntPtr ptrDM;
private IntPtr ptrPrinterInfo;
private int sizeOfDevMode = 0;
private int lastError;
private int nBytesNeeded;
private long nRet;
private int intError;
private System.Int32 nJunk;
private IntPtr yDevModeData;
#endregion
#region ■API___________________________________________________________
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private 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)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int Command);
#endregion
#region ■常数_____________________________________________________________
private const int DM_OUT_BUFFER = 2;
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int PRINTER_ACCESS_ADMINISTER = 0x4;
private const int PRINTER_ACCESS_USE = 0x8;
private const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
#endregion
#region ■方法_____________________________________________________________
public void ChangePrinterSetting(string i_printerName)
{
DEVMODE dm;
IntPtr pPrinter = IntPtr.Zero;
IntPtr pDevModeOutput = IntPtr.Zero;
IntPtr pDevModeInput = IntPtr.Zero;
PrinterValues.pDatatype = 0;
PrinterValues.pDevMode = 0;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
OpenPrinter(i_printerName, out pPrinter, ref PrinterValues);
int iNeeded = DocumentProperties(IntPtr.Zero, pPrinter, i_printerName, pDevModeOutput, ref pDevModeInput, 0);
pDevModeOutput = Marshal.AllocHGlobal(iNeeded);
int mode = 2 | 4;
int nRet = DocumentProperties(IntPtr.Zero, pPrinter, i_printerName, pDevModeOutput, ref pDevModeInput, mode);
if (nRet == 1)
{
dm = (DEVMODE)Marshal.PtrToStructure(pDevModeOutput, typeof(DEVMODE));
ChangePrinterSetting(i_printerName, ref dm);
}
ClosePrinter(pPrinter);
}
private void ChangePrinterSetting(string i_printerName, ref DEVMODE i_fujie)
{
{
dm = this.GetPrinterSettings(i_printerName);
dm = i_fujie;
Marshal.StructureToPtr(dm, yDevModeData, true);
pinfo.pDevMode = yDevModeData;
pinfo.pSecurityDescriptor = IntPtr.Zero;
Marshal.StructureToPtr(pinfo, ptrPrinterInfo, true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
}
}
private DEVMODE GetPrinterSettings(string i_printerName)
{
DEVMODE dm;
nRet = Convert.ToInt32(OpenPrinter(i_printerName, out hPrinter, ref PrinterValues));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
throw new System.Exception("Unable to allocate memory by fujie");
}
else
{
ptrPrinterInfo = Marshal.AllocHGlobal(nBytesNeeded);
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));
IntPtr Temp = new IntPtr();
if (pinfo.pDevMode == IntPtr.Zero)
{
IntPtr ptrZero = IntPtr.Zero;
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter, i_printerName, ptrZero, ref ptrZero, 0);
ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
int i;
i = DocumentProperties(IntPtr.Zero, hPrinter, i_printerName, ptrDM, ref ptrZero, DM_OUT_BUFFER);
if ((i < 0) || (ptrDM == IntPtr.Zero))
{
throw new System.Exception("fujie:Cannot get DEVMODE data");
}
pinfo.pDevMode = ptrDM;
}
intError = DocumentProperties(IntPtr.Zero, hPrinter, i_printerName, IntPtr.Zero, ref Temp, 0);
yDevModeData = Marshal.AllocHGlobal(intError);
intError = DocumentProperties(IntPtr.Zero, hPrinter, i_printerName, yDevModeData, ref Temp, 2);
dm = (DEVMODE)Marshal.PtrToStructure(yDevModeData, typeof(DEVMODE));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return dm;
}
}
#endregion
}
}
经典评论:
1、没打印机呀。
2、我用的是局域网的打印机,属性窗口也能出来,但是,无论我改变还是不改变打印设置,点击确定或取消,我的程序就从运行退出来了,我还以为是我没有权限去改变所连接的机子的打印机默认设置,所以我装了一个虚拟打印机,但是还是没能成功,麻烦你给看看什么问题吧。
3、第142行,为什么要把mode给赋成2|4这个值?这个值是6,为什么呢?
4、我调试时,每次到了143行这就出现问题,nRet这个值老是不是1。
5、我在VS2008下编译这段程序,但总是在159行的时候整个程序就崩溃了,不论我是否修改pinfo,请问您有碰到类似情况么?
6、修改不成功,看不出哪里有问题啊,兄弟指点一下呗,好像全部语句都执行了。
7、为何设置后点击确认,程序会直接挂掉,还有就是选择Fax时会出现异常。
8、仔细在看了一下,发现只能修改页面设置的内容,其他页面的属性修改不了。
9、楼主,只能设置基本的页面,页数等,对一些打印机不能设置双面打印,设置成功,但是双面设置没有变化,通过调试看DEVMODER的dmDuplex已变化 ,但设置后,实际无变化,求解!