Search code examples
c#printingwminetwork-printers

How do I remove a network printer connection in c#


My end goal is to unmap specific network printers on a local workstation. I have tried via WMI, Windows Script Host, and the Winspool.drv Win32 API. I'm open to ideas in any direction.

When using WMI, I have the following simplified function, largely copied from http://www.codeproject.com/Articles/80680/Managing-Printers-Programatically-using-C-and-WMI:

    public void unmapPrinter()
    {
        ManagementClass win32Printer = new ManagementClass("Win32_Printer");
        ManagementObjectCollection printers = win32Printer.GetInstances();
        foreach (ManagementObject printer in printers)
        {
            printer.Delete();
        }
    }

Executing this on Windows 8.1 as one of the sample users or as an administrator returns "Access Denied" with the following stack trace:

at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
at System.Management.ManagementObject.Delete(DeleteOptions options)
at System.Management.ManagementObject.Delete()
at PrintCentralClient.Workstation.unmapPrinter(Printer printerToRemove) in C:\projects\PrintCentralClient\PrintCentralClient\Workstation.cs:line 247
at PrintCentralClient.Program.unmapPrinters(FileLogger log, Workstation workstation) in C:\projects\PrintCentralClient\PrintCentralClient\Program.cs:line 310
at PrintCentralClient.Program.Main(String[] args) in C:\projects\PrintCentralClient\PrintCentralClient\Program.cs:line 75
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

The same idea in powershell, grabbing the Win32_Printer instances and doing a delete on them, works.

$printers = gwmi Win32_Printer
$printers[i].Delete()

Any ideas on why I am getting Access Denied with my C# code? Elsewhere in the code, I successfully use the Win32_Printer.AddPrinterConnection() method.

Thank you.

Edit 1: Using Windows Scripting Host errors out with "This network connection does not exist."

Demo function:

    public void unmapPrinterViaWSH()
    {
        IWshNetwork_Class network = new IWshNetwork_Class();
        network.RemovePrinterConnection(@"\\<server>\<printerName>", true, true);
    }

I have tried some variations, including changing the remove line to:

network.RemovePrinterConnection("\\\\<server>\\<printerName>", true, true)

I have also tried enumerating the printer connections and deleting with the exact name string:

    public void unmapPrinterViaWSH(Printer printerToRemove)
    {
        IWshNetwork_Class network = new IWshNetwork_Class();
        IWshCollection printers = network.EnumPrinterConnections();
        foreach (object printer in printers)
        {
            if (printer.ToString() == printerToRemove.mappedNameToString())
            {
                network.RemovePrinterConnection(printer.ToString(), true, true);
            }
        }
    }

This fails with the same "Network connection does not exist" error.


Solution

  • For WMI:

    I was missing ConnectionsOptions. The code sample initially provided didn't have anything to do with them. When doing my early testing, I always created the scope first, options second. Another reference I found did it the other way around. That seems to work. Below is a code sample that is working for me:

    public void unmapPrinter()
    {
        ConnectionOptions options = new ConnectionOptions();
        options.EnablePrivileges = true;
        ManagementScope scope = new ManagementScope(ManagementPath.DefaultPath, options);
        scope.Connect();
        ManagementClass win32Printer = new ManagementClass("Win32_Printer");
        ManagementObjectCollection printers = win32Printer.GetInstances();
        foreach (ManagementObject printer in printers)
        {
            printer.Delete();
        }
    }