Используйте функцию PrintSpoolerAPI SetForm() в C#

Я работаю над приложением Windows Forms, в котором я хочу распечатать пользовательский документ. Этот пользовательский документ имеет нестандартный размер, и я должен установить размер страницы печати по умолчанию, используя код C#.

Я сделал поиск в Google и наткнулся PrintSpoolerAPI, Код, который я нашел, добавит пользовательскую форму / страницу, используя AddForm() метод списка доступных страниц для печати. Я также хочу установить эту вновь добавленную страницу как страницу печати по умолчанию.

Я пытался написать эту строку в коде

bool x = SetForm(hPrinter, paperName, 1, ref formInfo);

Возвращает true значение, но не настройка печати по умолчанию.

Есть ли dmPaperSize играет какую-то роль в этом?

Вот полный код:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.ComponentModel;
using System.Drawing.Printing;

namespace WindowsFormsApplication1
{
    public class CustomPrintForm
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct structPrinterDefaults
        {
            [MarshalAs(UnmanagedType.LPTStr)]
            public String pDatatype;
            public IntPtr pDevMode;
            [MarshalAs(UnmanagedType.I4)]
            public int DesiredAccess;
        };

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
    SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPTStr)]
        string printerName,
        out IntPtr phPrinter,
        ref structPrinterDefaults pd);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false,
         CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool ClosePrinter(IntPtr phPrinter);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct structSize
    {
        public Int32 width;
        public Int32 height;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct structRect
    {
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;
    }

    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
    internal struct FormInfo1
    {
        [FieldOffset(0), MarshalAs(UnmanagedType.I4)]
        public uint Flags;
        [FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)]
        public String pName;
        [FieldOffset(8)]
        public structSize Size;
        [FieldOffset(16)]
        public structRect ImageableArea;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi/* changed from CharSet=CharSet.Auto */)]
    internal struct structDevMode
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public String
            dmDeviceName;
        [MarshalAs(UnmanagedType.U2)]
        public short dmSpecVersion;
        [MarshalAs(UnmanagedType.U2)]
        public short dmDriverVersion;
        [MarshalAs(UnmanagedType.U2)]
        public short dmSize;
        [MarshalAs(UnmanagedType.U2)]
        public short dmDriverExtra;
        [MarshalAs(UnmanagedType.U4)]
        public int dmFields;
        [MarshalAs(UnmanagedType.I2)]
        public short dmOrientation;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperSize;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperLength;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperWidth;
        [MarshalAs(UnmanagedType.I2)]
        public short dmScale;
        [MarshalAs(UnmanagedType.I2)]
        public short dmCopies;
        [MarshalAs(UnmanagedType.I2)]
        public short dmDefaultSource;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPrintQuality;
        [MarshalAs(UnmanagedType.I2)]
        public short dmColor;
        [MarshalAs(UnmanagedType.I2)]
        public short dmDuplex;
        [MarshalAs(UnmanagedType.I2)]
        public short dmYResolution;
        [MarshalAs(UnmanagedType.I2)]
        public short dmTTOption;
        [MarshalAs(UnmanagedType.I2)]
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public String dmFormName;
        [MarshalAs(UnmanagedType.U2)]
        public short dmLogPixels;
        [MarshalAs(UnmanagedType.U4)]
        public int dmBitsPerPel;
        [MarshalAs(UnmanagedType.U4)]
        public int dmPelsWidth;
        [MarshalAs(UnmanagedType.U4)]
        public int dmPelsHeight;
        [MarshalAs(UnmanagedType.U4)]
        public int dmNup;
        [MarshalAs(UnmanagedType.U4)]
        public int dmDisplayFrequency;
        [MarshalAs(UnmanagedType.U4)]
        public int dmICMMethod;
        [MarshalAs(UnmanagedType.U4)]
        public int dmICMIntent;
        [MarshalAs(UnmanagedType.U4)]
        public int dmMediaType;
        [MarshalAs(UnmanagedType.U4)]
        public int dmDitherType;
        [MarshalAs(UnmanagedType.U4)]
        public int dmReserved1;
        [MarshalAs(UnmanagedType.U4)]
        public int dmReserved2;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct PRINTER_INFO_9
    {
        public IntPtr pDevMode;
    }

    [DllImport("winspool.Drv", EntryPoint = "AddFormW", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = true,
         CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool AddForm(
     IntPtr phPrinter,
        [MarshalAs(UnmanagedType.I4)] int level,
     ref FormInfo1 form);

    [DllImport("winspool.Drv", EntryPoint = "SetForm", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false,
         CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool SetForm(IntPtr phPrinter, string paperName,
        [MarshalAs(UnmanagedType.I4)] int level, ref FormInfo1 form);

    [DllImport("winspool.Drv", EntryPoint = "DeleteForm", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
    SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool DeleteForm(
     IntPtr phPrinter,
        [MarshalAs(UnmanagedType.LPTStr)] string pName);

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

    [DllImport("GDI32.dll", EntryPoint = "CreateDC", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false,
         CallingConvention = CallingConvention.StdCall),
    SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPTStr)]
        string pDrive,
        [MarshalAs(UnmanagedType.LPTStr)] string pName,
        [MarshalAs(UnmanagedType.LPTStr)] string pOutput,
        ref structDevMode pDevMode);

    [DllImport("GDI32.dll", EntryPoint = "ResetDC", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false,
         CallingConvention = CallingConvention.StdCall),
    SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern IntPtr ResetDC(
     IntPtr hDC,
     ref structDevMode
        pDevMode);

    [DllImport("GDI32.dll", EntryPoint = "DeleteDC", SetLastError = true,
         CharSet = CharSet.Unicode, ExactSpelling = false,
         CallingConvention = CallingConvention.StdCall),
    SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool DeleteDC(IntPtr hDC);

    [DllImport("winspool.Drv", EntryPoint = "SetPrinterA", SetLastError = true,
        CharSet = CharSet.Auto, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool SetPrinter(
       IntPtr hPrinter,
       [MarshalAs(UnmanagedType.I4)] int level,
       IntPtr pPrinter,
       [MarshalAs(UnmanagedType.I4)] int command);

    /*
     LONG DocumentProperties(
       HWND hWnd,               // handle to parent window 
       HANDLE hPrinter,         // handle to printer object
       LPTSTR pDeviceName,      // device name
       PDEVMODE pDevModeOutput, // modified device mode
       PDEVMODE pDevModeInput,  // original device mode
       DWORD fMode              // mode options
       );
     */
    [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
    ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern int DocumentProperties(
       IntPtr hwnd,
       IntPtr hPrinter,
       [MarshalAs(UnmanagedType.LPStr)] string pDeviceName /* changed from String to string */,
       IntPtr pDevModeOutput,
       IntPtr pDevModeInput,
       int fMode
       );

    [DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
    ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool GetPrinter(
       IntPtr hPrinter,
       int dwLevel /* changed type from Int32 */,
       IntPtr pPrinter,
       int dwBuf /* chagned from Int32*/,
       out int dwNeeded /* changed from Int32*/
       );

    // SendMessageTimeout tools
    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL = 0x0000,
        SMTO_BLOCK = 0x0001,
        SMTO_ABORTIFHUNG = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }
    const int WM_SETTINGCHANGE = 0x001A;
    const int HWND_BROADCAST = 0xffff;

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessageTimeout(
       IntPtr windowHandle,
       uint Msg,
       IntPtr wParam,
       IntPtr lParam,
       SendMessageTimeoutFlags flags,
       uint timeout,
       out IntPtr result
       );

    public static void AddMjm80MmPaperSizeToDefaultPrinter()
    {
        AddCustomPaperSizeToDefaultPrinter("MJM 80mm * Receipt Length", 80.1f, 4003.9f);
    }

    public static void AddMjm104MmPaperSizeToDefaultPrinter()
    {
        AddCustomPaperSizeToDefaultPrinter("MJM 104mm * Receipt Length", 104.1f, 4003.9f);
    }

    /// <summary>
    /// Adds the printer form to the default printer
    /// </summary>
    /// <param name="paperName">Name of the printer form</param>
    /// <param name="widthMm">Width given in millimeters</param>
    /// <param name="heightMm">Height given in millimeters</param>
    public static void AddCustomPaperSizeToDefaultPrinter(string paperName, float widthMm, float heightMm)
    {
        PrintDocument pd = new PrintDocument();
        string sPrinterName = pd.PrinterSettings.PrinterName;
        AddCustomPaperSize(sPrinterName, paperName, widthMm, heightMm);
    }

    /// <summary>
    /// Add the printer form to a printer 
    /// </summary>
    /// <param name="printerName">The printer name</param>
    /// <param name="paperName">Name of the printer form</param>
    /// <param name="widthMm">Width given in millimeters</param>
    /// <param name="heightMm">Height given in millimeters</param>
    public static void AddCustomPaperSize(string printerName, string paperName, float
        widthMm, float heightMm)
    {
        if (PlatformID.Win32NT == Environment.OSVersion.Platform)
        {
            // The code to add a custom paper size is different for Windows NT then it is
            // for previous versions of windows

            const int PRINTER_ACCESS_USE = 0x00000008;
            const int PRINTER_ACCESS_ADMINISTER = 0x00000004;
            const int FORM_PRINTER = 0x00000002;

            structPrinterDefaults defaults = new structPrinterDefaults();
            defaults.pDatatype = null;
            defaults.pDevMode = IntPtr.Zero;
            defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;

            IntPtr hPrinter = IntPtr.Zero;

            // Open the printer.
            if (OpenPrinter(printerName, out hPrinter, ref defaults))
            {
                try
                {
                    // delete the form incase it already exists
                    DeleteForm(hPrinter, paperName);
                    // create and initialize the FORM_INFO_1 structure
                    FormInfo1 formInfo = new FormInfo1();
                    formInfo.Flags = 0;
                    formInfo.pName = paperName;
                    // all sizes in 1000ths of millimeters
                    formInfo.Size.width = (int)(widthMm * 1000.0);
                    formInfo.Size.height = (int)(heightMm * 1000.0);
                    formInfo.ImageableArea.left = 0;
                    formInfo.ImageableArea.right = formInfo.Size.width;
                    formInfo.ImageableArea.top = 0;
                    formInfo.ImageableArea.bottom = formInfo.Size.height;
                    if (!AddForm(hPrinter, 1, ref formInfo))
                    {
                        StringBuilder strBuilder = new StringBuilder();
                        strBuilder.AppendFormat("Failed to add the custom paper size {0} to the printer {1}, System error number: {2}",
                            paperName, printerName, GetLastError());
                        throw new ApplicationException(strBuilder.ToString());
                    }
                    else
                    {
                        bool x = SetForm(hPrinter, paperName, 1, ref formInfo);
                    }

                    // INIT
                    const int DM_OUT_BUFFER = 2;
                    const int DM_IN_BUFFER = 8;
                    structDevMode devMode = new structDevMode();
                    IntPtr hPrinterInfo, hDummy;
                    PRINTER_INFO_9 printerInfo;
                    printerInfo.pDevMode = IntPtr.Zero;
                    int iPrinterInfoSize, iDummyInt;


                    // GET THE SIZE OF THE DEV_MODE BUFFER
                    int iDevModeSize = DocumentProperties(IntPtr.Zero, hPrinter, printerName, IntPtr.Zero, IntPtr.Zero, 0);

                    if (iDevModeSize < 0)
                        throw new ApplicationException("Cannot get the size of the DEVMODE structure.");

                    // ALLOCATE THE BUFFER
                    IntPtr hDevMode = Marshal.AllocCoTaskMem(iDevModeSize + 100);

                    // GET A POINTER TO THE DEV_MODE BUFFER 
                    int iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName, hDevMode, IntPtr.Zero, DM_OUT_BUFFER);

                    if (iRet < 0)
                        throw new ApplicationException("Cannot get the DEVMODE structure.");

                    // FILL THE DEV_MODE STRUCTURE
                    devMode = (structDevMode)Marshal.PtrToStructure(hDevMode, devMode.GetType());

                    // SET THE FORM NAME FIELDS TO INDICATE THAT THIS FIELD WILL BE MODIFIED
                    devMode.dmFields = 0x10000; // DM_FORMNAME 
                    // SET THE FORM NAME
                    devMode.dmFormName = paperName;

                    // PUT THE DEV_MODE STRUCTURE BACK INTO THE POINTER
                    Marshal.StructureToPtr(devMode, hDevMode, true);

                    // MERGE THE NEW CHAGES WITH THE OLD
                    iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName,
                             printerInfo.pDevMode, printerInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);

                    if (iRet < 0)
                        throw new ApplicationException("Unable to set the orientation setting for this printer.");

                    // GET THE PRINTER INFO SIZE
                    GetPrinter(hPrinter, 9, IntPtr.Zero, 0, out iPrinterInfoSize);
                    if (iPrinterInfoSize == 0)
                        throw new ApplicationException("GetPrinter failed. Couldn't get the # bytes needed for shared PRINTER_INFO_9 structure");

                    // ALLOCATE THE BUFFER
                    hPrinterInfo = Marshal.AllocCoTaskMem(iPrinterInfoSize + 100);

                    // GET A POINTER TO THE PRINTER INFO BUFFER
                    bool bSuccess = GetPrinter(hPrinter, 9, hPrinterInfo, iPrinterInfoSize, out iDummyInt);

                    if (!bSuccess)
                        throw new ApplicationException("GetPrinter failed. Couldn't get the shared PRINTER_INFO_9 structure");

                    // FILL THE PRINTER INFO STRUCTURE
                    printerInfo = (PRINTER_INFO_9)Marshal.PtrToStructure(hPrinterInfo, printerInfo.GetType());
                    printerInfo.pDevMode = hDevMode;

                    // GET A POINTER TO THE PRINTER INFO STRUCTURE
                    Marshal.StructureToPtr(printerInfo, hPrinterInfo, true);

                    // SET THE PRINTER SETTINGS
                    bSuccess = SetPrinter(hPrinter, 9, hPrinterInfo, 0);

                    if (!bSuccess)
                        throw new Win32Exception(Marshal.GetLastWin32Error(), "SetPrinter() failed.  Couldn't set the printer settings");

                    // Tell all open programs that this change occurred.
                    SendMessageTimeout(new IntPtr(HWND_BROADCAST), WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, CustomPrintForm.SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out hDummy);


                }
                finally
                {
                    ClosePrinter(hPrinter);
                }
            }
            else
            {
                StringBuilder strBuilder = new StringBuilder();
                strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",
                    printerName, GetLastError());
                throw new ApplicationException(strBuilder.ToString());
            }
        }
        else
        {
            structDevMode pDevMode = new structDevMode();
            IntPtr hDC = CreateDC(null, printerName, null, ref pDevMode);
            if (hDC != IntPtr.Zero)
            {
                const long DM_PAPERSIZE = 0x00000002L;
                const long DM_PAPERLENGTH = 0x00000004L;
                const long DM_PAPERWIDTH = 0x00000008L;
                pDevMode.dmFields = (int)(DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);
                pDevMode.dmPaperSize = 256;
                pDevMode.dmPaperWidth = (short)(widthMm * 1000.0);
                pDevMode.dmPaperLength = (short)(heightMm * 1000.0);
                ResetDC(hDC, ref pDevMode);
                DeleteDC(hDC);
            }
        }
    }
}

}

Нужна помощь в решении этого вопроса.

4 ответа

Решение

Ваш код почти безупречен (нуждается в работе в 64-битном режиме) и делает то, что намеревается сделать:

Однако на этом хорошие новости заканчиваются. Используемый API-интерфейс напрямую связывается с драйвером принтера, и он отвечает за то, чтобы ваши изменения DEVMODE вступили в силу. Это прекрасно работает для драйвера принтера XPS, но не подходит для многих других драйверов. Как и драйвер Hewlett Packard, который я тоже пробовал.

Все, что вы делаете, имеет сразу видимый побочный эффект в реестре. Вы можете увидеть это с Regedit.exe. Что является хорошим способом перепроверить ваш код. И чтобы увидеть это, нужно прекратить попытки, потому что драйвер принтера не будет взаимодействовать. Ключи, на которые вы хотите посмотреть:

  • HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Print \ Forms. Этот ключ перечисляет форму, которую вы добавляете. У вас не должно быть проблем с этим.

  • HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Print \ Forms \ MJM 80 мм * Длина квитанции, значение "FormKeyword". Запишите это, это "ключевое слово", по которому принтер выбирает вашу форму.

  • HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Print \ Printers \ printername, значение "DevMode по умолчанию". Это написано, когда вы вызываете SetPrinter(). Вы можете посмотреть на шестнадцатеричный дамп значения. FormKeyword, который вы записали на предыдущем шаге, виден со смещением 0x348

  • HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Print \ Printers \ printername \ DsDriver, printMediaSupported value. Не совсем уверен в этом, но должен показать список размеров бумаги, и там должна быть видна ваша пользовательская форма.

Извините за плохие новости, принтеры отстой.

Для использования x64

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
        internal struct FormInfo1
        {
            [FieldOffset(0), MarshalAs(UnmanagedType.I4)]
            public uint Flags;
            [FieldOffset(8), MarshalAs(UnmanagedType.LPWStr)]
            public String pName;
            [FieldOffset(16)]
            public structSize Size;
            [FieldOffset(24)]
            public structRect ImageableArea;
        };

Я просто использовал подобный код, чтобы добавить специальный размер бумаги в НАШ пользовательский драйвер принтера для Windows. Кажется, что иногда это не работает, но причина в файле драйвера принтера. Если в этом файле нет специального раздела CUSTOMIZE, принтер никогда не примет любой размер бумаги. Вы можете легко проверить, используя стандартную процедуру Windows, чтобы добавить пользовательскую бумагу на любой принтер.

*Option: CUSTOMSIZE 
{ 
    *rcNameID: =USER_DEFINED_SIZE_DISPLAY 
    *MinSize: PAIR(100, 100) 
    *MaxSize: PAIR(4026, 11340) 
    *TopMargin: 0 
    *BottomMargin: 0 
    *MaxPrintableWidth: 4026 
    *MinLeftMargin: 0 
    *CenterPrintable?: FALSE
    *Command: CmdSelect
    {
        *Order: DOC_SETUP.10
        *Cmd: ""
    }
} 

Благодаря коду Nilesh и информации о регистре Ханса мое требование - установить размер страницы по умолчанию для конкретного принтера с помощью программы C#, а затем открыть веб-браузер для печати определенной веб-страницы.

К настоящему времени я попытался добавить свою пользовательскую форму страницы и установить dmFormName с ней по умолчанию. Затем я снова получаю структуру devmode, dmFormName - это то, на что я изменился, но когда я открываю блокнот или другое приложение и пытаюсь что-то напечатать, я вижу, что размер страницы по умолчанию - это не то, что я установил.

Также я попытался удалить все неиспользуемые размеры страниц, чтобы по умолчанию был только один, но можно было удалить только тот, который был добавлен мной.

PS: я делаю рефакторинг кода следующим образом:

internal class PrinterPagaSize
{
    public void DeleteAllPrintForm(string printerName)
    {
        structPrinterDefaults defaults = InitPrinterDefaults();
        IntPtr hPrinter = IntPtr.Zero;
        if (OpenPrinter(printerName, out hPrinter, ref defaults))
        {
            try
            {
                var printerSettings = PrinterSelector.DefaultPrinterSettings;
                foreach (PaperSize ps in printerSettings.PaperSizes)
                {
                    bool ret = DeleteForm(hPrinter, ps.PaperName);
                    Debug.WriteLine("delete ret:" + ret.ToString());
                }
            }
            finally
            {
                ClosePrinter(hPrinter);
            }
        }
    }

    public PaperSize GetPrintForm(string printerName, string paperName)
    {
        PaperSize paper = null;
        //var printer = new System.Drawing.Printing.PrinterSettings {PrinterName = printerName};
        var printer = PrinterSelector.DefaultPrinterSettings;
        foreach (PaperSize ps in printer.PaperSizes)
        {
            if (ps.PaperName.ToLower() == paperName.ToLower())
            {
                paper = ps;
                break;
            }
        }
        return paper;
    }

    /// <summary>
    /// Add the printer form to a printer 
    /// </summary>
    /// <param name="printerName">The printer name</param>
    /// <param name="paperName">Name of the printer form</param>
    /// <param name="widthMm">Width given in millimeters</param>
    /// <param name="heightMm">Height given in millimeters</param>
    public void AddCustomPaperSize(string printerName, string paperName, float
        width, float height)
    {
        if (PlatformID.Win32NT == Environment.OSVersion.Platform)
        {
            // The code to add a custom paper size is different for Windows NT then it is
            // for previous versions of windows
            structPrinterDefaults defaults = InitPrinterDefaults();
            IntPtr hPrinter = IntPtr.Zero;

            // Open the printer.
            if (OpenPrinter(printerName, out hPrinter, ref defaults))
            {
                try
                {
                    // delete the form incase it already exists
                    DeleteForm(hPrinter, paperName);
                    // create and initialize the FORM_INFO_1 structure
                    FormInfo1 formInfo = new FormInfo1();
                    formInfo.Flags = 0;
                    formInfo.pName = paperName;
                    // all sizes in 1000ths of millimeters
                    formInfo.Size.width = (int)(width * 100.0);
                    formInfo.Size.height = (int)(height * 100.0);
                    formInfo.ImageableArea.left = 0;
                    formInfo.ImageableArea.right = formInfo.Size.width;
                    formInfo.ImageableArea.top = 0;
                    formInfo.ImageableArea.bottom = formInfo.Size.height;
                    if (!AddForm(hPrinter, 1, ref formInfo))
                    {
                        StringBuilder strBuilder = new StringBuilder();
                        strBuilder.AppendFormat(
                            "Failed to add the custom paper size {0} to the printer {1}, System error number: {2}",
                            paperName, printerName, GetLastError());
                        throw new ApplicationException(strBuilder.ToString());
                    }
                    else
                    {
                        SetForm(hPrinter, paperName, 1, ref formInfo);
                    }

                    SetPrinterDevMode(hPrinter, printerName, paperName);
                }
                finally
                {
                    ClosePrinter(hPrinter);
                }
            }
            else
            {
                StringBuilder strBuilder = new StringBuilder();
                strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",
                    printerName, GetLastError());
                throw new ApplicationException(strBuilder.ToString());
            }
        }
        else
        {
            structDevMode pDevMode = new structDevMode();
            IntPtr hDC = CreateDC(null, printerName, null, ref pDevMode);
            if (hDC != IntPtr.Zero)
            {
                const long DM_PAPERSIZE = 0x00000002L;
                const long DM_PAPERLENGTH = 0x00000004L;
                const long DM_PAPERWIDTH = 0x00000008L;
                pDevMode.dmFields = (int)(DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);
                pDevMode.dmPaperSize = 256;
                pDevMode.dmPaperWidth = (short)(width * 1000.0);
                pDevMode.dmPaperLength = (short)(height * 1000.0);
                ResetDC(hDC, ref pDevMode);
                DeleteDC(hDC);
            }
        }
    }

    const int DM_OUT_BUFFER = 2;
    const int DM_IN_BUFFER = 8;
    private void SetPrinterDevMode(IntPtr hPrinter, string printerName, string paperName)
    {
        // INIT
        structDevMode devMode = new structDevMode();

        PRINTER_INFO_9 printerInfo = new PRINTER_INFO_9();
        printerInfo.pDevMode = IntPtr.Zero;


        IntPtr hDevMode = GetDevMode(hPrinter, printerName, ref devMode);
        // SET THE FORM NAME FIELDS TO INDICATE THAT THIS FIELD WILL BE MODIFIED
        devMode.dmFields = 0x10000; // DM_FORMNAME 
        // SET THE FORM NAME
        devMode.dmFormName = paperName;
        ChangeDevMode(hPrinter, printerName, paperName, devMode, hDevMode, ref printerInfo);

        IntPtr hPrinterInfo = GetPrinterInfo(hPrinter);

        // FILL THE PRINTER INFO STRUCTURE
        printerInfo = (PRINTER_INFO_9)Marshal.PtrToStructure(hPrinterInfo, printerInfo.GetType());
        printerInfo.pDevMode = hDevMode;

        // GET A POINTER TO THE PRINTER INFO STRUCTURE
        Marshal.StructureToPtr(printerInfo, hPrinterInfo, true);

        // SET THE PRINTER SETTINGS
        bool bSuccess = SetPrinter(hPrinter, 9, hPrinterInfo, 0);

        if (!bSuccess)
            throw new Exception("Set printer failed!");
    }

    private static IntPtr GetPrinterInfo(IntPtr hPrinter)
    {
        IntPtr hPrinterInfo;
        bool bSuccess = false;

        int iPrinterInfoSize, iDummyInt;
        // GET THE PRINTER INFO SIZE
        GetPrinter(hPrinter, 9, IntPtr.Zero, 0, out iPrinterInfoSize);
        if (iPrinterInfoSize == 0)
            throw new ApplicationException(
                "GetPrinter failed. Couldn't get the # bytes needed for shared PRINTER_INFO_9 structure");

        // ALLOCATE THE BUFFER
        hPrinterInfo = Marshal.AllocCoTaskMem(iPrinterInfoSize + 100);

        // GET A POINTER TO THE PRINTER INFO BUFFER
        bSuccess = GetPrinter(hPrinter, 9, hPrinterInfo, iPrinterInfoSize, out iDummyInt);

        if (!bSuccess)
            throw new ApplicationException(
                "GetPrinter failed. Couldn't get the shared PRINTER_INFO_9 structure");

        return hPrinterInfo;
    }

    private static void ChangeDevMode(IntPtr hPrinter, string printerName, string paperName, structDevMode devMode, IntPtr hDevMode, ref PRINTER_INFO_9 printerInfo)
    {
        // PUT THE DEV_MODE STRUCTURE BACK INTO THE POINTER
        Marshal.StructureToPtr(devMode, hDevMode, true);

        // MERGE THE NEW CHAGES WITH THE OLD
        int iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName,
            printerInfo.pDevMode, printerInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);

        if (iRet < 0)
            throw new ApplicationException("Unable to set the orientation setting for this printer.");
    }

    private static IntPtr GetDevMode(IntPtr hPrinter, string printerName, ref structDevMode devMode)
    {
        // GET THE SIZE OF THE DEV_MODE BUFFER
        int iDevModeSize = DocumentProperties(IntPtr.Zero, hPrinter, printerName, IntPtr.Zero,
            IntPtr.Zero, 0);

        if (iDevModeSize < 0)
            throw new ApplicationException("Cannot get the size of the DEVMODE structure.");

        // ALLOCATE THE BUFFER
        IntPtr hDevMode = Marshal.AllocCoTaskMem(iDevModeSize + 100);

        // GET A POINTER TO THE DEV_MODE BUFFER 
        int iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName, hDevMode, IntPtr.Zero,
            DM_OUT_BUFFER);

        if (iRet < 0)
            throw new ApplicationException("Cannot get the DEVMODE structure.");

        // FILL THE DEV_MODE STRUCTURE
        devMode = (structDevMode)Marshal.PtrToStructure(hDevMode, devMode.GetType());
        return hDevMode;
    }

    private static structPrinterDefaults InitPrinterDefaults()
    {
        const int PRINTER_ACCESS_USE = 0x00000008;
        const int PRINTER_ACCESS_ADMINISTER = 0x00000004;
        structPrinterDefaults defaults = new structPrinterDefaults();
        defaults.pDatatype = null;
        defaults.pDevMode = IntPtr.Zero;
        defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;
        return defaults;
    }

    #region Win API
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct structPrinterDefaults
    {
        [MarshalAs(UnmanagedType.LPTStr)]
        public String pDatatype;
        public IntPtr pDevMode;
        [MarshalAs(UnmanagedType.I4)]
        public int DesiredAccess;
    };

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
     SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPTStr)] string printerName,
        out IntPtr phPrinter,
        ref structPrinterDefaults pd);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool ClosePrinter(IntPtr phPrinter);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct structSize
    {
        public Int32 width;
        public Int32 height;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct structRect
    {
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct FormInfo1
    {
        [MarshalAs(UnmanagedType.I4)]
        public int Flags;
        [MarshalAs(UnmanagedType.LPTStr)]
        public String pName;
        public structSize Size;
        public structRect ImageableArea;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi /* changed from CharSet=CharSet.Auto */)]
    internal struct structDevMode
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public String
            dmDeviceName;

        [MarshalAs(UnmanagedType.U2)]
        public short dmSpecVersion;
        [MarshalAs(UnmanagedType.U2)]
        public short dmDriverVersion;
        [MarshalAs(UnmanagedType.U2)]
        public short dmSize;
        [MarshalAs(UnmanagedType.U2)]
        public short dmDriverExtra;
        [MarshalAs(UnmanagedType.U4)]
        public int dmFields;
        [MarshalAs(UnmanagedType.I2)]
        public short dmOrientation;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperSize;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperLength;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPaperWidth;
        [MarshalAs(UnmanagedType.I2)]
        public short dmScale;
        [MarshalAs(UnmanagedType.I2)]
        public short dmCopies;
        [MarshalAs(UnmanagedType.I2)]
        public short dmDefaultSource;
        [MarshalAs(UnmanagedType.I2)]
        public short dmPrintQuality;
        [MarshalAs(UnmanagedType.I2)]
        public short dmColor;
        [MarshalAs(UnmanagedType.I2)]
        public short dmDuplex;
        [MarshalAs(UnmanagedType.I2)]
        public short dmYResolution;
        [MarshalAs(UnmanagedType.I2)]
        public short dmTTOption;
        [MarshalAs(UnmanagedType.I2)]
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public String dmFormName;
        [MarshalAs(UnmanagedType.U2)]
        public short dmLogPixels;
        [MarshalAs(UnmanagedType.U4)]
        public int dmBitsPerPel;
        [MarshalAs(UnmanagedType.U4)]
        public int dmPelsWidth;
        [MarshalAs(UnmanagedType.U4)]
        public int dmPelsHeight;
        [MarshalAs(UnmanagedType.U4)]
        public int dmNup;
        [MarshalAs(UnmanagedType.U4)]
        public int dmDisplayFrequency;
        [MarshalAs(UnmanagedType.U4)]
        public int dmICMMethod;
        [MarshalAs(UnmanagedType.U4)]
        public int dmICMIntent;
        [MarshalAs(UnmanagedType.U4)]
        public int dmMediaType;
        [MarshalAs(UnmanagedType.U4)]
        public int dmDitherType;
        [MarshalAs(UnmanagedType.U4)]
        public int dmReserved1;
        [MarshalAs(UnmanagedType.U4)]
        public int dmReserved2;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct PRINTER_INFO_9
    {
        public IntPtr pDevMode;
    }

    [DllImport("winspool.Drv", EntryPoint = "AddFormW", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool AddForm(
        IntPtr phPrinter,
        [MarshalAs(UnmanagedType.I4)] int level,
        ref FormInfo1 form);

    [DllImport("winspool.Drv", EntryPoint = "SetForm", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool SetForm(IntPtr phPrinter, string paperName,
        [MarshalAs(UnmanagedType.I4)] int level, ref FormInfo1 form);

    [DllImport("winspool.Drv", EntryPoint = "DeleteForm", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
     SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool DeleteForm(
        IntPtr phPrinter,
        [MarshalAs(UnmanagedType.LPTStr)] string pName);

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

    [DllImport("GDI32.dll", EntryPoint = "CreateDC", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall),
     SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPTStr)] string pDrive,
        [MarshalAs(UnmanagedType.LPTStr)] string pName,
        [MarshalAs(UnmanagedType.LPTStr)] string pOutput,
        ref structDevMode pDevMode);

    [DllImport("GDI32.dll", EntryPoint = "ResetDC", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall),
     SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern IntPtr ResetDC(
        IntPtr hDC,
        ref structDevMode
            pDevMode);

    [DllImport("GDI32.dll", EntryPoint = "DeleteDC", SetLastError = true,
        CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall),
     SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool DeleteDC(IntPtr hDC);

    [DllImport("winspool.Drv", EntryPoint = "SetPrinterA", SetLastError = true,
        CharSet = CharSet.Auto, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
    internal static extern bool SetPrinter(
        IntPtr hPrinter,
        [MarshalAs(UnmanagedType.I4)] int level,
        IntPtr pPrinter,
        [MarshalAs(UnmanagedType.I4)] int command);

    /*
 LONG DocumentProperties(
   HWND hWnd,               // handle to parent window 
   HANDLE hPrinter,         // handle to printer object
   LPTSTR pDeviceName,      // device name
   PDEVMODE pDevModeOutput, // modified device mode
   PDEVMODE pDevModeInput,  // original device mode
   DWORD fMode              // mode options
   );
 */

    [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern int DocumentProperties(
        IntPtr hwnd,
        IntPtr hPrinter,
        [MarshalAs(UnmanagedType.LPStr)] string pDeviceName /* changed from String to string */,
        IntPtr pDevModeOutput,
        IntPtr pDevModeInput,
        int fMode
        );

    [DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool GetPrinter(
        IntPtr hPrinter,
        int dwLevel /* changed type from Int32 */,
        IntPtr pPrinter,
        int dwBuf /* chagned from Int32*/,
        out int dwNeeded /* changed from Int32*/
        );

    // SendMessageTimeout tools
    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL = 0x0000,
        SMTO_BLOCK = 0x0001,
        SMTO_ABORTIFHUNG = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    private const int WM_SETTINGCHANGE = 0x001A;
    private const int HWND_BROADCAST = 0xffff;

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessageTimeout(
        IntPtr windowHandle,
        uint Msg,
        IntPtr wParam,
        IntPtr lParam,
        SendMessageTimeoutFlags flags,
        uint timeout,
        out IntPtr result
        );
    #endregion
}
Другие вопросы по тегам