2012-06-20 39 views
7

Điều này có vẻ như một nhu cầu đơn giản như vậy, nhưng vì một lý do nào đó tôi không thể tìm thấy cách tôi có thể thực hiện việc này. Tôi có mã như sau:Cách in một docx đến một máy in cụ thể bằng cách sử dụng 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); 

Tôi cần phải có ổ đĩa cài đặt cấu hình máy in và khay được sử dụng. Sau khi tìm kiếm xung quanh, tôi tìm thấy Microsoft.Office.Interop.Word.Application.ActivePrinter là một thuộc tính chuỗi có thể cài đặt mà tài liệu nói có "tên của máy in đang hoạt động", nhưng tôi không biết ý nghĩa của nó đối với máy in "Active Printer", đặc biệt là khi tôi có hai người trong số họ. Làm thế nào điều này có thể được thực hiện?

Trả lời

12

TL; DR Bạn không thể in tới máy in cụ thể. Bạn phải thay đổi máy in mặc định thành những gì bạn muốn sử dụng. Sau đó in như bình thường.

Có thể mọi thứ đã thay đổi đáng kể kể từ lần đầu tiên chúng tôi làm việc trong lĩnh vực này, nhưng chúng tôi không thể tìm thấy bất kỳ cách nào để in ra một máy in cụ thể. Vì vậy, những gì chúng tôi đã làm thay cho máy in mặc định của hệ thống theo những gì chúng tôi muốn, in tất cả các tài liệu mà chúng tôi muốn trên máy tính đó, sau đó thay đổi nó trở về trước đó. (trên thực tế, chúng tôi từ bỏ thay đổi nó trở lại bởi vì không có gì khác đang mong đợi bất kỳ máy in mặc định cụ thể, do đó, nó không quan trọng).

Câu trả lời ngắn:

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 */); 

Nhưng, chúng ta thấy điều này rất không đáng tin cậy! Đọc tiếp để biết thêm chi tiết:

Nếu bạn chưa làm như vậy, tôi khuyên bạn nên xây dựng các lớp trình bao bọc của riêng bạn để xử lý tất cả các bit công việc nhàm chán của _Document_Application. Nó có thể không tồi tệ như bây giờ (dynamic không phải là một lựa chọn cho chúng tôi), nhưng nó vẫn là một ý tưởng tốt. Bạn sẽ lưu ý rằng một số bit ngon nhất của mã là vắng mặt từ này ... Tôi đã cố gắng tập trung vào mã có liên quan đến những gì bạn đang yêu cầu. Ngoài ra, lớp DocWrapper này là sự kết hợp của nhiều phần riêng biệt của mã - tha thứ cho sự rối loạn này. Cuối cùng, nếu bạn xem xét việc xử lý ngoại lệ lẻ (hoặc thiết kế kém bằng cách ném ngoại lệ) - hãy nhớ tôi đang cố gắng gộp các phần mã từ nhiều nơi (trong khi cũng loại bỏ các loại tùy chỉnh của riêng mình). Đọc các ý kiến ​​trong mã, họ quan trọng.

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

thú vị, tốt nếu không có cách nào thì cứ như vậy, hoán đổi nó qua lại là có thể thực hiện được. Điều gì về việc xác định nguồn giấy? Ngoài ra, tại sao lại ngủ? Không có cách nào để chắc chắn rằng việc thực thi có thể tiếp tục an toàn không? Điều gì xảy ra nếu bạn xóa chúng? – andrew

+0

Nguồn giấy - Tôi cần xem xét điều đó (kiểm tra lại sau). Thời gian chờ? Bởi vì nó không phải là một cuộc gọi chặn (đủ) và không xảy ra/cập nhật tự động. – payo

+0

Để chỉ định khay giấy, nó phải được thực hiện trong tệp docx (có nghĩa là chèn nó "bằng tay" bằng cách nào đó, tốt nhất là sử dụng API đóng gói để lấy tệp document.xml và tìm kiếm các lệnh khay giấy) - bằng cách lưu một tài liệu Word mà họ đã thiết lập và chỉ áp dụng cho một máy in cụ thể). Tôi muốn khuyên bạn nên làm nó bằng tay thông qua từ, sau đó kiểm tra của # sau đó. – payo

1

Có một cách để xác định máy in, nhưng không đặt nó như là mặc định hệ thống (tôi sử dụng C++/CLR, nhưng nó phải là cầm tay để 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

cần lưu ý, rằng phương pháp này sử dụng đối tượng WordBasic kế thừa cho phép không đặt máy in mặc định của hệ thống.Đó là lợi thế chính khi sử dụng thuộc tính 'Active.Application'' ActivePrinter'. – paulroho

Các vấn đề liên quan