Kevin Day
Kevin Day

Reputation: 16413

How to create a new port and assign it to a printer

We have a virtual printer (provided by a 3rd party) that is getting assigned to an invalid local printer port. The printer is always local (we aren't dealing with a remote print server or anything like that). I'd like to create a new local port (specific for our application), then configure the printer to be assigned to that port instead of the random (and often incorrect) port that the print driver installer chooses.

I believe that I need to use the XcvData and/or XcvDataPort functions to do this, but I'm at a bit of a loss as to how.

Does anyone have any examples or pointers on how to proceed?

I'd imagine that I need to do the following:

  1. Check to ensure the port name doesn't already exist (I can probably use EnumPorts for this, but I'm not sure that's the best approach given that I have to also create ports)
  2. Create the port name if it does exist
  3. Change the printer configuration to use the new port

and for uninstall:

  1. Remove the port

Upvotes: 3

Views: 6669

Answers (2)

Ulysses
Ulysses

Reputation: 1

I guess your code merely worked by chance. According to https://learn.microsoft.com/en-us/windows-hardware/drivers/print/tcpmon-xcv-commands (and to my own experience) the real solution is:

PORT_DATA_1 pdPortData;  
wcscpy_s(pdPortData.sztPortName, MAX_PORTNAME_LEN, lpPortName);  
[...]
if (!XcvData(hXcv, L"AddPort", (BYTE*) &pdPortData, sizeof(PORT_DATA_1), NULL, 0, &dwNeeded, &dwStatus))
[...]

By chance sztPortName is the first element in PORT_DATA_1 structure. Maybe that's why your code did not fail, although it is wrong.

Upvotes: 0

Kevin Day
Kevin Day

Reputation: 16413

Wow, looks like that one stumped everyone... After much digging, here's how to do it:

DWORD CreatePort(LPWSTR portName)
{
      HANDLE hPrinter;
      PRINTER_DEFAULTS PrinterDefaults;
      memset(&PrinterDefaults, 0, sizeof(PrinterDefaults));

      PrinterDefaults.pDatatype = NULL;
      PrinterDefaults.pDevMode = NULL;
      PrinterDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;

      DWORD needed;
      DWORD rslt;


      if (!OpenPrinter(",XcvMonitor Local Port", &hPrinter, &PrinterDefaults))
          return -1;

      DWORD xcvresult= 0;
      if (!XcvData(hPrinter, L"AddPort", (BYTE *)portName, (lstrlenW(portName) + 1)*2, NULL, 0, &needed, &xcvresult))
            rslt= GetLastError();

      if (!ClosePrinter(hPrinter))
          rslt= GetLastError();

      return rslt;
}

Setting the port on a given printer is relatively straight forward - OpenPrinter(), GetPrinter() with PRINTER_INFO_2, SetPrinter(), ClosePrinter()

Cheerio.

Upvotes: 3

Related Questions