2013-08-26 4 views
0

У меня есть серьезные проблемы для доступа и изменения настроек принтера на окнах 7.Доступ к настройкам принтера (DEVMODE) на окнах 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 

Использование:

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: И, вероятно, последняя ошибка остается при вызове SetPrinter API.

gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0)) 

Это бросает ошибку: "Драйвер принтера неизвестен", но

gNRet = Convert.ToInt16(SetPrinter(gPrinter, 0, gPtrPrinterInfo, 3)) 

работа без ошибок, но ненадежно.

Как избавиться от этой последней ошибки?

+0

Где (и как) объявлены 'gPrinter' и' gPrinterValues'? Почему вы объявляете 'lDevMode', но не используете его нигде? –

+0

Я думаю, это не важно здесь, так как класс большой, и это всего лишь критическая часть. Если вы думаете, что я могу опубликовать большой класс, и если это может быть полезно, я могу это сделать. –

+1

Вам необходимо опубликовать код, имеющий отношение к вашему вопросу. Поскольку проблема связана с вызовом OpenPrinter, вам необходимо предоставить весь соответствующий код для этого вызова.Вы не указали два из трех параметров, которые вы используете, которые, вероятно, * точно *, что вызывает проблему. –

ответ

2

Вот некоторые указатели:

  • Проверить и посмотреть, если ваши и ваши Структуры функции DllImport определены правильно. на pinvoke.net вы можете найти тестируемые подписи (пример функции DocumentProperties) http://www.pinvoke.net/default.aspx/winspool/DocumentProperties.html
  • Если вы используете систему x64, обратите внимание на комментарии на pinvoke.net. Некоторые функции/структуры необходимо изменить в системах x64.
  • Вы используете свою программу/визуальную студию в качестве администратора или обычного пользователя? Это может иметь значение.

FYI Я получил функцию GetPrinterSettings, работающую на C# на Win7 x64 без каких-либо логических изменений кода. OpenPrinter не сработал, но возвратил ошибку № 87 до того, как я изменил ее подпись функции: gPrinter должен был стать параметром out. Я думаю, вы должны быть в состоянии выполнить те же проверки и заставить работать в 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; 
      } 
     } 
    } 

} 
+0

Hi HB. Я хотел бы запустить этот код без административных привилегий как обычный пользователь. Я также использую Win7/64, и я бы хотел, чтобы этот код работал на 32 и 64-битных системах. Пожалуйста, напишите свой код на C#. Я могу легко преобразовать его. –

+0

Я добавил класс C# к ответу. –

+0

Я не могу получить лучшие результаты, меняя подписи с pinvoke.net. Тем не менее, ваш код значительно меня продвигает вперед, и проблемы остаются еще незначительными. В соответствии с вашим примером кода я должен изменить свой код в вопросе, надеюсь, что это правильно для вас. Теперь только обновление данных DEVMODE в функции SetPrinterSettings является проблемой для получения функционального класса, и я надеюсь, что вы тоже можете помочь здесь. Кроме того, функция SetPrinter API была непроверена. Кстати, я делаю все члены DEVMODE public, чтобы иметь возможность устанавливать его извне. Диалог настроек не требуется, поэтому я делаю это необязательным. Я добавил пример использования. –

2
Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, True) 

Это завершит программу с кодом исключения 0xc0000374, вы можете увидеть его в окне Output. Это STATUS_HEAP_CORRUPTION, «Куча повреждена».

Это произошло из-за последнего аргумента, вы неправильно передали True. Это инструктирует метод освободить память для старых данных структуры. Это неверно, функция GetPrinter() winapi делает не хотите, чтобы вы освободили указатели строк. Вы избавились от этого в более старой версии Windows, например XP. Но начиная с Vista, включая Windows 7, диспетчер кучи больше не игнорирует значения плохого указателя.

Измените третий аргумент на False, чтобы решить вашу проблему.

+0

Привет, Ханс, вы (как всегда) правы. Это решено сейчас. Следующая проблема заключается в вызове функции SetPrinter API. Все найденные примеры используют показанный вызов, который вызывает ошибку: «Драйвер принтера неизвестен». Согласно документации MS я изменил эту строку на gNRet = Convert.ToInt16 (SetPrinter (gPrinter, 0, gPtrPrinterInfo, 3)), и теперь все работает как ожидалось. Правильно ли, что я оставляю эти параметры таким образом? –

Смежные вопросы