Search code examples
powershellprintingvbscriptcom

Installing 'printer forms' using powershell without prnadmin.dll?


Due to the fact that the latest security updates coming from Microsoft have turned the Jet OLEDB Provider unusable I have to rewrite several elder VBScripts.

Is there a better way to install printer forms on Windows Server 2008 R2 and 2012 R2 then calling the outdated prnadmin.dll via regsvr32/COM/VBscript?

prnadmin.dll was first introduced with Windows Server 2000 Resource Kit and I would like to migrate the whole script to PowerShell.

Unfortunately I can't find any usefull PowerShell cmdlet within the module PrintManagement. So how can I add custom forms to the Printer Server using PSH?


Solution

  • The programmatic way to add a system form definition is to call AddForm. There is not a good wrapper for this call that I am aware of, but P/Invoking to AddForm works. I wrote a quick wrapper and posted it on GitHub.

    Example using the wrapper:

    Windows PowerShell
    Copyright (C) 2016 Microsoft Corporation. All rights reserved.
    
    PS C:\Drop> Import-Module .\PowershellPrinterFormsModule.dll
    PS C:\Drop> Add-SystemForm -Name 'Demo User Form try 1' -Units Inches -Size '4,5'
    PS C:\Drop> Add-SystemForm -Name 'Demo User Form try 2' -Units Inches -Size '4,5' -Margin '0.25,0.5'
    PS C:\Drop> Add-SystemForm -Name 'Demo User Form try 3' -Units Millimeters -Size '80,50' -Margin '10,10,0,0'
    

    Actual P/Invoke call to AddForm:

    SafePrinterHandle hServer;
    if (!OpenPrinter(null, out hServer, IntPtr.Zero))
    {
        throw new Win32Exception();
    }
    using (hServer)
    {
        var form = new FORM_INFO_1()
        {
            Flags = 0,
            Name = this.Name,
            Size = (SIZEL)pageSize,
            ImageableArea = (RECTL)imageableArea
        };
    
        if (!AddForm(hServer, 1, ref form))
        {
            throw new Win32Exception();
        }
    }
    
    internal static class NativeMethods
    {
        #region Constants
    
        internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
    
        #endregion
    
        #region winspool.drv
    
        private const string Winspool = "winspool.drv";
    
        [DllImport(Winspool, SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool OpenPrinter(string szPrinter, out SafePrinterHandle hPrinter, IntPtr pd);
    
        public static SafePrinterHandle OpenPrinter(string szPrinter)
        {
            SafePrinterHandle hServer;
            if (!OpenPrinter(null, out hServer, IntPtr.Zero))
            {
                throw new Win32Exception();
            }
            return hServer;
        }
    
        [DllImport(Winspool, SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool ClosePrinter(IntPtr hPrinter);
    
        [DllImport(Winspool, SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool EnumForms(SafePrinterHandle hPrinter, int level, IntPtr pBuf, int cbBuf, out int pcbNeeded, out int pcReturned);
    
        [DllImport(Winspool, SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool AddForm(SafePrinterHandle hPrinter, int level, [In] ref FORM_INFO_1 form);
    
        [DllImport(Winspool, SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool DeleteForm(SafePrinterHandle hPrinter, string formName);
    
        #endregion
    
        #region Structs
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct FORM_INFO_1
        {
            public int Flags;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string Name;
            public SIZEL Size;
            public RECTL ImageableArea;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct SIZEL
        {
            public int cx;
            public int cy;
    
            public static explicit operator SIZEL(Size r)
                => new SIZEL { cx = (int)r.Width, cy = (int)r.Height };
            public static explicit operator Size(SIZEL r)
                => new Size(r.cx, r.cy);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct RECTL
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
    
            public static explicit operator RECTL(Rect r)
                => new RECTL { left = (int)r.Left, top = (int)r.Top, right = (int)r.Right, bottom = (int)r.Bottom };
            public static explicit operator Rect(RECTL r)
                => new Rect(new Point(r.left, r.top), new Point(r.right, r.bottom));
        }
    
        #endregion
    }
    
    internal sealed class SafePrinterHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public SafePrinterHandle()
            : base(true)
        {
        }
    
        protected override bool ReleaseHandle()
        {
            return NativeMethods.ClosePrinter(handle);
        }
    }