打印机端口设置

时间:2013-02-14 22:48:58

标签: c++ printing marshalling

需要在XPS打印机上设置端口。我在Stackoverflow上找到了一些例子,但它不起作用。 这是一个代码(很多垃圾):

    LPTSTR pDeviceName = _T("Microsoft XPS Document Writer");
    HANDLE phPrinter(nullptr);
    PRINTER_DEFAULTS defaults;
    defaults.DesiredAccess = PRINTER_ACCESS_USE;
    defaults.pDatatype = 0;

    PORT_INFO_3 pInfo3;;

    DWORD needed;
    DWORD XcvResult;

    DWORD err = OpenPrinter(pDeviceName,&phPrinter,NULL);
    //const BYTE* portValue = reinterpret_cast<const BYTE*>("TestPort");
    PBYTE port = (PBYTE)_T("Test1");

    if(err) {
        int res =    XcvData(phPrinter,_T("AddPort"),port,sizeof(port),NULL,0,&needed,&XcvResult);
                }
    else {
        AfxMessageBox(_T("ERROR."),MB_OK);
    }
    ClosePrinter(phPrinter);

这个代码只运行一次最有趣的事情(XcvData func的第一次启动)!

另一个相同行为的例子:

 BOOL AddPortX(void)
    {
        DWORD cbneed,cbstate;
        PBYTE pOutputData;
        HANDLE hXcv = INVALID_HANDLE_VALUE;
        PRINTER_DEFAULTS Defaults = { NULL,NULL,SERVER_ACCESS_ADMINISTER };     

        WCHAR pszPortName[]=L"UTReportPDFPort:"; 
        pOutputData=(PBYTE)malloc(MAX_PATH);

        if(!OpenPrinter(_T("Microsoft XPS Document Writer"),&hXcv,NULL ))
        {
            LPVOID lpMsgBuf; 
            GetLastError();
            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
            ::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);
            free(pOutputData);
            LocalFree( lpMsgBuf );
            return FALSE;

        }
// False
        if(!XcvData(hXcv,L"AddPort",(PBYTE)pszPortName,sizeof(pszPortName),(PBYTE)pOutputData,MAX_PATH,&cbneed,&cbstate))
        {
            LPVOID lpMsgBuf; 
            SetLastError(cbstate);
            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL ); 
            ::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);       
            LocalFree( lpMsgBuf ); 
            free(pOutputData);
        }

        free(pOutputData);
        ClosePrinter(hXcv);

        return TRUE;

    }

那么,如何设置添加打印机端口,并在添加后自动选择它? 也许,有人知道它为什么只运作一次?我的意思是 - XcvData函数。所有下一次它都会返回错误代码6。 .NET解决方案也会很好。

1 个答案:

答案 0 :(得分:1)

  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;
    }
}

Resource is from CodeProject

OpenPrinter()中的第一个参数必须是 - XcvMonitor Local Port。 我们可以使用.NET管理对象来选择端口是默认的。