vb.net 在 Windows 7 上访问打印机设置 (DEVMODE)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18452172/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Access to printer settings (DEVMODE) on windows 7
提问by Wine Too
I have serious trouble to access and change printer settings on windows 7.
我无法在 Windows 7 上访问和更改打印机设置。
Option Strict On
Imports System.Runtime.InteropServices
Imports System.ComponentModel
<StructLayout(LayoutKind.Sequential)> _
Structure PRINTER_DEFAULTS
Public pDatatype As IntPtr
Public pDevMode As IntPtr
Public DesiredAccess As Integer
End Structure
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure PRINTER_INFO_2
<MarshalAs(UnmanagedType.LPTStr)> _
Public pServerName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPrinterName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pShareName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPortName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pDriverName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pComment As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pLocation As String
Public pDevMode As IntPtr
<MarshalAs(UnmanagedType.LPTStr)> _
Public pSepFile As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPrintProcessor As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pDatatype As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pParameters As String
Public pSecurityDescriptor As IntPtr
Public Attributes As UInteger
Public Priority As UInteger
Public DefaultPriority As UInteger
Public StartTime As UInteger
Public UntilTime As UInteger
Public Status As UInteger
Public cJobs As UInteger
Public AveragePPM As UInteger
End Structure
Structure POINTL
Public x As Int32
Public y As Int32
End Structure
<Flags()> _
Enum DM As Integer
Orientation = &H1
PaperSize = &H2
PaperLength = &H4
PaperWidth = &H8
Scale = &H10
Position = &H20
NUP = &H40
DisplayOrientation = &H80
Copies = &H100
DefaultSource = &H200
PrintQuality = &H400
Color = &H800
Duplex = &H1000
YResolution = &H2000
TTOption = &H4000
Collate = &H8000
FormName = &H10000
LogPixels = &H20000
BitsPerPixel = &H40000
PelsWidth = &H80000
PelsHeight = &H100000
DisplayFlags = &H200000
DisplayFrequency = &H400000
ICMMethod = &H800000
ICMIntent = &H1000000
MediaType = &H2000000
DitherType = &H4000000
PanningWidth = &H8000000
PanningHeight = &H10000000
DisplayFixedOutput = &H20000000
End Enum
<StructLayout(LayoutKind.Explicit, CharSet:=CharSet.Ansi)> _
Structure DEVMODE
Public Const CCHDEVICENAME As Integer = 32
Public Const CCHFORMNAME As Integer = 32
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> _
<System.Runtime.InteropServices.FieldOffset(0)> _
Public dmDeviceName As String
<System.Runtime.InteropServices.FieldOffset(32)> _
Public dmSpecVersion As Int16
<System.Runtime.InteropServices.FieldOffset(34)> _
Public dmDriverVersion As Int16
<System.Runtime.InteropServices.FieldOffset(36)> _
Public dmSize As Int16
<System.Runtime.InteropServices.FieldOffset(38)> _
Public dmDriverExtra As Int16
<System.Runtime.InteropServices.FieldOffset(40)> _
Public dmFields As DM
<System.Runtime.InteropServices.FieldOffset(44)> _
Private dmOrientation As Int16
<System.Runtime.InteropServices.FieldOffset(46)> _
Private dmPaperSize As Int16
<System.Runtime.InteropServices.FieldOffset(48)> _
Private dmPaperLength As Int16
<System.Runtime.InteropServices.FieldOffset(50)> _
Private dmPaperWidth As Int16
<System.Runtime.InteropServices.FieldOffset(52)> _
Public dmScale As Int16
<System.Runtime.InteropServices.FieldOffset(54)> _
Public dmCopies As Int16
<System.Runtime.InteropServices.FieldOffset(56)> _
Private dmDefaultSource As Int16
<System.Runtime.InteropServices.FieldOffset(58)> _
Private dmPrintQuality As Int16
<System.Runtime.InteropServices.FieldOffset(44)> _
Public dmPosition As POINTL
<System.Runtime.InteropServices.FieldOffset(52)> _
Public dmDisplayOrientation As Int32
<System.Runtime.InteropServices.FieldOffset(56)> _
Public dmDisplayFixedOutput As Int32
<System.Runtime.InteropServices.FieldOffset(60)> _
Public dmColor As Short
<System.Runtime.InteropServices.FieldOffset(62)> _
Public dmDuplex As Short
<System.Runtime.InteropServices.FieldOffset(64)> _
Public dmYResolution As Short
<System.Runtime.InteropServices.FieldOffset(66)> _
Public dmTTOption As Short
<System.Runtime.InteropServices.FieldOffset(68)> _
Public dmCollate As Short
<System.Runtime.InteropServices.FieldOffset(72)> _
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)> _
Public dmFormName As String
<System.Runtime.InteropServices.FieldOffset(102)> _
Public dmLogPixels As Int16
<System.Runtime.InteropServices.FieldOffset(104)> _
Public dmBitsPerPel As Int32
<System.Runtime.InteropServices.FieldOffset(108)> _
Public dmPelsWidth As Int32
<System.Runtime.InteropServices.FieldOffset(112)> _
Public dmPelsHeight As Int32
<System.Runtime.InteropServices.FieldOffset(116)> _
Public dmDisplayFlags As Int32
<System.Runtime.InteropServices.FieldOffset(116)> _
Public dmNup As Int32
<System.Runtime.InteropServices.FieldOffset(120)> _
Public dmDisplayFrequency As Int32
End Structure
Class PrinterSettings
Private gPrinter As IntPtr = New System.IntPtr()
Private gPrinterValues As New PRINTER_DEFAULTS()
Private gPInfo As New PRINTER_INFO_2()
Private gDevMode As DEVMODE
Private gPtrDevMode As IntPtr
Private gPtrPrinterInfo As IntPtr
Private gSizeOfDevMode As Integer = 0
Private gLastError As Integer
Private gNBytesNeeded As Integer
Private gNRet As Long
Private gIntError As Integer
Private gNTemporary As Integer
Private gDevModeData As IntPtr
<DllImport("winspool.Drv", EntryPoint:="ClosePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="DocumentPropertiesA", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function DocumentProperties(ByVal hwnd As IntPtr, ByVal hPrinter As IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal pDeviceName As String, ByVal pDevModeOutput As IntPtr, ByVal pDevModeInput As IntPtr, ByVal fMode As Integer) As Integer
End Function
<DllImport("winspool.drv", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function GetPrinter(ByVal hPrinter As IntPtr, ByVal dwLevel As Int32, ByVal pPrinter As IntPtr, ByVal dwBuf As Int32, ByRef dwNeeded As Int32) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="OpenPrinterA", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function OpenPrinter(<MarshalAs(UnmanagedType.LPStr)> ByVal szPrinter As String, ByRef hPrinter As IntPtr, ByRef pd As PRINTER_DEFAULTS) As Boolean
End Function
<DllImport("winspool.drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _
Private Shared Function SetPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, ByVal pPrinter As IntPtr, ByVal Command As Integer) As Boolean
End Function
Private Const DM_DUPLEX As Integer = 4096
''0x1000
Private Const DM_IN_BUFFER As Integer = 8
Private Const DM_OUT_BUFFER As Integer = 2
Private Const PRINTER_ACCESS_ADMINISTER As Integer = 4
''0x4
Private Const STANDARD_RIGHTS_REQUIRED As Integer = 983040
''0xF0000
Private Const PRINTER_ALL_ACCESS As Integer = STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE
Private Const PRINTER_ACCESS_USE As Integer = 8
''0x8
Public Function SetPrinterSettings(ByVal iPrinterName As String, ByVal dm As DEVMODE, Optional ByVal ShowPreferences As Integer = 0) As Integer
gDevMode = dm
Marshal.StructureToPtr(gDevMode, gDevModeData, True)
gPInfo.pDevMode = gDevModeData
gPInfo.pSecurityDescriptor = IntPtr.Zero
''optionally brings up the printer preferences dialog
If ShowPreferences = 1 Then
Dim retval As Integer
retval = DocumentProperties(IntPtr.Zero, gPrinter, iPrinterName, gDevModeData, gPInfo.pDevMode, DM_IN_BUFFER Or DM_OUT_BUFFER Or PRINTER_ACCESS_ADMINISTER)
If retval = DialogResult.Cancel Then Return -1
End If
''update driver dependent part of the DEVMODE
''HERE CRASHES ******************************
Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, True)
''
gLastError = Marshal.GetLastWin32Error()
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0))
If gNRet = 0 Then
''Unable to set extern printer settings.
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
If gPrinter <> IntPtr.Zero Then
ClosePrinter(gPrinter)
End If
Return CInt(gNRet)
End Function
Public Function GetPrinterSettings(ByVal PrinterName As String) As DEVMODE
Dim lDevMode As DEVMODE
gPrinterValues.pDatatype = IntPtr.Zero
gPrinterValues.pDevMode = IntPtr.Zero
gPrinterValues.DesiredAccess = PRINTER_ALL_ACCESS
gNRet = Convert.ToInt32(OpenPrinter(PrinterName, gPrinter, gPrinterValues))
If gNRet = 0 Then
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
GetPrinter(gPrinter, 2, IntPtr.Zero, 0, gNBytesNeeded)
If gNBytesNeeded <= 0 Then
Throw New System.Exception("Unable to allocate memory")
Else
'' Allocate enough space for PRINTER_INFO_2...
gPtrPrinterInfo = Marshal.AllocCoTaskMem(gNBytesNeeded)
gPtrPrinterInfo = Marshal.AllocHGlobal(gNBytesNeeded)
''The second GetPrinter fills in all the current settings, so all you
''need to do is modify what youre interested in...
gNRet = Convert.ToInt32(GetPrinter(gPrinter, 2, gPtrPrinterInfo, gNBytesNeeded, gNTemporary))
If gNRet = 0 Then
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
gPInfo = CType(Marshal.PtrToStructure(gPtrPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2)
Dim lTempBuffer As New IntPtr()
If gPInfo.pDevMode = IntPtr.Zero Then
''if GetPrinter didnt fill in the DEVMODE, try to get it by calling
''DocumentProperties...
Dim ptrZero As IntPtr = IntPtr.Zero
''get the size of the devmode struct
gSizeOfDevMode = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, ptrZero, ptrZero, 0)
gPtrDevMode = Marshal.AllocCoTaskMem(gSizeOfDevMode)
Dim i As Integer = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, gPtrDevMode, ptrZero, DM_OUT_BUFFER)
If i < 0 OrElse gPtrDevMode <> IntPtr.Zero Then
''Cannot get the DEVMODE struct.
Throw New System.Exception("Cannot get DEVMODE data")
End If
gPInfo.pDevMode = gPtrDevMode
End If
gIntError = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, IntPtr.Zero, lTempBuffer, 0)
gDevModeData = Marshal.AllocHGlobal(gIntError)
gIntError = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, gDevModeData, lTempBuffer, 2)
lDevMode = CType(Marshal.PtrToStructure(gDevModeData, GetType(DEVMODE)), DEVMODE)
If gNRet = 0 OrElse gPrinter = IntPtr.Zero Then
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
Return lDevMode
End If
End Function
End Class
Usage:
用法:
Dim defaultPrinterName As String = getDefaultPrinter()
Dim dm As DEVMODE
Dim lPrinterpreferences As PrinterSettings = New PrinterSettings()
dm = lPrinterpreferences.GetPrinterSettings(defaultPrinterName)
dm.dmCopies = 4
lPrinterpreferences.SetPrinterSettings(defaultPrinterName, dm, 1)
EDIT: And probably last error remains when call to SetPrinter API.
编辑:调用 SetPrinter API 时,可能最后一个错误仍然存在。
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0))
This throws error: "The printer driver is unknown", but
这会引发错误:“打印机驱动程序未知”,但是
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 0, gPtrPrinterInfo, 3))
work without error but unreliable.
工作没有错误但不可靠。
How to get rid of this last error?
如何摆脱最后一个错误?
回答by H B
Here are some pointers:
以下是一些提示:
- Check and see if your structs and your dllimport functions are defined correctly. on pinvoke.net you can find tested signatures (example DocumentProperties function) http://www.pinvoke.net/default.aspx/winspool/DocumentProperties.html
- If you are running an x64 system, pay attention to the comments on pinvoke.net. Some functions/structs need to be changed on x64 systems.
- Are you running your program/visual studio as administrator or normal user? It might make a difference.
- 检查您的结构和 dllimport 函数是否定义正确。在 pinvoke.net 上,您可以找到经过测试的签名(例如 DocumentProperties 函数)http://www.pinvoke.net/default.aspx/winspool/DocumentProperties.html
- 如果您运行的是 x64 系统,请注意 pinvoke.net 上的评论。在 x64 系统上需要更改某些函数/结构。
- 您是作为管理员还是普通用户运行您的程序/视觉工作室?它可能会有所作为。
FYI I got the GetPrinterSettings function working in C# on Win7 x64 without any logical changes to the code. OpenPrinter didn't crash, but returned error nr 87 before I changed its function signature: gPrinter had to become an out parameter. I think you should be able to do the same checks and make it work in VB.
仅供参考,我让 GetPrinterSettings 函数在 Win7 x64 上的 C# 中工作,而没有对代码进行任何逻辑更改。OpenPrinter 没有崩溃,但在我更改其函数签名之前返回错误 nr 87:gPrinter 必须成为输出参数。我认为您应该能够进行相同的检查并使其在 VB 中工作。
Here's the class, though I believe you're better off using the structures and function signatures from pinvoke.net than trying to convert this back to VB.
这是课程,尽管我相信您最好使用 pinvoke.net 中的结构和函数签名,而不是尝试将其转换回 VB。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace PrintersettingsTest
{
[StructLayout(LayoutKind.Sequential)]
struct PRINTER_DEFAULTS
{
public IntPtr pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PRINTER_INFO_2
{
[MarshalAs(UnmanagedType.LPTStr)]
public string pServerName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pPrinterName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pShareName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pPortName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pDriverName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pComment;
[MarshalAs(UnmanagedType.LPTStr)]
public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPTStr)]
public string pSepFile;
[MarshalAs(UnmanagedType.LPTStr)]
public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPTStr)]
public string pDatatype;
[MarshalAs(UnmanagedType.LPTStr)]
public string pParameters;
public IntPtr pSecurityDescriptor;
public uint Attributes;
public uint Priority;
public uint DefaultPriority;
public uint StartTime;
public uint UntilTime;
public uint Status;
public uint cJobs;
public uint AveragePPM;
}
struct POINTL
{
public Int32 x;
public Int32 y;
}
[Flags()]
enum DM : int
{
Orientation = 0x1,
PaperSize = 0x2,
PaperLength = 0x4,
PaperWidth = 0x8,
Scale = 0x10,
Position = 0x20,
NUP = 0x40,
DisplayOrientation = 0x80,
Copies = 0x100,
DefaultSource = 0x200,
PrintQuality = 0x400,
Color = 0x800,
Duplex = 0x1000,
YResolution = 0x2000,
TTOption = 0x4000,
Collate = 0x8000,
FormName = 0x10000,
LogPixels = 0x20000,
BitsPerPixel = 0x40000,
PelsWidth = 0x80000,
PelsHeight = 0x100000,
DisplayFlags = 0x200000,
DisplayFrequency = 0x400000,
ICMMethod = 0x800000,
ICMIntent = 0x1000000,
MediaType = 0x2000000,
DitherType = 0x4000000,
PanningWidth = 0x8000000,
PanningHeight = 0x10000000,
DisplayFixedOutput = 0x20000000
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[System.Runtime.InteropServices.FieldOffset(0)]
public string dmDeviceName;
[System.Runtime.InteropServices.FieldOffset(32)]
public Int16 dmSpecVersion;
[System.Runtime.InteropServices.FieldOffset(34)]
public Int16 dmDriverVersion;
[System.Runtime.InteropServices.FieldOffset(36)]
public Int16 dmSize;
[System.Runtime.InteropServices.FieldOffset(38)]
public Int16 dmDriverExtra;
[System.Runtime.InteropServices.FieldOffset(40)]
public DM dmFields;
[System.Runtime.InteropServices.FieldOffset(44)]
Int16 dmOrientation;
[System.Runtime.InteropServices.FieldOffset(46)]
Int16 dmPaperSize;
[System.Runtime.InteropServices.FieldOffset(48)]
Int16 dmPaperLength;
[System.Runtime.InteropServices.FieldOffset(50)]
Int16 dmPaperWidth;
[System.Runtime.InteropServices.FieldOffset(52)]
Int16 dmScale;
[System.Runtime.InteropServices.FieldOffset(54)]
Int16 dmCopies;
[System.Runtime.InteropServices.FieldOffset(56)]
Int16 dmDefaultSource;
[System.Runtime.InteropServices.FieldOffset(58)]
Int16 dmPrintQuality;
[System.Runtime.InteropServices.FieldOffset(44)]
public POINTL dmPosition;
[System.Runtime.InteropServices.FieldOffset(52)]
public Int32 dmDisplayOrientation;
[System.Runtime.InteropServices.FieldOffset(56)]
public Int32 dmDisplayFixedOutput;
[System.Runtime.InteropServices.FieldOffset(60)]
public short dmColor;
[System.Runtime.InteropServices.FieldOffset(62)]
public short dmDuplex;
[System.Runtime.InteropServices.FieldOffset(64)]
public short dmYResolution;
[System.Runtime.InteropServices.FieldOffset(66)]
public short dmTTOption;
[System.Runtime.InteropServices.FieldOffset(68)]
public short dmCollate;
[System.Runtime.InteropServices.FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
[System.Runtime.InteropServices.FieldOffset(102)]
public Int16 dmLogPixels;
[System.Runtime.InteropServices.FieldOffset(104)]
public Int32 dmBitsPerPel;
[System.Runtime.InteropServices.FieldOffset(108)]
public Int32 dmPelsWidth;
[System.Runtime.InteropServices.FieldOffset(112)]
public Int32 dmPelsHeight;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmDisplayFlags;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmNup;
[System.Runtime.InteropServices.FieldOffset(120)]
public Int32 dmDisplayFrequency;
}
class PrinterSettings
{
private IntPtr gPrinter = new System.IntPtr();
private PRINTER_DEFAULTS gPrinterValues = new PRINTER_DEFAULTS();
private PRINTER_INFO_2 gPInfo = new PRINTER_INFO_2();
private DEVMODE gDevMode;
private IntPtr gPtrDevMode;
private IntPtr gPtrPrinterInfo;
private int gSizeOfDevMode = 0;
private int gLastError;
private int gNBytesNeeded;
private long gNRet;
private int gIntError;
private int gNTemporary;
private IntPtr gDevModeData;
[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 pDeviceName,
IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
public 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);
private const int DM_DUPLEX = 4096; //0x1000
private const int DM_IN_BUFFER = 8;
private const int DM_OUT_BUFFER = 2;
private const int PRINTER_ACCESS_ADMINISTER = 4; //0x4
private const int STANDARD_RIGHTS_REQUIRED = 983040; //0xF0000
private const int PRINTER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;
private const int PRINTER_ACCESS_USE = 8; //0x8
public bool ChangePrinterSetting(string iPrinterName)
{
gDevMode = this.GetPrinterSettings(iPrinterName);
Marshal.StructureToPtr(gDevMode, gDevModeData, true);
gPInfo.pDevMode = gDevModeData;
gPInfo.pSecurityDescriptor = IntPtr.Zero;
//bring up the printer preferences dialog
DocumentProperties(IntPtr.Zero, gPrinter, iPrinterName, gDevModeData
, gPInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER | PRINTER_ACCESS_ADMINISTER);
//update driver dependent part of the DEVMODE
Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, true);
gLastError = Marshal.GetLastWin32Error();
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0));
if (gNRet == 0)
{
//Unable to set extern printer settings.
gLastError = Marshal.GetLastWin32Error();
throw new Win32Exception(gLastError);
}
if (gPrinter != IntPtr.Zero)
{
ClosePrinter(gPrinter);
}
return Convert.ToBoolean(gNRet);
}
public DEVMODE GetPrinterSettings(string PrinterName)
{
DEVMODE lDevMode;
gPrinterValues.pDatatype = IntPtr.Zero;
gPrinterValues.pDevMode = IntPtr.Zero;
gPrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
// HERE CRASHES
gNRet = Convert.ToInt32(OpenPrinter(PrinterName, out gPrinter, ref gPrinterValues));
if (gNRet == 0)
{
gLastError = Marshal.GetLastWin32Error();
throw new Win32Exception(gLastError);
}
GetPrinter(gPrinter, 2, IntPtr.Zero, 0, out gNBytesNeeded);
if (gNBytesNeeded <= 0)
throw new System.Exception("Unable to allocate memory");
else
{
// Allocate enough space for PRINTER_INFO_2...
gPtrPrinterInfo = Marshal.AllocCoTaskMem(gNBytesNeeded);
gPtrPrinterInfo = Marshal.AllocHGlobal(gNBytesNeeded);
//The second GetPrinter fills in all the current settings, so all you
//need to do is modify what youre interested in...
gNRet = Convert.ToInt32(GetPrinter(gPrinter, 2,
gPtrPrinterInfo, gNBytesNeeded, out gNTemporary));
if (gNRet == 0)
{
gLastError = Marshal.GetLastWin32Error();
throw new Win32Exception(gLastError);
}
gPInfo = (PRINTER_INFO_2)Marshal.PtrToStructure(gPtrPrinterInfo, typeof(PRINTER_INFO_2));
IntPtr lTempBuffer = new IntPtr();
if (gPInfo.pDevMode == IntPtr.Zero)
{
//if GetPrinter didnt fill in the DEVMODE, try to get it by calling
//DocumentProperties...
IntPtr ptrZero = IntPtr.Zero;
//get the size of the devmode struct
gSizeOfDevMode = DocumentProperties(IntPtr.Zero, gPrinter,
PrinterName, ptrZero, ptrZero, 0);
gPtrDevMode = Marshal.AllocCoTaskMem(gSizeOfDevMode);
int i = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, gPtrDevMode,
ptrZero, DM_OUT_BUFFER);
if (i < 0 || gPtrDevMode != IntPtr.Zero)
{
//Cannot get the DEVMODE struct.
throw new System.Exception("Cannot get DEVMODE data");
}
gPInfo.pDevMode = gPtrDevMode;
}
gIntError = DocumentProperties(IntPtr.Zero, gPrinter,
PrinterName, IntPtr.Zero, lTempBuffer, 0);
gDevModeData = Marshal.AllocHGlobal(gIntError);
gIntError = DocumentProperties(IntPtr.Zero, gPrinter,
PrinterName, gDevModeData, lTempBuffer, 2);
lDevMode = (DEVMODE)Marshal.PtrToStructure(gDevModeData, typeof(DEVMODE));
if (gNRet == 0 || gPrinter == IntPtr.Zero)
{
gLastError = Marshal.GetLastWin32Error();
throw new Win32Exception(gLastError);
}
return lDevMode;
}
}
}
}
回答by Hans Passant
Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, True)
This will terminate the program with exception code 0xc0000374, you can see it in the Output window. That's STATUS_HEAP_CORRUPTION, "A heap has been corrupted".
这将终止带有异常代码 0xc0000374 的程序,您可以在“输出”窗口中看到它。那是 STATUS_HEAP_CORRUPTION,“堆已损坏”。
This happened because of the last argument, you incorrectly passed True. That instructs the method to release the memory for the old structure data. That's not correct, the GetPrinter() winapi function does notwant you to release the string pointers. You got away with this on older version of Windows, like XP. But starting at Vista, and including Windows 7, the heap manager no longer ignores bad pointer values.
这是因为最后一个参数,你错误地传递了 True。这指示方法释放旧结构数据的内存。这是不正确的,该GETPRINTER()函数WINAPI也不会要你释放字符串指针。您在旧版本的 Windows 上(例如 XP)逃脱了这个问题。但是从 Vista 开始,包括 Windows 7,堆管理器不再忽略错误的指针值。
Change the 3rd argument to False to solve your problem.
将第三个参数更改为 False 以解决您的问题。

