none
¿Cómo obtener dmCopies de la estructura DEVMODE en C#? RRS feed

  • Pregunta

  • Estoy intentando obtener este valor desde hace algunas semanas y solo no puedo. Encontré en Internet una única forma de hacerlo (espero que haya otras, de ser así y la conocen por favor cuéntenme un poco) y es con la estructura DEVMODE que es parte de otra estructura llamada JOB_ONFO_2. El caso es que al intentar solucinar mi problema con estos recursos me surgen diferentes dificultades. Lo primero es saber si las estructuras están bien definidas, ya que en Internet no se encuentra nada en concreto, si no fragmentos. Suponiendo que están bien, estas son las que tengo:

                                        

    //ESTRUCTURAS
            [StructLayout(LayoutKind.Sequential)]
            private struct PRINTER_DEFAULTS{
                public int pDatatype;
                public int pDevMode;
                public int DesiredAccess;
            }

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

            }

            private const short CCDEVICENAME = 32;
            private const short CCFORMNAME = 32;

            [StructLayout(LayoutKind.Sequential)]
            private 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;

            }

            public const int DM_DUPLEX = 0x1000;
            public const int DM_IN_BUFFER = 8;
            public const int DM_OUT_BUFFER = 2;
            public const int PRINTER_ACCESS_ADMINISTER = 0x4;
            public const int PRINTER_ACCESS_USE = 0x8;
            public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
            public const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);

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

            [DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true,
            ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
            static extern bool ClosePrinter(IntPtr hPrinter);

            [DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true,
            ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
            private static extern long DocumentProperties (IntPtr hwnd, IntPtr hPrinter,
            [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
            IntPtr pDevModeOutput, IntPtr pDevModeInput, IntPtr 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", CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall, SetLastError = true)]   
            public static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);
            /*[DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi,
            ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
            static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
            out IntPtr hPrinter, PRINTER_DEFAULTS pd);
            [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = false, 
                CallingConvention = CallingConvention.StdCall, SetLastError = true)]
            public static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);*/

            [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
            private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command);

            // Wrapper for Win32 message formatter
            [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
            private unsafe static extern
            int FormatMessage(int dwFlags,
            ref IntPtr pMessageSource,
            int dwMessageID,
            int dwLanguageID,
            ref string lpBuffer,
            int nSize,
            IntPtr* pArguments);


    ->Pero los problemas que surgen son en el siguiente método que debería recuperar el valor dmCopies: 

    //INFO PRINTER 
            public String SetPrinterDuplex(string sPrinterName, int nDuplexSetting)
            {
            IntPtr hPrinter;
            PRINTER_DEFAULTS pd = new PRINTER_DEFAULTS();
            PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
            DEVMODE dm;
            IntPtr ptrDM;
            IntPtr ptrPrinterInfo;

            int lastError;
            //byte[] yDevModeData;
            //byte[] yPInfoMemory;
            int nBytesNeeded;
            int nRet;
            System.Int32 nJunk;

            //On Error GoTo cleanup

            if ((nDuplexSetting < 1) || (nDuplexSetting > 3) )
            {
            throw new ArgumentOutOfRangeException("nDuplexSetting","nDup lexSetting is incorrect.");
            }
            else
            {
            //if no printername provided, check if there is a default printer and use it instead
            if (sPrinterName.Trim() == "")
            {
            PrintDocument printDocument1 = new PrintDocument();
            sPrinterName = printDocument1.PrinterSettings.PrinterName + "\0";
            }

            //open the printer
            pd.DesiredAccess = PRINTER_ALL_ACCESS;
                    int rawsize = Marshal.SizeOf(pd);
                    IntPtr pdPtr = Marshal.AllocHGlobal(rawsize);
                    pd.DesiredAccess = PRINTER_ALL_ACCESS;
                    Marshal.StructureToPtr(pd, pdPtr, true);
                    nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, pdPtr));

            if ((nRet == 0) || (hPrinter == IntPtr.Zero))
            {
            return "Error: OpenPrinter";
            }

            //get the size of the Printer Info structure
            GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
            if (nBytesNeeded <= 0)
            {
            return "Error: nBytesNeeded";
            }

            // Allocate enough space for PRINTER_INFO_2...
            ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);

            // The second GetPrinter fills in all the current settings, so all you
            // need to do is modify what you're interested in...
            nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
            if (nRet == 0)
            {
            return "Error: GetPrinter";
            }

            pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));

            if (pinfo.pDevMode == IntPtr.Zero)
            {
            // If GetPrinter didn't fill in the DEVMODE, try to get it by calling
            // DocumentProperties...

            //get the size of the devmode structure
            nRet = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
            if (nRet <= 0)
            {
            return "Error: DocumentProperties";
            }

            ptrDM = Marshal.AllocCoTaskMem(nRet);

            nRet = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
            IntPtr.Zero, (IntPtr)DM_OUT_BUFFER);
            if ((nRet < 0) || (ptrDM == IntPtr.Zero))
            {
            //Cannot get the DEVMODE structure.
                            return "Error: Cannot get the DEVMODE structure.";
            }

            pinfo.pDevMode = ptrDM;

            }

            dm = (DEVMODE)Marshal.PtrToStructure(pinfo.pDevMode, typeof(DEVMODE));
            if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
            {
            //You cannot modify the duplex flag for this printer
            //because it does not support duplex or the driver does not support setting
            //it from the Windows API.
                        return "Error: You cannot modify the duplex flag for this printer";
            }

            //update fields
            //dm.dmDuplex = (short)nDuplexSetting;
            //dm.dmFields = DM_DUPLEX;

            Marshal.StructureToPtr(dm,pinfo.pDevMode,true);

            //pinfo.pDevMode = ptrDM;
            pinfo.pSecurityDescriptor = IntPtr.Zero;

            //update driver dependent part of the DEVMODE
            nRet = (int)DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pinfo.pDevMode, 
                        pinfo.pDevMode, (IntPtr)(DM_IN_BUFFER | DM_OUT_BUFFER));
            if (nRet < 0)
            {
            //Unable to set duplex setting to this printer.
                        return "Error: Unable to set duplex setting to this printer.";
            }

            Marshal.StructureToPtr(pinfo,ptrPrinterInfo,true);

            lastError = Marshal.GetLastWin32Error();

            nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
            if (nRet == 0)
            {
            //Unable to set shared printer settings.
            lastError = Marshal.GetLastWin32Error();
            string myErrMsg = lastError.ToString();

                        return "Error: Unable to set shared printer settings.";
            }
            }
            if (hPrinter != IntPtr.Zero)
            ClosePrinter(hPrinter);

            return dm.dmCopies.ToString();

            }//End SetPrinterDuplex

    Primeramente no logro abrir la impresora con:

         nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, pdPtr));

    Ya que siempre devuelve falso y asumo que por lo mismo no funciona getPrinter.

    Paro aún así hay gente que no usa esos pasos para obtener el valor y se va directamente a llenar las estructuras con ptrPrinterInfo y demás. El caso es que cuando quiero instanciar y llenar la estructura DEVMODE (supuestamente Job_Info_2 no) me arroja una excepción tipo: accessViolationexception.

    Realmente no encuentro gran cosa en Internet y empieza a volverme loco esto. Agradecería demasiado a quien pueda ayudarme.

    Saludos :)


    martes, 30 de enero de 2018 23:28