Печать с расширенными параметрами (выбор лотка, двусторонняя печать, сшивание)

У нас есть проект управления печатью документов. Сначала я удивляюсь, почему нельзя настроить параметры печати в одном месте. Например, выбор лотка принтера для первой и других страниц можно выполнить с помощью автоматизации MS Word:

var doc = _applicationObject.Documents.OpenNoRepairDialog(FileName: ref sourceFile, ReadOnly: ref readOnly,
                                                                 AddToRecentFiles: ref addToRecentFiles,
                                                                 Visible: ref visible);
doc.PageSetup.FirstPageTray = (WdPaperTray) firstPageTrayCode;
doc.PageSetup.OtherPagesTray = (WdPaperTray) otherPagesTrayCode;
_applicationObject.ActivePrinter = printerPath;
doc.Activate();
_applicationObject.PrintOut(Background: ref backgroundPrint, FileName: sourceFile);
doc.Close(ref saveChanges, ref _missing, ref _missing);

В приведенном выше коде лоток принтера указан как целое число, потому что у некоторых принтеров нет стандартных значений для лотков (у нас была эта проблема с HP - это коды лотков, описанные здесь). Итак, сначала мы извлекаем, какие лотки есть у принтера, используя код:

var setting = new PrinterSettings();
setting.PrinterName = myPrinterName;
foreach (PaperSource tray in setting.PaperSources)
{
    Console.WriteLine("\t{0}: #{1}", tray.SourceName, tray.RawKind);
}

И этот код работает без проблем.

Но здесь нет возможности указать параметры двусторонней печати и сшивания. Дуплекс можно сделать, используя функции драйвера OpenPrinter и SetPrinter, как описано здесь, а также рекомендуется Microsoft в эта ветка форума. Скрепка совершенно непонятна, и если кто-нибудь знает, кстати, как это реализовать, дайте мне знать. Использование Stapling enum, как в этой статье MSDN, бесполезно так как для печати требуется настраиваемый рендеринг документа.

Я описал ситуацию и как были реализованы детали. Это отлично работает в нашей среде: Windows Server 2008 R2, MS Office 2010 x32, принтеры HP LaserJet P2055 и Ricoh Nashuatec DSm635. Протестировано с использованием собственных и универсальных драйверов PCL6 / PCL5e: двусторонняя печать и выбор лотка работают должным образом.

Но после развертывания приложения на клиенте принтеры (HP LaserJet 4250 и Ricoh Aficio MP C7501) всегда выполняют печать из лотка по умолчанию и без двусторонней печати. Пробовал несколько разных драйверов с точно таким же результатом.

В обеих средах принтеры являются сетевыми принтерами. Поэтому, чтобы заставить их применить настройку дуплекса с помощью драйвера принтера, нам нужно было установить локальный драйвер на сервер и сделать локальный принтер, как рекомендовал мой Microsoft на эта ветка форума поддержки.

Хотя используемые среды и принтеры очень похожи, одно работает, а другое - нет. Любая помощь будет принята с благодарностью.


person Sasha    schedule 27.06.2012    source источник
comment
Взгляните на программное обеспечение Ricoh Print & Share. Там вы можете настроить очень сложные параметры, возможно, то, что вы хотите использовать.   -  person juFo    schedule 07.02.2013
comment
У меня была аналогичная проблема, и вот мой ответ на нее: stackoverflow.com/a/33239717/2559297   -  person D_Bester    schedule 20.10.2015


Ответы (1)


На случай, если кому-то это понадобится, я придумал обходной путь, основанный на сохранении блока памяти настроек принтера в двоичном файле с последующим его восстановлением. Идея описана в это сообщение в блоге, но оно не сработало для меня при простом копировании (оно работало только для некоторых драйверов и некоторых настроек, тогда как другие параметры печати были проигнорированы).

Поэтому я немного переделал его, чтобы теперь он мог поддерживать все настройки, которые я пробовал на любом принтере (с совместимым драйвером), который я тестировал. Конечно, если вы используете драйвер другого принтера, например, он не сработает.

Недостатком этого подхода, конечно же, является то, что вы должны сначала установить настройки принтера по умолчанию (в Панели управления) на то, что вам нужно. Конечно, это не всегда возможно, но, по крайней мере, в некоторых случаях это может помочь.

Итак, полный исходный код тестовой утилиты, которая может сохранять настройки принтера в файл, снова загружать этот файл в принтер и распечатывать документ, используя указанный файл настроек:

using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Office.Interop.Word;

namespace PrintAdvancedTest
{
    [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_DEFAULTS
    {
        public int pDatatype;
        public int pDevMode;
        public int DesiredAccess;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_INFO_2
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pServerName;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pPrinterName;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pShareName;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pPortName;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pDriverName;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pComment;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pLocation;
        public IntPtr pDevMode;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pSepFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pPrintProcessor;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pDatatype;
        [MarshalAs(UnmanagedType.LPStr)]
        public readonly string pParameters;
        public IntPtr pSecurityDescriptor;
        public readonly Int32 Attributes;
        public readonly Int32 Priority;
        public readonly Int32 DefaultPriority;
        public readonly Int32 StartTime;
        public readonly Int32 UntilTime;
        public readonly Int32 Status;
        public readonly Int32 cJobs;
        public readonly Int32 AveragePPM;
    }

    public class PrintSettings
    {
        private const short CCDEVICENAME = 32;
        private const short CCFORMNAME = 32;

        //Constants for DEVMODE
        // Constants for DocumentProperties
        private const int DM_MODIFY = 8;
        private const int DM_COPY = 2;
        private const int DM_IN_BUFFER = DM_MODIFY;
        private const int DM_OUT_BUFFER = DM_COPY;
        // const intants for dmOrientation
        private const int DMORIENT_PORTRAIT = 1;
        private const int DMORIENT_LANDSCAPE = 2;
        // const intants for dmPrintQuality
        private const int DMRES_DRAFT = (-1);
        private const int DMRES_HIGH = (-4);
        private const int DMRES_LOW = (-2);
        private const int DMRES_MEDIUM = (-3);
        // const intants for dmTTOption
        private const int DMTT_BITMAP = 1;
        private const int DMTT_DOWNLOAD = 2;
        private const int DMTT_DOWNLOAD_OUTLINE = 4;
        private const int DMTT_SUBDEV = 3;
        // const intants for dmColor
        private const int DMCOLOR_COLOR = 2;
        private const int DMCOLOR_MONOCHROME = 1;
        // const intants for dmCollate
        private const int DMCOLLATE_FALSE = 0;
        private const int DMCOLLATE_TRUE = 1;
        // const intants for dmDuplex
        private const int DMDUP_HORIZONTAL = 3;
        private const int DMDUP_SIMPLEX = 1;
        private const int DMDUP_VERTICAL = 2;

        //const for security access
        private const int PRINTER_ACCESS_ADMINISTER = 0x4;
        private const int PRINTER_ACCESS_USE = 0x8;
        private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;

        private const int PRINTER_ALL_ACCESS =
            (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER
             | PRINTER_ACCESS_USE);


        /* field selection bits */
        private const int DM_ORIENTATION = 0x00000001;
        private const int DM_PAPERSIZE = 0x00000002;
        private const int DM_PAPERLENGTH = 0x00000004;
        private const int DM_PAPERWIDTH = 0x00000008;
        private const int DM_SCALE = 0x00000010;
        private const int DM_POSITION = 0x00000020;
        private const int DM_NUP = 0x00000040;
        private const int DM_DISPLAYORIENTATION = 0x00000080;
        private const int DM_COPIES = 0x00000100;
        private const int DM_DEFAULTSOURCE = 0x00000200;
        private const int DM_PRINTQUALITY = 0x00000400;
        private const int DM_COLOR = 0x00000800;
        private const int DM_DUPLEX = 0x00001000;
        private const int DM_YRESOLUTION = 0x00002000;
        private const int DM_TTOPTION = 0x00004000;
        private const int DM_COLLATE = 0x00008000;
        private const int DM_FORMNAME = 0x00010000;
        private const int DM_LOGPIXELS = 0x00020000;
        private const int DM_BITSPERPEL = 0x00040000;
        private const int DM_PELSWIDTH = 0x00080000;
        private const int DM_PELSHEIGHT = 0x00100000;
        private const int DM_DISPLAYFLAGS = 0x00200000;
        private const int DM_DISPLAYFREQUENCY = 0x00400000;
        private const int DM_ICMMETHOD = 0x00800000;
        private const int DM_ICMINTENT = 0x01000000;
        private const int DM_MEDIATYPE = 0x02000000;
        private const int DM_DITHERTYPE = 0x04000000;
        private const int DM_PANNINGWIDTH = 0x08000000;
        private const int DM_PANNINGHEIGHT = 0x10000000;
        private const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;


        [StructLayout(LayoutKind.Sequential)]
        public struct DEVMODE
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
            public string dmDeviceName;
            public short dmSpecVersion;
            public short dmDriverVersion;
            public short dmSize;
            public short dmDriverExtra;
            public int dmFields;
            public short dmOrientation;
            public short dmPaperSize;
            public short dmPaperLength;
            public short dmPaperWidth;
            public short dmScale;
            public short dmCopies;
            public short dmDefaultSource;
            public short dmPrintQuality;
            public short dmColor;
            public short dmDuplex;
            public short dmYResolution;
            public short dmTTOption;
            public short dmCollate;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
            public string dmFormName;
            public short dmUnusedPadding;
            public short dmBitsPerPel;
            public int dmPelsWidth;
            public int dmPelsHeight;
            public int dmDisplayFlags;
            public int dmDisplayFrequency;
        }

        static void Main(string[] args)
        {
            Dictionary<string, Action> commands = new Dictionary<string, Action>
                                                      {
                                                          {"save", PrinterPreferencesSave},
                                                          {"print", PrinterPreferencesPrint},
                                                          {"set", PrinterPreferencesSet},
                                                          {"info", PrinterInfo}
                                                      };

            while (true)
            {
                Console.Write("Command ({0}): ", string.Join(", ", commands.Keys));
                string command = Console.ReadLine();
                Action action;
                if (!commands.TryGetValue(command, out action))
                {
                    Console.WriteLine("Invalid command");
                }
                else
                {
                    action();
                }
            }
        }

        static void PrinterPreferencesSave()
        {
            Console.Write("Printer name: ");
            string printerName = Console.ReadLine();
            Console.Write("Settings file path format: ");
            string SettingsFileNameFormat = Console.ReadLine();
            string testName;

            while (true)
            {
                Console.Write("SAVE: Settings set name: ");
                testName = Console.ReadLine();
                if (testName == "end")
                {
                    break;
                }
                getDevMode(printerName, string.Format(SettingsFileNameFormat, testName));
            }
        }

        static void PrinterPreferencesPrint()
        {
            Console.Write("Printer name: ");
            string printerName = Console.ReadLine();
            Console.Write("Settings file path format: ");
            string SettingsFileNameFormat = Console.ReadLine();
            Console.Write("Document to print: ");
            string docToPrintPath = Console.ReadLine();

            string testName;
            while (true)
            {
                Console.Write("PRINT: Settings set name: ");
                testName = Console.ReadLine();
                if (testName == "end")
                {
                    break;
                }
                string filePath = string.Format(SettingsFileNameFormat, testName);
                if (!File.Exists(filePath))
                {
                    Console.WriteLine("File {0} not exists", filePath);
                    return;
                }
                var success = setDevMode(printerName, filePath);
                if (success)
                {
                    PrintWordDocument(docToPrintPath, printerName);
                }
            }
        }

        static void PrinterPreferencesSet()
        {
            Console.Write("Printer name: ");
            string printerName = Console.ReadLine();
            Console.Write("Settings file path format: ");
            string SettingsFileNameFormat = Console.ReadLine();

            string testName;
            while (true)
            {
                Console.Write("SET: Settings set name: ");
                testName = Console.ReadLine();
                if (testName == "end")
                {
                    break;
                }
                string filePath = string.Format(SettingsFileNameFormat, testName);
                if (!File.Exists(filePath))
                {
                    Console.WriteLine("File {0} not exists", filePath);
                    return;
                }
                var success = setDevMode(printerName, filePath);
                if(!success)
                {
                    Console.WriteLine("Failed");
                }
            }
        }

        private static void PrinterInfo()
        {
            Console.Write("Printer name: ");
            string printerName = Console.ReadLine();

            IntPtr hDevMode;                        // handle to the DEVMODE
            IntPtr pDevMode;                        // pointer to the DEVMODE
            DEVMODE devMode;                        // the actual DEVMODE structure


            //var printController = new StandardPrintController();
            PrinterSettings printerSettings = new PrinterSettings();
            printerSettings.PrinterName = printerName;


            // Get a handle to a DEVMODE for the default printer settings
            hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);

            // Obtain a lock on the handle and get an actual pointer so Windows won't
            // move it around while we're futzing with it
            pDevMode = GlobalLock(hDevMode);

            // Marshal the memory at that pointer into our P/Invoke version of DEVMODE
            devMode = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE));

            Dictionary<string, int> dmConstants = new Dictionary<string, int>
                                                      {
                                                          {"DM_ORIENTATION", 0x00000001},
                                                          {"DM_PAPERSIZE", 0x00000002},
                                                          {"DM_PAPERLENGTH", 0x00000004},
                                                          {"DM_PAPERWIDTH", 0x00000008},
                                                          {"DM_SCALE", 0x00000010},
                                                          {"DM_POSITION", 0x00000020},
                                                          {"DM_NUP", 0x00000040},
                                                          {"DM_DISPLAYORIENTATION", 0x00000080},
                                                          {"DM_COPIES", 0x00000100},
                                                          {"DM_DEFAULTSOURCE", 0x00000200},
                                                          {"DM_PRINTQUALITY", 0x00000400},
                                                          {"DM_COLOR", 0x00000800},
                                                          {"DM_DUPLEX", 0x00001000},
                                                          {"DM_YRESOLUTION", 0x00002000},
                                                          {"DM_TTOPTION", 0x00004000},
                                                          {"DM_COLLATE", 0x00008000},
                                                          {"DM_FORMNAME", 0x00010000},
                                                          {"DM_LOGPIXELS", 0x00020000},
                                                          {"DM_BITSPERPEL", 0x00040000},
                                                          {"DM_PELSWIDTH", 0x00080000},
                                                          {"DM_PELSHEIGHT", 0x00100000},
                                                          {"DM_DISPLAYFLAGS", 0x00200000},
                                                          {"DM_DISPLAYFREQUENCY", 0x00400000},
                                                          {"DM_ICMMETHOD", 0x00800000},
                                                          {"DM_ICMINTENT", 0x01000000},
                                                          {"DM_MEDIATYPE", 0x02000000},
                                                          {"DM_DITHERTYPE", 0x04000000},
                                                          {"DM_PANNINGWIDTH", 0x08000000},
                                                          {"DM_PANNINGHEIGHT", 0x10000000},
                                                          {"DM_DISPLAYFIXEDOUTPUT", 0x20000000},
                                                      };
            Console.WriteLine("Allow set: {0}. Details: {1}", Convert.ToString(devMode.dmFields, 16), string.Join(",", dmConstants.Where(c=>(devMode.dmFields & c.Value)==c.Value).Select(c=>c.Key)));

            //private const int DM_POSITION = 0x00000020;
            //private const int DM_NUP = 0x00000040;
            //private const int DM_DISPLAYORIENTATION = 0x00000080;
            //private const int DM_DEFAULTSOURCE = 0x00000200;
            //private const int DM_PRINTQUALITY = 0x00000400;
            //private const int DM_COLOR = 0x00000800;
            //private const int DM_YRESOLUTION = 0x00002000;
            //private const int DM_TTOPTION = 0x00004000;
            //private const int DM_FORMNAME = 0x00010000;
            //private const int DM_LOGPIXELS = 0x00020000;
            //private const int DM_BITSPERPEL = 0x00040000;
            //private const int DM_PELSWIDTH = 0x00080000;
            //private const int DM_PELSHEIGHT = 0x00100000;
            //private const int DM_DISPLAYFLAGS = 0x00200000;
            //private const int DM_DISPLAYFREQUENCY = 0x00400000;
            //private const int DM_ICMMETHOD = 0x00800000;
            //private const int DM_ICMINTENT = 0x01000000;
            //private const int DM_MEDIATYPE = 0x02000000;
            //private const int DM_DITHERTYPE = 0x04000000;
            //private const int DM_PANNINGWIDTH = 0x08000000;
            //private const int DM_PANNINGHEIGHT = 0x10000000;
            //private const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;

            WriteDevModePropertyInfo("DeviceName", devMode.dmDeviceName, null);
            WriteDevModePropertyInfo("SpecVersion", devMode.dmSpecVersion.ToString(), null);
            WriteDevModePropertyInfo("DriverVersion", devMode.dmDriverVersion.ToString(), null);
            WriteDevModePropertyInfo("Size", devMode.dmSize.ToString(), null);
            WriteDevModePropertyInfo("DriverExtra", devMode.dmDriverExtra.ToString(), null);
            WriteDevModePropertyInfo("Orientation", devMode.dmOrientation.ToString(), (devMode.dmFields & DM_ORIENTATION) == DM_ORIENTATION);
            WriteDevModePropertyInfo("PaperSize", devMode.dmPaperSize.ToString(), (devMode.dmFields & DM_PAPERSIZE) == DM_PAPERSIZE);
            WriteDevModePropertyInfo("PaperLength", devMode.dmPaperLength.ToString(), (devMode.dmFields & DM_PAPERLENGTH) == DM_PAPERLENGTH);
            WriteDevModePropertyInfo("PaperWidth", devMode.dmPaperWidth.ToString(), (devMode.dmFields & DM_PAPERWIDTH) == DM_PAPERWIDTH);
            WriteDevModePropertyInfo("Scale", devMode.dmScale.ToString(), (devMode.dmFields & DM_SCALE) == DM_SCALE);
            WriteDevModePropertyInfo("Copies", devMode.dmCopies.ToString(), (devMode.dmFields & DM_COPIES) == DM_COPIES);
            WriteDevModePropertyInfo("Duplex", devMode.dmDuplex.ToString(), (devMode.dmFields & DM_DUPLEX) == DM_DUPLEX);
            WriteDevModePropertyInfo("YResolution", devMode.dmYResolution.ToString(), null);
            WriteDevModePropertyInfo("TTOption", devMode.dmTTOption.ToString(), null);
            WriteDevModePropertyInfo("Collate", devMode.dmCollate.ToString(), (devMode.dmFields & DM_COLLATE) == DM_COLLATE);
            WriteDevModePropertyInfo("FormName", devMode.dmFormName.ToString(), null);
            WriteDevModePropertyInfo("UnusedPadding", devMode.dmUnusedPadding.ToString(), null);
            WriteDevModePropertyInfo("BitsPerPel", devMode.dmBitsPerPel.ToString(), null);
            WriteDevModePropertyInfo("PelsWidth", devMode.dmPelsWidth.ToString(), null);
            WriteDevModePropertyInfo("PelsHeight", devMode.dmPelsHeight.ToString(), null);
            WriteDevModePropertyInfo("DisplayFlags", devMode.dmDisplayFlags.ToString(), null);
            WriteDevModePropertyInfo("DisplayFrequency", devMode.dmDisplayFlags.ToString(), null);
        }

        private static void WriteDevModePropertyInfo(string settingName, string value, bool? allowSet)
        {
            Console.WriteLine("{0} {1} {2}", allowSet.HasValue ? (allowSet.Value ? "+" : "-") : " ", settingName.PadRight(20, '.'), value);
        }


        [DllImport("kernel32.dll", ExactSpelling = true)]
        public static extern IntPtr GlobalFree(IntPtr handle);

        [DllImport("kernel32.dll", ExactSpelling = true)]
        public static extern IntPtr GlobalLock(IntPtr handle);

        [DllImport("kernel32.dll", ExactSpelling = true)]
        public static extern IntPtr GlobalUnlock(IntPtr handle);

        [DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
            ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern Int32 GetLastError();

        [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 pDeviceNameg,
                                                     IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

        [DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
            CharSet = CharSet.Ansi, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        private 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);

        [DllImport("kernel32.dll")]
        static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes);

        public static void getDevMode(string printerName, string filepath)
        {
            PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
            PrinterValues.pDatatype = 0;
            PrinterValues.pDevMode = 0;
            PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;

            IntPtr ptrZero = IntPtr.Zero;
            IntPtr hPrinter;
            IntPtr pDevMode = new IntPtr();

            //get printer handle
            OpenPrinter(printerName, out hPrinter, ref PrinterValues);

            //allocate memory for ptr to devmode, 0 argument retrieves bytes required
            int bytes = DocumentProperties(new IntPtr(0), hPrinter, printerName, ptrZero, ref pDevMode, 0);
            pDevMode = GlobalAlloc(0, bytes);

            //set the pointer
            DocumentProperties(new IntPtr(0), hPrinter, printerName, pDevMode, ref ptrZero, DM_OUT_BUFFER);

            //write the devMode to a file
            using (FileStream fs = new FileStream(filepath, FileMode.Create))
            {
                for (int i = 0; i < bytes; i++)
                {
                    fs.WriteByte(Marshal.ReadByte(pDevMode, i));
                }
            }
            //free resources
            GlobalFree(pDevMode);
            ClosePrinter(hPrinter);
        }

        public static bool setDevMode(string printerName, string filepath)
        {
            if(!File.Exists(filepath))
            {
                return false;
            }

            IntPtr hPrinter;
            int bytes = 0;
            IntPtr pPInfo;
            IntPtr pDevMode;
            PRINTER_INFO_2 pInfo = new PRINTER_INFO_2();

            PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
            PrinterValues.pDatatype = 0;
            PrinterValues.pDevMode = 0;
            PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;

            //retrieve the devmode from file
            using (FileStream fs = new FileStream(filepath, FileMode.Open))
            {
                int length = Convert.ToInt32(fs.Length);
                pDevMode = GlobalAlloc(0, length);
                for (int i = 0; i < length; i++)
                {
                    Marshal.WriteByte(pDevMode, i, (byte)fs.ReadByte());
                }
            }

            //get printer handle
            OpenPrinter(printerName, out hPrinter, ref PrinterValues);

            //get bytes for printer info structure and allocate memory
            GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out bytes);
            if (bytes == 0)
            {
                throw new Exception("Get Printer Failed");
            }
            pPInfo = GlobalAlloc(0, bytes);

            //set pointer to printer info
            GetPrinter(hPrinter, 2, pPInfo, bytes, out bytes);

            //place the printer info structure
            pInfo = (PRINTER_INFO_2)Marshal.PtrToStructure(pPInfo, typeof(PRINTER_INFO_2));

            //insert the new devmode
            pInfo.pDevMode = pDevMode;
            pInfo.pSecurityDescriptor = IntPtr.Zero;

            //set pointer to new printer info
            Marshal.StructureToPtr(pInfo, pPInfo, true);

            //update
            SetPrinter(hPrinter, 2, pPInfo, 0);

            //free resources
            GlobalFree(pPInfo);
            GlobalFree(pDevMode);
            ClosePrinter(hPrinter);

            return true;
        }

        private static void PrintWordDocument(string path, string printerName)
        {
            object readOnly = true;
            object addToRecentFiles = false;
            object visible = false;
            object backgroundPrint = false;
            object saveChanges = false;
            object sourceFile = path;

            var wordApplication = new Application();
            var doc = wordApplication.Documents.OpenNoRepairDialog(FileName: ref sourceFile, ReadOnly: ref readOnly,
                                                                 AddToRecentFiles: ref addToRecentFiles,
                                                                 Visible: ref visible);
            wordApplication.ActivePrinter = printerName;
            doc.Activate();
            wordApplication.PrintOut(Background: ref backgroundPrint, FileName: sourceFile);
            object _missing = Type.Missing;
            doc.Close(ref saveChanges, ref _missing, ref _missing);
        }
    }
}

ОБНОВЛЕНИЕ 04.12.2018 (через 5,5 лет): Была неприятная редкая проблема с вызовом Marshal.StructureToPtr в этом коде, и сегодня я наконец получил ответ на этот вопрос (см. комментарий Ганса Пассанта). Я не могу проверить, действительно ли это работает, поскольку я больше не работаю над этим проектом, но, похоже, вам может потребоваться применить это исправление, если вы попытаетесь использовать этот код.

person Sasha    schedule 28.08.2012
comment
Как я уже сказал, этот ответ не идеален. Так что, если у кого-то есть лучший подход, пожалуйста, разместите его. - person Sasha; 28.08.2012