简体   繁体   English

如何以编程方式为Windows创建空打印机?

[英]How do I programmatically create a null printer for Windows?

I am going to ask and answer my own question. 我要问和回答我自己的问题。 I would have thought needing to create a null printer was not a rare thing, but I spent waaay too long googling and assembling bit and pieces before I had a working solution. 我本来以为需要创建一个空打印机并不是一件罕见的事,但是在我找到可行的解决方案之前,我花了很长时间来进行谷歌搜索和零碎地组装。 I found lots of answers on creating a null printer via the Windows GUI but info on doing it programmatically was relatively scarce and scattered. 通过Windows GUI创建空打印机时,我发现了很多答案,但是以编程方式进行操作的信息相对稀缺且分散。 Hopefully my answer or the better suggestions it elicites will save some time for some other poor shmo. 希望我的回答或它引起的更好的建议将为其他一些糟糕的shmo节省一些时间。

This "worked for me". 这“为我工作”。 I imagine there is a more elegant way of achieving this, and I expect any number of suggestions for improving/correcting the code, but I was not able to find an succinct complete answer to what I thought would have been a relatively common need. 我想有一种更优雅的方法可以实现这一目标,并且我希望能有许多建议来改进/更正代码,但是我无法找到一个简洁完整的答案来解决我认为比较普遍的需求。 I had a fairly specific requirement, obviously this code can be generalized, proper error handling can be added, etc. 我有一个相当具体的要求,显然此代码可以推广,可以添加适当的错误处理,等等。

//pd is a PrintDocument. used like:

PrintController printController = new StandardPrintController();
pd.PrintController = printController;

NullPrinter np = new NullPrinter();                
if (!np.NullPortExists())
{
   np.CreateNullPort();
}

if (!np.NullPrinterExists())
{
    np.CreateNullPrinter();
}

pd.PrinterSettings.PrinterName = "NUL_PRINTER";


/*********************************************/ 
using System;
using System.Management; // This must also be added as a reference
using System.Drawing.Printing;
using System.Runtime.InteropServices;

namespace YourFavoriteNamespace
{
    //
    // This helper class has methods to determine whether a 'Nul Printer' exists,
    // and to create a null port and null printer if it does not.
    //
    public class NullPrinter
    {
    // Printer port management via Windows GUI (Windows 7, probably same in other versions):
    // 
    //      Go to printers & devices
    //      Select any printer
    //      Click on Print server properties
    //      Select Ports tab
    //      Add or remove (local) port
    //      To remove a local port, if "in use", stop and restart the print spooler service.
    //      It seems that the most recently used port will be "in use" until the system restarts,
    //      or until another port is used.
    //      A port may also be added when adding a printer.
    //      Valid names for a Null port appear to be NUL, NULL, NUL: - all case insensitive. 

    public bool NullPortExists()
    {
        for (int i = 0; i < PrinterSettings.InstalledPrinters.Count; i++)
        {
           string printerName = PrinterSettings.InstalledPrinters[i];
           string query = string.Format("SELECT * from Win32_Printer WHERE Name LIKE '%{0}'", printerName);
           ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
           ManagementObjectCollection coll = searcher.Get();
           foreach (ManagementObject printer in coll)
           {
               string pName = printer["PortName"].ToString();
               if (pName.Equals("NULL", StringComparison.InvariantCultureIgnoreCase) ||
                pName.Equals("NUL", StringComparison.InvariantCultureIgnoreCase) ||
                pName.Equals("NUL:", StringComparison.InvariantCultureIgnoreCase))
               {
                return true;
               }
           }
        }
        return false;
    }

    // The application that uses this requires a printer specifically named "NUL_PRINTER"
    public bool NullPrinterExists()
    {
        for (int i = 0; i < PrinterSettings.InstalledPrinters.Count; i++)
        {
            if (PrinterSettings.InstalledPrinters[i] == "NUL_PRINTER")
            {
                return true;
            }
        }
        return false;
    }

    public bool CreateNullPort()
    {
        return Winspool.AddLocalPort("NUL") == 0 ? true : false;
    }

    public void CreateNullPrinter()
    {
        Winspool.AddPrinter("NUL_PRINTER");
    }

}
/*********************************************************/
    //
    // This Winspool class was mostly borrowed and adapted 
    // from several different people's blog posts, 
    // the links to which I have lost. 
    // Thank you, whoever you are.
    //
    public static class Winspool
    {
        [StructLayout(LayoutKind.Sequential)]
        private class PRINTER_DEFAULTS
        {
            public string pDatatype;
            public IntPtr pDevMode;
            public int DesiredAccess;
        }

        [DllImport("winspool.drv", CharSet = CharSet.Auto)]
        static extern IntPtr AddPrinter(string pName, uint Level, [In] ref PRINTER_INFO_2 pPrinter);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct PRINTER_INFO_2
        {
            public string pServerName,
              pPrinterName,
                  pShareName,
                  pPortName,
                  pDriverName,
                  pComment,
                  pLocation;
            public IntPtr pDevMode;
            public string pSepFile,
                  pPrintProcessor,
                  pDatatype,
                  pParameters;
            public IntPtr pSecurityDescriptor;
            public uint Attributes,
                  Priority,
                  DefaultPriority,
                  StartTime,
                  UntilTime,
                  Status,
                  cJobs,
                  AveragePPM;
        }

        [DllImport("winspool.drv", EntryPoint = "XcvDataW", SetLastError = true)]
        private static extern bool XcvData(
            IntPtr hXcv,
            [MarshalAs(UnmanagedType.LPWStr)] string pszDataName,
            IntPtr pInputData,
            uint cbInputData,
            IntPtr pOutputData,
            uint cbOutputData,
            out uint pcbOutputNeeded,
            out uint pwdStatus);

        [DllImport("winspool.drv", EntryPoint = "OpenPrinterA",  SetLastError = true)]
        private static extern int OpenPrinter(
            string pPrinterName,
            ref IntPtr phPrinter,
            PRINTER_DEFAULTS pDefault);

        [DllImport("winspool.drv", EntryPoint = "ClosePrinter")]
        private static extern int ClosePrinter(IntPtr hPrinter);

        public static int AddLocalPort(string portName)
        {
            PRINTER_DEFAULTS def = new PRINTER_DEFAULTS();

            def.pDatatype = null;
            def.pDevMode = IntPtr.Zero;
            def.DesiredAccess = 1; //Server Access Administer

            IntPtr hPrinter = IntPtr.Zero;

            int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
            if (n == 0)
            return Marshal.GetLastWin32Error();

            if (!portName.EndsWith("\0"))
            portName += "\0"; // Must be a null terminated string

            // Must get the size in bytes. .NET strings are formed by 2-byte characters
            uint size = (uint)(portName.Length * 2);

            // Alloc memory in HGlobal to set the portName
            IntPtr portPtr = Marshal.AllocHGlobal((int)size);
            Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);

            uint NotUsedByUs;
            uint xcvResult; 

            XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0,  out NotUsedByUs, out xcvResult);

            ClosePrinter(hPrinter);
            Marshal.FreeHGlobal(portPtr);

            return (int)xcvResult;
        }

        public static void AddPrinter(string PrinterName)
        {
          IntPtr mystrptr = new IntPtr(0);    
          IntPtr mysend2;
          PRINTER_INFO_2 pi = new PRINTER_INFO_2();

          pi.pServerName =  "";
          pi.pPrinterName = PrinterName;
          pi.pShareName = "NUL";
          pi.pPortName = "NUL";
          pi.pDriverName = "Generic / Text Only";
          pi.pComment = "No Comment";
          pi.pLocation = "Local";
          pi.pDevMode = mystrptr;
          pi.pSepFile = "";
          pi.pPrintProcessor = "WinPrint";
          pi.pDatatype = "RAW";
          pi.pParameters = "";
          pi.pSecurityDescriptor = mystrptr;
          mysend2 = AddPrinter(null,2, ref pi);                    
        }
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM