Windows Mobile无线打印的实现

发布时间: 2009/10/18 11:18:20

      本文介绍Windows Mobile或CE平台下的企业应用中的打印的实现方法和部分打印机指令参考。

 实现主要方法是采用Wifi方式直接向打印机发送打印指令。
       在普通PC上打印是通过安装打印驱动来实现打印,而一般的打印机都没有驱动可以安装在Windows mobile 或其它嵌入式操作系统上。
所以在PDA上要实现打印就只能发送打印指令给打印机了,或通过将数据发送给电脑让电脑来打印。
       在PDA上可以采用WIFi和蓝牙来发送打印指令。可能支持Wifi的打印机相当多些,而且大多打印机都有串口或并口,
因此可以通过TCP转串口或并口使打印机可支持wifi,而且Wifi的移动性更好,蓝牙只适合短距离内的打印。
采用电脑做为服务器端来中转。这样在普通电脑上就通过安装打印机驱动通过串口或并口或网络来打印,
同时也有另一个好处就是可以同时和多个PDA客户端建立打印连接。
如果通过PDA直接向打印机发送打印指令,一般的打印机只能和一个PDA建立连接。
(一)确定打印机的IP和端口
   网络打印的端口一般都是9100,如果不确定,可以在PC上安装下的驱动,看下默认的端口同时测试下打印机是否可打印。
 (二)通过Socket与打印机建立Tcp连接,并发生打印机指令。
     需建立tcp连接,不能使用udp直接发送打印命令。
     同时在PDA设备上最好采用异步socket,因为无线环境不稳定,同时PDA的性能不如普通电脑,如果通过同步Socket很容易导致UI进程死掉。
     基本步骤:
Socket mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(“192.168.1.4”);
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 9100);
//异步连接
mySocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), null);
//异步发送打印数据及指令
mySocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnSend), null);

为了更好的通用性最好封装一个异步Socket类,这样不仅可以用在发送打印指令下,发送其他数据给主机也可以使用:

下面是我封装的一个异步socket类:

namespace PDA.Net
{
    /// <summary>
    /// 通知事件类型
    /// </summary>
    public enum NotifyEvents
    {
        Connected,
        DataSent,
        DataReceived,
        Disconnected,
        ConnectError,
        SendError,
        ReceiveError,
        DisconnectError,
        OtherError
    }
    /// <summary>
    /// 通知事件委托
    /// </summary>
    /// <param name="notifyEvent">事件类型</param>
    /// <param name="eventMessage">事件消息</param>
    /// <param name="data">数据内容</param>
    public delegate void NotifyEventHandler(NotifyEvents notifyEvent, string eventMessage, object data);

    //socket异步通讯类
    public class DeviceSocket
    {
        public Socket mySocket = null;

        // 处理同步
        ManualResetEvent asyncEvent = new ManualResetEvent(true);

        public string receiveBuf = null;
        private const int BUFFER_SIZE = 1024;

       //通知事件
        public event NotifyEventHandler Notify;

        // Closing flag
        private bool closing = false;
        byte[] bRcvd = new byte[BUFFER_SIZE];

        /// <summary>
        ///连接到一个网络地址
        /// </summary>
        public void Connect(String ip, int port)
        {
            mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 将事件状态设置为非终止状态,导致线程阻止
            asyncEvent.Reset();

            // 准备异步socket
            IPAddress ipAddress = IPAddress.Parse(ip);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

            //开始异步连接主机
            mySocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), null);

            // 等待所有异步操作都完成
            //asyncEvent.WaitOne();
        }

        /// <summary>
        /// 确定Socket是否连接,有时Socket的Connected是true,但不能一定保证已连接
        ///需通过poll已确认是否真的已连接 
        /// 
        /// 连接返回true,否则false;
        /// </summary>
        public bool IsConnected
        {
            get
            {
                if (mySocket == null) return false;

                if (!mySocket.Connected) return false;

                //确保socket已连接,即使Conected属性已连接
                try
                {
                    // 确定socket是否连接
                    return !mySocket.Poll(1, SelectMode.SelectError);
                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Async connect callback
        /// </summary>
        private void OnConnect(IAsyncResult ar)
        {
            try
            {
                // 接收挂起的异步连接请求
                mySocket.EndConnect(ar);

                // 通知主线程已连接
                NotifyCaller(NotifyEvents.Connected, null);
            }
            catch (Exception ex)
            {
                NotifyCaller(NotifyEvents.ConnectError, ex.Message);
            }
        }

        /// <summary>
        /// 断开已连接的socket
        /// </summary>
        public void Disconnect()
        {
            // 如果socket没有被创建,直接返回
            if (mySocket == null) return;

            closing = true;

            try
            {
                mySocket.Shutdown(SocketShutdown.Both);
            }
            catch { }

            try
            {
                mySocket.Close();

                // 等待所有异步操作直至完成
                //asyncEvent.WaitOne();
            }
            catch { }

            closing = false;
            NotifyCaller(NotifyEvents.Disconnected, "断开成功");
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        public void Send(String data)
        {
            // String  to byet
            byte[] byteArray = Encoding.Default.GetBytes(data);
            Send(byteArray);
        }

        public void Send(byte[] byteData)
        {
            try
            {

               //发送数据给已连接的socket
                mySocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnSend), null);

                asyncEvent.Reset();
            }
            catch (Exception ex)
            {
                NotifyCaller(NotifyEvents.SendError, ex.Message, byteData);
            }
        }

        /// <summary>
        /// 异步通知发送数据的主线程
        /// </summary>
        private void OnSend(IAsyncResult ar)
        {
            try
            {

                int sendLen = mySocket.EndSend(ar);

               //通知发送成功和发送的数据长度
                NotifyCaller(NotifyEvents.DataSent, sendLen.ToString());
            }
            catch (Exception ex)
            {
                NotifyCaller(NotifyEvents.SendError, ex.Message);
            }
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        public void Receive()
        {
            try
            {
                asyncEvent.Reset();

                mySocket.BeginReceive(bRcvd, 0, BUFFER_SIZE, SocketFlags.None, new AsyncCallback(OnReceive), null);

                //Thread.Sleep(1000);
            }
            catch (Exception ex)
            {
                NotifyCaller(NotifyEvents.ReceiveError, ex.Message);
            }
        }

        /// <summary>
        /// 异步回传接收到的数据
        /// </summary>
        private void OnReceive(IAsyncResult ar)
        {
            try
            {
                int len = mySocket.EndReceive(ar);
                receiveBuf = ASCIIEncoding.ASCII.GetString(bRcvd, 0, len);

               //通知数据接收成功,并返回接收的数据给接收数据的线程
                NotifyCaller(NotifyEvents.DataReceived, null, receiveBuf);
            }
            catch (Exception ex)
            {
                NotifyCaller(NotifyEvents.ReceiveError, ex.Message);
            }
        }

        /// <summary>
        /// 通知发送数据的线程
        /// </summary>
        private void NotifyCaller(NotifyEvents nEvent, string message, object data)
        {
           
            asyncEvent.Set();

         //当没有绑定事件和已断开连接时不发送通知
            if ((this.Notify != null) && !closing)
                Notify(nEvent, message, data);
        }
        public void NotifyCaller(NotifyEvents nEvent, string message)
        {
            asyncEvent.Set();

            //当没有绑定事件和已断开连接时不发送通知
            if ((this.Notify != null) && !closing)
                Notify(nEvent, message, null);
        }

    }
}

在UI中调用DeviceSoket

DeviceSocket=socket = new DeviceSocket();
socket.Notify += new NotifyEventHandler(socket_Notify);

//socket异步通知
void socket_Notify(NotifyEvents nEvent, string message, object data)
{
      //返回给UI进程
  this.Invoke(new NotifyEventHandler(OnSocketNofity), 
              new object[] { nEvent, message, data});
}

//在UI进程中显示Socket通知
 private void OnSocketNofity(NotifyEvents eventType, string message, object data)
 {
      switch (eventType)
      {
        case NotifyEvents.DataReceived:
          // 接收到数据:data
           break;
        case NotifyEvents.Connected:
            //连接成功
          break;
        case NotifyEvents.ConnectError:
            //连接错误
          break;
        case NotifyEvents.Disconnected:
           //断开连接
          break;
        case NotifyEvents.SendError:
          //发送数据错误
          break;
        case NotifyEvents.OtherError:
           //其他错误
          break;
      }

    } 

连接成功后可以根据PDA扫描的数据动态生成打印的直接通过socket 发送给打印机就可以了。
类似:
  byte[] buffer = commandBuilder.GetPrintCommands(barcode);
  socket.Send(buffer);

(三)斑马和Intermec打印机指令参考
仅供参考具体参考相关打印机指令手册 
Zebra打印指令
 public byte[] ZebraCommands(string code)
 {
      int x, y;
      StringBuilder str = new StringBuilder("^XA");
      str.Append("^CW1,DOWNLD1");
      str.Append("^CW2,DOWNLD2");
      str.Append("^CW4,DOWNLD4");
      str.Append("^COY,0^MMT^MD+0^JUS");
      str.Append("^XZ");
      str.Append("^XA");
      str.Append("^PR3^FS");

      //打条形码
      str.Append("^BY3,1,0,20^FS");
      x = 80;
      y = 180;
      str.Append(string.Format("^FT{0},{1}^BCN,120,N,N,N^FD{2}^FS", x, y, code));

      str.Append("^CI0");
      str.Append(string.Format("^FT{0},{1}^A0N,70,80^FD{2}^FS", x + 15, y + 75, code));


      // ******设为中文打印 * ****************
      str.Append("^CI14");
      str.Append("^SEE:GB.DAT");
      str.Append("^CW1,E:MSUNG24.FNT");
      str.Append("^CI14");
      str.Append(string.Format("^FT{0},{1}^A1N,70,80^FD{2}^FS", x + 15, y + 175, "中文测试A"));
      //str.Append("^FT350,270^A1N,36,36^FD中文^FS");
      //str.Append("^CI14");
      str.Append("^CI0");
      str.Append(string.Format("^FT{0},{1}^A0N,70,80^FD{2}^FS", x + 15, y + 275, "123"));

      str.Append("^XZ");
      return Encoding.Default.GetBytes(str.ToString());
 }

Intermes打印指令
 public byte[] IntermecPrintCommands()
    {
      StringBuilder str = new StringBuilder();
      str.Append("\nN\n");
      
      str.Append("A200,100,2,4,1,1,N,\"Example 1\"\n");
      str.Append("A250,150,2,4,1,1,N,\"Example 2\"\n");
      str.Append("A200,200,2,8,2,2,N,\"中文测试\"\n");
      str.Append("B50,60,0,3,2,4,50,B,\"G100102010\"\n");
      str.Append("P1\n");
      return Encoding.Default.GetBytes(str.ToString());
    }

(四)实际应用还需考虑的问题

  

1.判断是否打印成功   
 要看打印机是否支持,就是在打印成功后会有相应的指令发出,通过接收打印机的指令,就可以判断是否成功。  

2.判断没有打印成功的数据,并在网络恢复是能自动打印。

赞助商