2010-03-13 17 views
7

EDITAR: ¡Mi error! Esperé que los cambios se volvieran a escribir en la configuración predeterminada de la impresora cuando, de hecho, solo se modificó la instancia local de PrinterSettings. - El siguiente código parece funcionar como se pretendía¿Cómo se muestran las propiedades de la impresora/diálogo de preferencias y se guardan los cambios?

Estoy tratando de mostrar las propiedades de impresora personalizadas de una impresora determinada. Necesito esto como parte de un PrintDialog personalizado que estoy tratando de escribir.

La mayoría de los ejemplos que puedo encontrar en línea logran mostrar el cuadro de diálogo, pero se pierden todos los cambios que pueda hacer el usuario, lo que lo hace inútil.

Ejemplo: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx

(con respecto a la página anterior: He intentado cambiar el código como lo sugiere BartJoy (en la página), pero que no se ha solucionado el problema)

También probé la muestra y sugerencias de la página pinvoke.net pero todavía no funciona:

http://www.pinvoke.net/default.aspx/winspool.documentproperties

de los sitios web anteriores que suponer que el problema sólo puede estar en un Windows de 64 bits nd/o si el nombre de una impresora tiene más de 32 caracteres.

No sé qué debo intentar a continuación ... ¡Agradezco cualquier sugerencia y comentario!

EDIT: Esto es lo que he intentado:

[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, 
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 
static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, 
     [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, 
     IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode); 

[DllImport("winspool.drv")] 
private static extern int OpenPrinter(string pPrinterName, out IntPtr hPrinter, IntPtr pDefault); 
[DllImport("winspool.drv")] 
private static extern int ClosePrinter(IntPtr phPrinter); 

[DllImport("kernel32.dll")] 
static extern IntPtr GlobalLock(IntPtr hMem); 
[DllImport("kernel32.dll")] 
static extern bool GlobalUnlock(IntPtr hMem); 
[DllImport("kernel32.dll")] 
static extern bool GlobalFree(IntPtr hMem); 

private const int DM_PROMPT = 4; 
private const int DM_OUT_BUFFER = 2; 
private const int DM_IN_BUFFER = 8; 

private void OpenPrinterPropertiesDialog() 
{ 
    var printerSettings = new System.Drawing.Printing.PrinterSettings(); 
    var printerName = printerSettings.PrinterName; 

    IntPtr handle; 
    OpenPrinter(printerName, out handle, IntPtr.Zero); 

    IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); 
    IntPtr pDevMode = GlobalLock(hDevMode); 
    int sizeNeeded = DocumentProperties(this.Handle, handle, printerName, pDevMode, pDevMode, 0); 
    IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); 
    DocumentProperties(this.Handle, handle, printerName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER); 

    ClosePrinter(handle); 
    GlobalUnlock(hDevMode); 

    printerSettings.SetHdevmode(devModeData); 
    printerSettings.DefaultPageSettings.SetHdevmode(devModeData); 

    GlobalFree(hDevMode); 
    Marshal.FreeHGlobal(devModeData); 
} 

He intentado utilizar el método OpenPrinter y ClosePrinter y pasar el devModeData como el parámetro de salida en la segunda convocatoria ya que me pareció extraño que el original el código de pinvoke.net no hizo esto. (pero admito que no sé lo que estoy haciendo, esto es solo prueba y error).

Aquí es el código original del sitio PInvoke:

private void OpenPrinterPropertiesDialog(PrinterSettings printerSettings) 
{ 
    IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); 
    IntPtr pDevMode = GlobalLock(hDevMode); 
    int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0); 
    IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); 
    DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, IntPtr.Zero, pDevMode, 14); 
    GlobalUnlock(hDevMode); 
    printerSettings.SetHdevmode(devModeData); 
    printerSettings.DefaultPageSettings.SetHdevmode(devModeData); 
    GlobalFree(hDevMode); 
    Marshal.FreeHGlobal(devModeData); 
} 
+0

Y ¿cómo ha logrado obtener los chages guardan la espalda? Este código cambia las configuraciones de impresora, pero los cambios no se guardan como la configuración predeterminada de las impresoras :( – Ando

Respuesta

3
  • cuando su aplicación se puso en marcha:
    • tienen consultada el controlador de impresora para el tamaño correcto de la estructura DEVMODE antes de asignar ¿eso?
    • ¿Ha pedido al controlador del dispositivo que inicialice el almacenamiento intermedio DEVMODE con la configuración predeterminada después de haberlo asignado?
  • cuando su solicitud apareció el diálogo de la impresora:
    • se han establecido las banderas DM_IN_BUFFER y DM_OUT_BUFFER (además de DM_IN_PROMPT) en el parámetro fMode a DocumentProperties?
    • ¿Ha señalado pDevModeInput y pDevModeOutput al buffer DEVMODE que inicializó al inicio de la aplicación?
    • son los dmFields bits en la memoria intermedia DEVMODE adecuadamente ajustado antes de su llamada DocumentProperties(... DM_IN_PROMPT ...)
    • estás preservando el contenido de la memoria intermedia en DEVMODE entre llamadas a DocumentProperties(... DM_IN_PROMPT ...)?

Ver:

+0

gracias por la entrada. Creo que hago estas cosas. He actualizado la pregunta e incluido el código que he probado. –

+0

mi error ! Esperaba que los cambios se volvieran a escribir en la configuración predeterminada de la impresora para que cuando llame al mismo método con una nueva PrinterSettings() refleje los cambios anteriores. - Parece que todo está funcionando correctamente a medida que se actualizan las printerSettings correctamente. –

7

A pesar de que la respuesta terminó trabajando su camino en la pregunta, creo que a continuación se ofrece una mejor respuesta a la pregunta original,

(1) Debido a que claramente no modifica el PrinterSettings pasado si el usuario cancela.

(2) Debido a que devuelve un DialogResult, que la persona que llama es probable estar interesado en

[DllImport("kernel32.dll")] 
static extern IntPtr GlobalLock(IntPtr hMem); 
[DllImport("kernel32.dll")] 
static extern bool GlobalUnlock(IntPtr hMem); 
[DllImport("kernel32.dll")] 
static extern bool GlobalFree(IntPtr hMem); 
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] 
static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode); 

private const int DM_PROMPT = 4; 
private const int DM_OUT_BUFFER = 2; 
private const int DM_IN_BUFFER = 8; 

private DialogResult EditPrinterSettings(PrinterSettings printerSettings) 
{ 
    DialogResult myReturnValue = DialogResult.Cancel; 
    IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); 
    IntPtr pDevMode = GlobalLock(hDevMode); 
    int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0); 
    IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); 
    long userChoice = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER); 
    long IDOK = (long)DialogResult.OK; 
    if (userChoice == IDOK) 
    { 
     myReturnValue = DialogResult.OK; 
     printerSettings.SetHdevmode(devModeData); 
     printerSettings.DefaultPageSettings.SetHdevmode(devModeData); 
    } 
    GlobalUnlock(hDevMode); 
    GlobalFree(hDevMode); 
    Marshal.FreeHGlobal(devModeData); 
    return myReturnValue; 
} 
+0

@JeffRow Acabo de probar el código que proporcionó en una máquina win8 de 64 bits y DocumentProperties devolvió -1 y por lo tanto cuando Marshal.Se llama a AllocHGlobal (sizeNeeded), se emite un error "Memoria insuficiente para continuar la ejecución del programa". lo cual tiene sentido ya que el tamaño Necesitado es -1. – Thierry

+1

Si bien es una etapa inicial, este parece ser el código más estable que he encontrado hasta ahora para resolver mi problema. El único pequeño cambio basado en mi comentario anterior es que para obtener el tamaño Necesario, necesita cambiar la llamada API para usar un IntPtr.Zero en lugar de un 0, es decir, Dim sizeNeeded As Integer = DocumentProperties (Me.Handle, IntPtr.Zero , printerSettings.PrinterName, IntPtr.Zero, pDevMode, 0). ¡Gracias por compartir! – Thierry

7

Si se dirige a la compilación x 86 y se ejecuta desde una máquina de 64 bits, el código de Jeff Roe no funcionará.: en la asignación de devModeData, DocumentPropreties siempre se producirá un error y devuelve un sizeNeeded de -1, con un código de LastError 13.

para resolver el problema, asegúrese de que se dirigen a Cualquier CPU o simplemente cambiar la llamada a DocumentPropreties a la siguiente:

int sizeNeeded = DocumentProperties(pHandle, 
            IntPtr.Zero, 
            printerSettings.PrinterName, 
            IntPtr.Zero, // This solves it 
            pDevMode, 
            fMode); 

Usando IntPtr.Zero en lugar de un puntero adecuado a una estructura DEVMODE se ve mal, pero esa primera llamada a DocumentProperties no intente modificar la memoria en esa posición. La única información devuelta por la llamada es el tamaño de memoria necesario para almacenar los datos del modo del dispositivo que representan los parámetros internos del controlador de impresión.

Referencia:

Cuestiones relacionadas