如何使用Microsoft.Office.Interop.Word.Document.PrintOut()将docx打印到特定的打印机

时间:2012-06-20 18:41:07

标签: c# ms-word office-interop

这似乎是一个简单的需求,但由于某种原因,我无法找到如何实现这一目标。我有这样的代码:

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

我需要一个配置设置驱动器,使用哪个打印机和托盘。在搜索之后我发现了Microsoft.Office.Interop.Word.Application.ActivePrinter这是一个可设置的字符串属性,文档中说的是“活动打印机的名称”,但我不知道这对打印机来说意味着什么“活动打印机”,特别是当我有两个时。如何实现这一目标?

3 个答案:

答案 0 :(得分:16)

TL; DR 您无法打印到特定的打印机。您必须将默认打印机更改为您要使用的打印机。然后照常打印。

自从我们第一次在这个领域开展工作以来,情况可能发生了巨大的变化,但我们无法找到任何打印到特定打印机的方法。因此,我们所做的就是将系统的默认打印机更改为我们想要的,打印我们想要的那台计算机上的所有文档,然后将其更改回之前的版本。 (实际上,我们退出了更改它,因为没有别的东西期待任何特定的默认打印机,所以没关系)。

简短回答:

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

但是,我们发现这非常不可靠!请继续阅读以获取更多详细信息:

如果你还没有这样做,我强烈建议你建立自己的包装类来处理_Document_Application的所有平凡工作。它现在可能没有那么糟糕(dynamic不是我们的选择),但它仍然是一个好主意。你会注意到这里没有一些美味的代码......我试着把注意力集中在与你所要求的相关的代码上。此外,这个DocWrapper类是代码的许多独立部分的合并 - 原谅这种混乱。最后,如果你认为异常处理是奇怪的(或者通过抛出异常只是糟糕的设计) - 记住我正在尝试将来自许多地方的代码部分放在一起(同时也省略了我们自己的自定义类型)。阅读代码中的注释,它们很重要。

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;
  }
}

答案 1 :(得分:1)

有一种方法可以指定打印机,但不要将其设置为系统默认值(我使用C ++ / CLR,但它应该可以移植到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);
}

答案 2 :(得分:0)

我继承了一个使用Word.Application的C#项目。由于它是一个C#项目,并且翻译似乎非常简单,因此我使用了@Lars C ++代码,并将C#转换后的方法添加到了我的项目中。在此发布他的代码的直接翻译,以期使某人的生活变得更轻松:

string printername = "...";
var wordapp = new Microsoft.Office.Interop.Word.Application();
if (wordapp != null)
{
    object[] argValues = new object[2];
    argValues[0] = printername;
    argValues[1] = 1;
    string[] argNames = new string[2];
    argNames[0] = "Printer";
    argNames[1] = "DoNotSetAsSysDefault";
    var wb = wordapp.WordBasic;
    wb.GetType().InvokeMember("FilePrintSetup", System.Reflection.BindingFlags.InvokeMethod, null, wb, argValues, null, null, argNames);
}