Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / printing

Adding a Local Port through XcvData and C#

4.88/5 (8 votes)
10 Jul 2010CPOL 34.9K  
Do you know what a "function for all" is? Well, I will tell you if you didn't know: an absolute disaster. The Winspool XcvData function is a great sample of this: it can be used for so many different thigs and it is so poorly documented that using it becomes an incredible pain in the neck.

I just wanted to add a Local Port... If you want to, maybe you would like to try this way:

C#
public static class Winspool
{
    [StructLayout(LayoutKind.Sequential)]
    private class PRINTER_DEFAULTS
    {
        public string pDatatype;
        public IntPtr pDevMode;
        public int DesiredAccess;
    }

    [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. Rememeber .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 needed; // Not that needed in fact...
        uint xcvResult; // Will receive de result here

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

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

        return (int)xcvResult;
    }
}


I think this might save a lot of time for some of you. With this class, you just have to call the AddLocalPort method, passing the name of the port you want. It will return a System error code (0 means ERROR_SUCCESS).

See you...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)