2012-06-20 23 views
7

Esto parece una necesidad tan simple, pero por alguna razón no puedo encontrar cómo puedo lograr esto. Tengo un código como este:Cómo imprimir un docx a una impresora específica usando Microsoft.Office.Interop.Word.Document.PrintOut()

Microsoft.Office.Interop.Word.Application word = new Microsoft.Office.Interop.Word.Application(); 
MemoryStream documentStream = getDocStream(); 
FileInfo wordFile = new FileInfo("c:\\test.docx"); 
object fileObject = wordFile.FullName; 
object oMissing = System.Reflection.Missing.Value; 
Microsoft.Office.Interop.Word.Document doc = wordInstance.Documents.Open(ref fileObject, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing); 
doc.Activate(); 
doc.PrintOut(oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing); 

Necesito tener un controlador de configuración que impresora y bandeja se utilizan. Después de buscar encontré Microsoft.Office.Interop.Word.Application.ActivePrinter que es una propiedad de cadena configurable que según la documentación lleva "el nombre de la impresora activa", pero no sé lo que significa para una impresora para el la "Impresora activa", especialmente cuando tengo dos de ellos. ¿Cómo se puede lograr esto?

Respuesta

12

TL; DR No se puede imprimir en una impresora específica. Debe cambiar la impresora predeterminada a la que desea usar. Luego imprime como siempre.

Es posible que las cosas hayan cambiado drásticamente desde que hicimos nuestro trabajo por primera vez en esta área, pero no pudimos encontrar ninguna forma de imprimir en una impresora específica. Entonces, lo que hicimos en lugar de eso fue cambiar la impresora predeterminada del sistema a lo que queríamos, imprimir todos los documentos que queríamos en esa computadora, y luego volver a cambiarlos a los que tenía antes. (en realidad, dejamos de cambiarlo porque nada más esperaba una impresora predeterminada en particular, por lo que no importaba).

Respuesta corta:

Microsoft.Office.Interop.Word._Application _app = [some valid COM instance]; 
Microsoft.Office.Interop.Word.Document doc = _app.Documents.Open(ref fileName, ...); 
doc.Application.ActivePrinter = "name of printer"; 
doc.PrintOut(/* ref options */); 

embargo, nos pareció que era muy poco fiable! Siga leyendo para conocer más detalles:

Si no lo ha hecho ya, en gran medida sugerir la construcción de sus propias clases de envoltura para hacer frente a todos los bits de trabajo y mundanos de _Document_Application. Puede que ahora no sea tan malo como lo fue entonces (dynamic no era una opción para nosotros), pero sigue siendo una buena idea. Notarás que ciertos pedazos deliciosos de código están ausentes de esto ... Traté de enfocarme en el código que está relacionado con lo que estás preguntando. Además, esta clase DocWrapper es una combinación de muchas partes separadas del código: perdón el desorden. Por último, si consideras que el manejo de excepciones es impar (o solo un diseño pobre arrojando Exception), recuerda que estoy tratando de juntar partes de código de muchos lugares (dejando de lado también nuestros propios tipos personalizados). Lea los comentarios en el código, ellos importan.

class DocWrapper 
{ 
    private const int _exceptionLimit = 4; 

    // should be a singleton instance of wrapper for Word 
    // the code below assumes this was set beforehand 
    // (e.g. from another helper method) 
    private static Microsoft.Office.Interop.Word._Application _app; 

    public virtual void PrintToSpecificPrinter(string fileName, string printer) 
    { 
    // Sometimes Word fails, so needs to be restarted. 
    // Sometimes it's not Word's fault. 
    // Either way, having this in a retry-loop is more robust. 
    for (int retry = 0; retry < _exceptionLimit; retry++) 
    { 
     if (TryOncePrintToSpecificPrinter(fileName, printer)) 
     break; 

     if (retry == _exceptionLimit - 1) // this was our last chance 
     { 
     // if it didn't have actual exceptions, but was not able to change the printer, we should notify somebody: 
     throw new Exception("Failed to change printer."); 
     } 
    } 
    } 

    private bool TryOncePrintToSpecificPrinter(string fileName, string printer) 
    { 
    Microsoft.Office.Interop.Word.Document doc = null; 

    try 
    { 
     doc = OpenDocument(fileName); 

     if (!SetActivePrinter(doc, printer)) 
     return false; 

     Print(doc); 

     return true; // we did what we wanted to do here 
    } 
    catch (Exception e) 
    { 
     if (retry == _exceptionLimit) 
     { 
     throw new Exception("Word printing failed.", e); 
     } 
     // restart Word, remembering to keep an appropriate delay between Quit and Start. 
     // this should really be handled by wrapper classes 
    } 
    finally 
    { 
     if (doc != null) 
     { 
     // release your doc (COM) object and do whatever other cleanup you need 
     } 
    } 

    return false; 
    } 

    private void Print(Microsoft.Office.Interop.Word.Document doc) 
    { 
    // do the actual printing: 
    doc.Activate(); 
    Thread.Sleep(TimeSpan.FromSeconds(1)); // emperical testing found this to be sufficient for our system 
    // (a delay may not be required for you if you are printing only one document at a time) 
    doc.PrintOut(/* ref objects */); 
    } 

    private bool SetActivePrinter(Microsoft.Office.Interop.Word.Document doc, string printer) 
    { 
    string oldPrinter = GetActivePrinter(doc); // save this if you want to preserve the existing "default" 

    if (printer == null) 
     return false; 

    if (oldPrinter != printer) 
    { 
     // conditionally change the default printer ... 
     // we found it inefficient to change the default printer if we don't have to. YMMV. 
     doc.Application.ActivePrinter = printer; 
     Thread.Sleep(TimeSpan.FromSeconds(5)); // emperical testing found this to be sufficient for our system 
     if (GetActivePrinter(doc) != printer) 
     { 
     // don't quit-and-restart Word, this one actually isn't Word's fault -- just try again 
     return false; 
     } 

     // successful printer switch! (as near as anyone can tell) 
    } 

    return true; 
    } 

    private Microsoft.Office.Interop.Word.Document OpenDocument(string fileName) 
    { 
    return _app.Documents.Open(ref fileName, /* other refs */); 
    } 

    private string GetActivePrinter(Microsoft.Office.Interop.Word._Document doc) 
    { 
    string activePrinter = doc.Application.ActivePrinter; 
    int onIndex = activePrinter.LastIndexOf(" on "); 
    if (onIndex >= 0) 
    { 
     activePrinter = activePrinter.Substring(0, onIndex); 
    } 
    return activePrinter; 
    } 
} 
+0

interesante, bien si no hay manera entonces que así sea, cambiarlo de ida y vuelta es factible. ¿Qué hay de especificar la fuente de papel? Además, ¿por qué dormir? ¿No hay forma de estar seguro de que la ejecución puede continuar de forma segura? ¿Qué pasa si los quitas? – andrew

+0

Fuente de papel: necesito ver eso (verifique más tarde). El tiempo de espera? Porque no es una llamada (suficientemente) bloqueada y no ocurre/se actualiza automáticamente. – payo

+0

Para especificar bandejas de papel, tiene que hacerse en el archivo docx (lo que significa insertarlo "a mano" de alguna manera, preferiblemente utilizando las API de empaquetado para obtener el archivo document.xml y buscar los comandos de la bandeja de papel) - que se obtiene guardando un documento de Word que los tiene configurados, y se aplica solo a una impresora específica). Recomiendo hacerlo manualmente a través de palabras, luego inspeccionar los # después. – payo

1

Hay una forma de especificar la impresora, pero no la ponga como por defecto del sistema (utilizo C++/CLR, pero debe ser portátil a C#):

String^ printername = "..."; 
auto wordapp= gcnew Microsoft::Office::Interop::Word::Application(); 
if (wordapp != nullptr) 
{ 
    cli::array<Object^>^ argValues= gcnew cli::array<Object^>(2); 
    argValues[0]= printername; 
    argValues[1]= 1; 
    cli::array<String^>^ argNames= gcnew cli::array<String^>(2); 
    argNames[0]= "Printer"; 
    argNames[1]= "DoNotSetAsSysDefault"; 
    Object^ wb= wordapp->WordBasic; 
    wb->GetType()->InvokeMember("FilePrintSetup", System::Reflection::BindingFlags::InvokeMethod, nullptr, wb, argValues, nullptr, nullptr, argNames); 
} 
+0

It Se debe tener en cuenta que este método que utiliza el objeto heredado de WordBasic permite no configurar la impresora predeterminada del sistema.Esa es la principal ventaja sobre el uso de la propiedad 'ActivePrinter' de Word.Application'. – paulroho

Cuestiones relacionadas