I'm currently writing a helper class (wrapper) for the Windows printer management.
At the moment I'm able to create and remove printer objects.
In one of my next steps I want to do the same for the printer ports.
At the moment, I am using the following code to create a new printer port in the Windows printer management.
1.) DLL-Import
[DllImport("winspool.drv")]
private static extern bool OpenPrinter(string printerName, out IntPtr phPrinter, ref PrinterDefaults printerDefaults);
[DllImport("winspool.drv")]
private static extern bool ClosePrinter(IntPtr phPrinter);
[DllImport("winspool.drv", CharSet = CharSet.Unicode)]
private static extern bool XcvDataW(IntPtr hXcv, string pszDataName, IntPtr pInputData, UInt32 cbInputData, out IntPtr pOutputData, UInt32 cbOutputData, out UInt32 pcbOutputNeeded, out UInt32 pdwStatus);
2.) Structures
/// <summary>
/// Defines the printer default settings like the access rights
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct PrinterDefaults
{
public IntPtr pDataType;
public IntPtr pDevMode;
public PrinterAccess DesiredAccess;
}
/// <summary>
/// Stores the port data for adding a new printer port
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct CreatePortData
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string sztPortName;
public UInt32 dwVersion;
public UInt32 dwProtocol;
public UInt32 cbSize;
public UInt32 dwReserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)]
public string sztHostAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)]
public string sztSNMPCommunity;
public UInt32 dwDoubleSpool;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)]
public string sztQueue;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string sztIPAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 540)]
public byte[] Reserved;
public UInt32 dwPortNumber;
public UInt32 dwSNMPEnabled;
public UInt32 dwSNMPDevIndex;
}
/// <summary>
/// Specifies identifiers to indicate the printer access
/// </summary>
internal enum PrinterAccess
{
ServerAdmin = 0x01,
ServerEnum = 0x02,
PrinterAdmin = 0x04,
PrinterUse = 0x08,
JobAdmin = 0x10,
JobRead = 0x20,
StandardRightsRequired = 0x000f0000,
PrinterAllAccess = (StandardRightsRequired | PrinterAdmin | PrinterUse)
}
3.) Method
/// <summary>
/// Adds a new printer port to the windows print management
/// </summary>
/// <param name="configurationType">The configuration type of the port. For example Standard TCP/IP Port.</param>
/// <param name="portName"></param>
/// <param name="portType"></param>
/// <param name="endpoint"></param>
/// <exception cref="ArgumentNullException">Occurs when a parameter is null or empty</exception>
/// <exception cref="PrinterManagementHelperException">Occurs when adding a new TCP printer port to the printer management failed</exception>
public static void CreatePrinterPort(string configurationType, string portName, PrinterPortType portType, string endpoint)
{
// Validation
if (String.IsNullOrEmpty(configurationType))
throw new ArgumentNullException(nameof(configurationType));
if (String.IsNullOrEmpty(portName))
throw new ArgumentNullException(nameof(portName));
if (String.IsNullOrEmpty(endpoint))
throw new ArgumentNullException(nameof(endpoint));
// Opens the printer management
PrinterDefaults defaults = new PrinterDefaults { DesiredAccess = PrinterAccess.ServerAdmin };
if (!OpenPrinter(",XcvMonitor " + configurationType, out IntPtr printerHandle, ref defaults))
{
string message = String.Format(Resources.FailedToOpenPrinterManagement, configurationType);
throw new PrinterManagementHelperException(message);
}
try
{
// Defines the port properties
CreatePortData portData = new CreatePortData
{
dwVersion = 1,
dwProtocol = (uint)portType,
dwPortNumber = portType == PrinterPortType.Raw ? 9100u : 515u,
dwReserved = 0,
sztPortName = portName,
sztIPAddress = endpoint,
sztHostAddress = endpoint,
sztSNMPCommunity = "public",
dwSNMPEnabled = 1,
dwSNMPDevIndex = 1
};
// Sets the port properties into the pointer
uint size = (uint)Marshal.SizeOf(portData);
portData.cbSize = size;
IntPtr pointer = Marshal.AllocHGlobal((int)size);
Marshal.StructureToPtr(portData, pointer, true);
try
{
// Adds the port to the printer management
if (!XcvDataW(printerHandle, "AddPort", pointer, size, out IntPtr outputData, 0, out uint outputNeeded, out uint status))
throw new PrinterManagementHelperException(Resources.FailedToAddTcpPrinterPortToPrinterManagement);
}
catch (Exception)
{
throw;
}
finally
{
Marshal.FreeHGlobal(pointer);
}
}
catch (Exception exception)
{
string message = String.Format(Resources.FailedToAddTcpPrinterPort,
configurationType, portName, portType, endpoint);
throw new PrinterManagementHelperException(message, exception);
}
finally
{
ClosePrinter(printerHandle);
}
}
I've found the following topic on MSDN. Based on the topic I have to change the 'AddPort' Parameter to 'DeletePort'. Changing the parameter value doesn't remove the printer port from the printer management. The status that the method returns is 13 (HEX: 0x0000000d). Regarding the Win32 Error Codes, the data seems to be invalid. Did anyone know how to set the data when I want to delete a printer port?
Update 1
I figured out that the port data structure I used in the above code sample is only for adding printer ports. I found a topic on MSDN for this. There is also another topic about the structure to remove a printer port. I tried to rebuild the model based on the CreatePortData structure sample. I've created the following structure
/// <summary>
/// Stores the port data for deleting an existing printer port
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct DeletePortData
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string sztPortName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)]
public string sztName;
public UInt32 dwVersion;
public UInt32 dwReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 540)]
public byte[] Reserved;
}
Now the whole source code to remove the printer port looks the following:
/// <summary>
/// Deletes an existing printer port from the printer management
/// </summary>
/// <param name="configurationType">The configuration type of the port. For example Standard TCP/IP Port.</param>
/// <param name="portName"></param>
public static void DeletePrinterPort(string configurationType, string portName)
{
// Validation
if (String.IsNullOrEmpty(configurationType))
throw new ArgumentNullException(nameof(configurationType));
if (String.IsNullOrEmpty(portName))
throw new ArgumentNullException(nameof(portName));
// Opens the printer management
PrinterDefaults defaults = new PrinterDefaults { DesiredAccess = PrinterAccess.ServerAdmin };
if (!OpenPrinter(",XcvMonitor " + configurationType, out IntPtr printerHandle, ref defaults))
{
string message = String.Format(Resources.FailedToOpenPrinterManagement, configurationType);
throw new PrinterManagementHelperException(message);
}
try
{
// Defines the port properties
DeletePortData portData = new DeletePortData
{
dwVersion = 1,
dwReserved = 0,
sztPortName = portName
};
// Sets the port properties into the pointer
uint size = (uint)Marshal.SizeOf(portData);
IntPtr pointer = Marshal.AllocHGlobal((int)size);
Marshal.StructureToPtr(portData, pointer, true);
try
{
// Deletes the port from the printer management
if (!XcvDataW(printerHandle, "DeletePort", pointer, size, out IntPtr outputData, 0, out uint outputNeeded, out uint status))
throw new PrinterManagementHelperException(Resources.FailedToDeletePrinterPortFromPrinterManagement);
}
catch (Exception)
{
throw;
}
finally
{
Marshal.FreeHGlobal(pointer);
}
}
catch (Exception exception)
{
string message = string.Format(Resources.FailedToDeletePrinterPort, configurationType, portName);
throw new PrinterManagementHelperException(message, exception);
}
finally
{
ClosePrinter(printerHandle);
}
}
The method is still returning the status 13. The data is still invalid. Do anyone know what I did wrong?
I would appreciate if someone could help me.
Thanks in advance!
I've found the solution for my problem. I had to remove the Reserved property from the DeletePortData structure.
Summary:
To remove an existing printer port from the printer management you have to do the following steps:
1.) DLL-Import
[DllImport("winspool.drv")]
private static extern bool OpenPrinter(string printerName, out IntPtr phPrinter, ref PrinterDefaults printerDefaults);
[DllImport("winspool.drv")]
private static extern bool ClosePrinter(IntPtr phPrinter);
[DllImport("winspool.drv", CharSet = CharSet.Unicode)]
private static extern bool XcvDataW(IntPtr hXcv, string pszDataName, IntPtr pInputData, UInt32 cbInputData, out IntPtr pOutputData, UInt32 cbOutputData, out UInt32 pcbOutputNeeded, out UInt32 pdwStatus);
2.) Define the needed structures:
/// <summary>
/// Defines the printer default settings like the access rights
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct PrinterDefaults
{
public IntPtr pDataType;
public IntPtr pDevMode;
public PrinterAccess DesiredAccess;
}
/// <summary>
/// Stores the port data for deleting an existing printer port
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct DeletePortData
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string sztPortName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)]
public string sztName;
public UInt32 dwVersion;
public UInt32 dwReserved;
}
/// <summary>
/// Specifies identifiers to indicate the printer access
/// </summary>
internal enum PrinterAccess
{
ServerAdmin = 0x01,
ServerEnum = 0x02,
PrinterAdmin = 0x04,
PrinterUse = 0x08,
JobAdmin = 0x10,
JobRead = 0x20,
StandardRightsRequired = 0x000f0000,
PrinterAllAccess = (StandardRightsRequired | PrinterAdmin | PrinterUse)
}
2.) Implement the following method
/// <summary>
/// Deletes an existing printer port from the printer management
/// </summary>
/// <param name="configurationType">The configuration type of the port. For example Standard TCP/IP Port.</param>
/// <param name="portName"></param>
public static void DeletePrinterPort(string configurationType, string portName)
{
// Validation
if (String.IsNullOrEmpty(configurationType))
throw new ArgumentNullException(nameof(configurationType));
if (String.IsNullOrEmpty(portName))
throw new ArgumentNullException(nameof(portName));
// Opens the printer management
PrinterDefaults defaults = new PrinterDefaults { DesiredAccess = PrinterAccess.ServerAdmin };
if (!OpenPrinter(",XcvMonitor " + configurationType, out IntPtr printerHandle, ref defaults))
{
string message = String.Format(Resources.FailedToOpenPrinterManagement, configurationType);
throw new PrinterManagementHelperException(message);
}
try
{
// Defines the port properties
DeletePortData portData = new DeletePortData
{
dwVersion = 1,
dwReserved = 0,
sztPortName = portName
};
// Sets the port properties into the pointer
uint size = (uint)Marshal.SizeOf(portData);
IntPtr pointer = Marshal.AllocHGlobal((int)size);
Marshal.StructureToPtr(portData, pointer, true);
try
{
// Deletes the port from the printer management
if (!XcvDataW(printerHandle, "DeletePort", pointer, size, out IntPtr outputData, 0, out uint outputNeeded, out uint status))
throw new PrinterManagementHelperException(Resources.FailedToDeletePrinterPortFromPrinterManagement);
}
catch (Exception)
{
throw;
}
finally
{
Marshal.FreeHGlobal(pointer);
}
}
catch (Exception exception)
{
string message = string.Format(Resources.FailedToDeletePrinterPort, configurationType, portName);
throw new PrinterManagementHelperException(message, exception);
}
finally
{
ClosePrinter(printerHandle);
}
}
User contributions licensed under CC BY-SA 3.0