4

I am trying to create a virtual printer to capture print outs from applications in my own application.

I have successfully implemented one that uses the Microsoft PostScript driver and produces ps files. (A lot of code extracted from different open source projects)

However, due to licensing issues with GhostScript on production servers, (it is NOT free for business solutions), I want to implement a different driver that produces XPS files or any other format that I can use to extract text, convert to a PDF, extract images of each page, etc.

The code that I am using with the postscript driver and actually works quite well is as follows:

// Declare the AddPrinterDriver as extern.
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool AddPrinterDriver(String pName, UInt32 Level, ref DRIVER_INFO_3 pDriverInfo);

// Create a function to call it.
public void AddPrinterDriver(string driverName, string driverPath, string dataPath, string configPath, string helpPath)
{
    DRIVER_INFO_3 di = new DRIVER_INFO_3();
    di.cVersion = 3;
    di.pName = driverName;
    di.pEnvironment = null;
    di.pDriverPath = driverPath;
    di.pDataFile = dataPath;
    di.pConfigFile = configPath;
    di.pHelpFile = helpPath;
    di.pDependentFiles = "";
    di.pMonitorName = null;
    di.pDefaultDataType = "RAW";

    if (!AddPrinterDriver(null, 3, ref di))
    {
         Exception exc = new Win32Exception(Marshal.GetLastWin32Error());
         throw exc;
    }
}

Install Printer method (without proper validation and logging):

public void InstallVirtualPrinter()
{
    // Declare file names for PostScript printer driver. (Preinstalled in Vista and Up)
    string driverFileName = "PSCRIPT5.DLL";
    string configFileName = "PS5UI.DLL";
    string helpFileName = "PSCRIPT.HLP";
    string dataFileName = "MyCustomConfig.PPD";

    string driverPath = null;
    string dataPath = null;
    string configPath = null;
    string helpPath = null;

    try
    {
        //Set Printer Driver Path and Files.
        string printerDriverPath = Path.Combine(GetPrinterDirectory(), "3");

        // Set the path for the driver files.
        if (!String.IsNullOrWhiteSpace(printerDriverPath))
        {
            driverPath = string.Format("{0}\\{1}", printerDriverPath, driverFileName);
            dataPath = string.Format("{0}\\{1}", printerDriverPath, dataFileName);
            configPath = string.Format("{0}\\{1}", printerDriverPath, configFileName);
            helpPath = string.Format("{0}\\{1}", printerDriverPath, helpFileName);
        }

        // Add Printer Monitor
        if (!DoesMonitorExist(PrinterMonitorName))
        {
            AddPrinterMonitor(PrinterMonitorName); 
        }

        // Add Printer Port
        if (!DoesPrinterPortExist(PrinterPortName))
        {
            AddPrinterPort(PrinterPortName, PrinterMonitorName);
        }

        // Add Printer Driver
        if (!DoesPrinterDriverExist(PrinterDriverName))
        {
            //
            //
            //
            //
            // This references the above method in this SO question.

            AddPrinterDriver(PrinterDriverName, driverPath, dataPath, configPath, helpPath);
            //
            // This fails when trying with a driver different than PScript.
            //
        }

        // Add Printer
        if (!DoesPrinterExist(PrinterName))
        {
            InstallPrinter(PrinterName, PrinterPortName, PrinterDriverName);
        }

        // Configure Virtual Port
        ConfigureVirtualPort(PrinterMonitorName, PrinterPortName);

        // Restart Spool Service
        RestartSpoolService();

        log.Info("Virtual printer installation completed successfully");
        return;
    }
    catch (Exception exc)
    {
        log.ErrorFormat("An exception has been raise when attempting to install the printer \n{0}", exc);
    }
}

So here is the question:

How can I use a different driver, like UniDrv or XPS to implement the virtual printer/monitor?.

I have tried with UniDrv by replacing the following lines in the code above:

string driverFileName = "unidrv.dll";
string dataFileName = "sample.GPD";
string configFileName = "unidrvui.dll";
string helpFileName = "unidrv.hlp";

When I run the method AddPrinterDriver I get an exception indicating "The system cannot find the file specified".

It doesn't say what file is missing. I assume there may be missing some dependencies, or the sample.GPD file that I found is not good.

Any help will be greatly appreciated.

agarcian
  • 3,909
  • 3
  • 33
  • 55
  • I dont think the driver can be written in C#. You used C++? And how is that you can display the virtual printer among other printers. Is that done by the first function AddPrinterDriver? – Shameel Mohamed Apr 03 '17 at 15:00
  • It is written in C and accessed from C#. I finally ended up with a different driver as this one wasn't really the correct implementation. – agarcian Apr 04 '17 at 13:47
  • Oh, please describe more. I am also working with a similar situation. I need to chose between unidrv, postscript and xpsdrv. I suppose I would not choose Postscript as it seemed to be very old. And I want to write a virtual printer out of it which should redirect the print data to my application. Your knowledge would really help. – Shameel Mohamed Apr 04 '17 at 14:14
  • Did anyone figure out how to do it? – jNewbie Jan 30 '18 at 20:00
  • Hi @agarcian, Did you found what should be used in place of "sample.GPD" ? – Ashish Gehlot Jun 06 '22 at 05:50

1 Answers1

1

Before executing AddPrinterDriver, install all files (unidrv.dll, unidrvui.dll, sample.gpd) in the driver path ie., spool/drivers/x86Orx64 path.