1. 动机
- 应用程序开发语言为C#
- 应用程序要求既能在PDA上运行,又能在PC上运行,具备良好的兼容性
- 应用程序要求尽可能多的兼容不同PDA上的红外线设备
- .net自带的SerialPort在HP某型号PDA上操作红外线设备时,只能接受,不能发射
2. 目标
- 具备良好的兼容性,既能够在PC上运行也能够在PDA上运行。
- 挡在PDA上运行时,尽可能多的兼容红外设备。
3. 局限性
- 单线程
- 仅提供send/receive模式的接口,无事件驱动机制
4. 关键技术问题及典型测试案例
最关键的是对EscapeCommFunction的调用,当串口为红外设备时,必须调用此函数才能确保串口工作正常。当串口为一般的COM口时,出于兼容性的考虑,切勿调用此函数。
- 在HP某PDA上测试时,不管串口是否为红外设备,均调用此函数启用红外功能,均能够工作正常。
- 在SIEMENS某PDA上测试时,如果串口为一般COM接口,并且调用了此函数,那么串口不能正常工作,屏蔽此函数后,一切正常。
5. 源码
SerialPort Source Code
using System;
using System.Runtime.InteropServices;
namespace NativeDll
{
public class SerialPort
{
serial port api#region serial port api
//for ir control
private const int SETIR = 10;
private const int CLRIR = 11;
//win32 api constants
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;
private const int MAXBLOCK = 4096;
private const uint PURGE_TXABORT = 0x0001;
// Kill the pending/current writes to the comm port.
private const uint PURGE_RXABORT = 0x0002;
// Kill the pending/current reads to the comm port.
private const uint PURGE_TXCLEAR = 0x0004;
// Kill the transmit queue if there.
private const uint PURGE_RXCLEAR = 0x0008;
// Kill the typeahead buffer if there.
[StructLayout(LayoutKind.Sequential)]
private struct DCB
{
//taken from c struct in platform sdk
public int DCBlength; // sizeof(DCB)
public int BaudRate; // current baud rate
public int fBinary; // binary mode, no EOF check
public int fParity; // enable parity checking
public int fOutxCtsFlow; // CTS output flow control
public int fOutxDsrFlow; // DSR output flow control
public int fDtrControl; // DTR flow control type
public int fDsrSensitivity; // DSR sensitivity
public int fTXContinueOnXoff; // XOFF continues Tx
public int fOutX; // XON/XOFF out flow control
public int fInX; // XON/XOFF in flow control
public int fErrorChar; // enable error replacement
public int fNull; // enable null stripping
public int fRtsControl; // RTS flow control
public int fAbortOnError; // abort on error
public int fDummy2; // reserved
public ushort wReserved; // not currently used
public ushort XonLim; // transmit XON threshold
public ushort XoffLim; // transmit XOFF threshold
public byte ByteSize; // number of bits/byte, 4-8
public byte Parity; // 0-4=no,odd,even,mark,space
public byte StopBits; // 0,1,2 = 1, 1.5, 2
public char XonChar; // Tx and Rx XON character
public char XoffChar; // Tx and Rx XOFF character
public char ErrorChar; // error replacement character
public char EofChar; // end of input character
public char EvtChar; // received event character
public ushort wReserved1; // reserved; do not use
}
[StructLayout(LayoutKind.Sequential)]
private struct COMMTIMEOUTS
{
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
private struct OVERLAPPED
{
public int Internal;
public int InternalHigh;
public int Offset;
public int OffsetHigh;
public int hEvent;
}
[StructLayout(LayoutKind.Sequential)]
private struct COMSTAT
{
/**//*public int fCtsHold;
public int fDsrHold;
public int fRlsdHold;
public int fXoffHold;
public int fXoffSent;
public int fEof;
public int fTxim;
public int fReserved;
public int cbInQue;
public int cbOutQue;*/
// Should have a reverse, i don't know why!!!!!
public int cbOutQue;
public int cbInQue;
public int fReserved;
public int fTxim;
public int fEof;
public int fXoffSent;
public int fXoffHold;
public int fRlsdHold;
public int fDsrHold;
public int fCtsHold;
}
#endregion
private int hComm = INVALID_HANDLE_VALUE;
private bool bOpened = false;
private API api = null;
public SerialPort()
{
if (System.Environment.OSVersion.Platform != PlatformID.WinCE)
api = new PCAPI();
else
api = new PDAAPI();
}
public bool Opened
{
get
{
return bOpened;
}
}
/**//// <summary>
/// open port
/// </summary>
/// <param name="lpFileName"></param>
/// <param name="baudRate"></param>
/// <param name="parity"></param>
/// <param name="byteSize"></param>
/// <param name="stopBits"></param>
/// <returns></returns>
public bool OpenPort(string lpFileName, int baudRate, byte parity, byte
byteSize, byte stopBits)
{
// OPEN THE COMM PORT.
hComm = api.CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0,
0, OPEN_EXISTING, 0, 0);
// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (hComm == INVALID_HANDLE_VALUE)
{
return false;
}
api.SetupComm(hComm, MAXBLOCK, MAXBLOCK);
// SET THE COMM TIMEOUTS.
COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();
api.GetCommTimeouts(hComm, ref ctoCommPort);
ctoCommPort.ReadIntervalTimeout = Int32.MaxValue;
ctoCommPort.ReadTotalTimeoutConstant = 1000;
ctoCommPort.ReadTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutConstant = 1000;
api.SetCommTimeouts(hComm, ref ctoCommPort);
// SET BAUD RATE, PARITY, word SIZE, AND STOP BITS.
// THERE ARE OTHER WAYS OF DOING SETTING THESE BUT THIS IS THE EASIEST.
// IF YOU WANT TO LATER ADD CODE FOR OTHER BAUD RATES, REMEMBER
// THAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING.
// ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING.
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = baudRate;
dcbCommPort.Parity = parity;
dcbCommPort.ByteSize = byteSize;
dcbCommPort.StopBits = stopBits;
api.SetCommState(hComm, ref dcbCommPort);
api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);
bOpened = true;
return true;
}
/**//// <summary>
/// when set this property ,be sure the port is opened.
/// </summary>
public bool IREnable
{
set
{
if (bOpened)
{
if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
{
if (value)
{
api.EscapeCommFunction(hComm, SETIR);//enable ir
}
else
{
api.EscapeCommFunction(hComm, CLRIR);//disable ir
}
}
}
}
}
/**//// <summary>
/// get/set baudrate
/// </summary>
public int BaudRate
{
get
{
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
return dcbCommPort.BaudRate;
}
set
{
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = value;
api.SetCommState(hComm, ref dcbCommPort);
//12/21/2006 wang
//api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
//api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);
}
}
/**//// <summary>
/// close port
/// </summary>
/// <returns></returns>
public bool ClosePort()
{
if (hComm == INVALID_HANDLE_VALUE)
{
return false;
}
if (api.CloseHandle(hComm))
{
hComm = INVALID_HANDLE_VALUE;
bOpened = false;
return true;
}
else
{
return false;
}
}
/**//// <summary>
/// write to port
/// </summary>
/// <param name="WriteBytes"></param>
/// <returns></returns>
public bool WritePort(byte[] WriteBytes)
{
bool flag = false;
COMSTAT ComStat = new COMSTAT();
int dwErrorFlags = 0;
int BytesWritten = 0;
OVERLAPPED ovlCommPort = new OVERLAPPED();
api.ClearCommError(hComm, ref dwErrorFlags, ref ComStat);
api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
//api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);//19/10 2006 wang this is because of the hitachi mode writing bytes continuesly
flag = api.WriteFile(hComm, WriteBytes, WriteBytes.Length, ref BytesWritten, ref ovlCommPort);
return flag;
}
/**//// <summary>
/// read from port
/// </summary>
/// <param name="NumBytes"></param>
/// <param name="commRead"></param>
/// <returns></returns>
public bool ReadPort(int NumBytes, ref byte[] commRead)
{
bool flag = false;
COMSTAT ComStat = new COMSTAT();
int dwErrorFlags = 0;
int BytesRead = 0;
OVERLAPPED ovlCommPort = new OVERLAPPED();
api.ClearCommError(hComm, ref dwErrorFlags, ref ComStat);
flag = api.ReadFile(hComm, commRead, NumBytes, ref BytesRead, ref ovlCommPort);
return flag;
}
interface API
{
int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
bool GetCommState(int hFile, // handle to communications device
ref DCB lpDCB // device-control block
);
bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
);
bool SetCommState(int hFile, // handle to communications device
ref DCB lpDCB // device-control block
);
bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
bool CloseHandle(int hObject // handle to object
);
bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat);
bool PurgeComm(int hFile, // handle to file
uint dwFlags);
bool SetupComm(int hFile, int dwInQueue, int dwOutQueue);
int EscapeCommFunction(int hFile, UInt32 dwFunc);
}
class PCAPI : API
{
public int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
)
{
return WinCreateFile(lpFileName, // file name
dwDesiredAccess, // access mode
dwShareMode, // share mode
lpSecurityAttributes, // SD
dwCreationDisposition, // how to create
dwFlagsAndAttributes, // file attributes
hTemplateFile // handle to template file
);
}
public bool GetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return WinGetCommState(hFile,
// handle to communications device
ref lpDCB // device-control block
);
}
public bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
)
{
return WinBuildCommDCB(lpDef, // device-control string
ref lpDCB // device-control block
);
}
public bool SetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return WinSetCommState(hFile,
// handle to communications device
ref lpDCB // device-control block
);
}
public bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return WinGetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return WinSetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return WinReadFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToRead, // number of bytes to read
ref lpNumberOfBytesRead, // number of bytes read
ref lpOverlapped // overlapped buffer
);
}
public bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return WinWriteFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToWrite, // number of bytes to write
ref lpNumberOfBytesWritten, // number of bytes written
ref lpOverlapped // overlapped buffer
);
}
public bool CloseHandle(int hObject // handle to object
)
{
return WinCloseHandle(hObject // handle to object
);
}
public bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat)
{
return WinClearCommError(hFile, // handle to file
ref lpErrors, ref lpStat);
}
public bool PurgeComm(int hFile, // handle to file
uint dwFlags)
{
return WinPurgeComm(hFile, // handle to file
dwFlags);
}
public bool SetupComm(int hFile, int dwInQueue, int dwOutQueue)
{
return WinSetupComm(hFile, dwInQueue, dwOutQueue);
}
public int EscapeCommFunction(int hFile, UInt32 dwFunc)
{
return WinEscapeCommFunction(hFile, dwFunc);
}
P/Invoke#region P/Invoke
[DllImport("kernel32", EntryPoint = "CreateFile")]
private static extern int WinCreateFile(string lpFileName,
// file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("kernel32", EntryPoint = "GetCommState")]
private static extern bool WinGetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "BuildCommDCB")]
private static extern bool WinBuildCommDCB(string lpDef,
// device-control string
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "SetCommState")]
private static extern bool WinSetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "GetCommTimeouts")]
private static extern bool WinGetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("kernel32", EntryPoint = "SetCommTimeouts")]
private static extern bool WinSetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("kernel32", EntryPoint = "ReadFile")]
private static extern bool WinReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("kernel32", EntryPoint = "WriteFile")]
private static extern bool WinWriteFile(int hFile,
// handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
private static extern bool WinCloseHandle(int hObject
// handle to object
);
[DllImport("kernel32", EntryPoint = "ClearCommError")]
private static extern bool WinClearCommError(int hFile,
// handle to file
ref int lpErrors, ref COMSTAT lpStat);
[DllImport("kernel32", EntryPoint = "PurgeComm")]
private static extern bool WinPurgeComm(int hFile,
// handle to file
uint dwFlags);
[DllImport("kernel32", EntryPoint = "SetupComm")]
private static extern bool WinSetupComm(int hFile, int dwInQueue,
int dwOutQueue);
[DllImport("kernel32", EntryPoint = "EscapeCommFunction")]
private static extern int WinEscapeCommFunction(int hFile, UInt32 dwFunc);
#endregion
}
class PDAAPI : API
{
public int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
)
{
return CECreateFile(lpFileName, // file name
dwDesiredAccess, // access mode
dwShareMode, // share mode
lpSecurityAttributes, // SD
dwCreationDisposition, // how to create
dwFlagsAndAttributes, // file attributes
hTemplateFile // handle to template file
);
}
public bool GetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return CEGetCommState(hFile, // handle to communications device
ref lpDCB // device-control block
);
}
public bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
)
{
return CEBuildCommDCB(lpDef, // device-control string
ref lpDCB // device-control block
);
}
public bool SetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return CESetCommState(hFile, // handle to communications device
ref lpDCB // device-control block
);
}
public bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return CEGetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return CESetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return CEReadFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToRead, // number of bytes to read
ref lpNumberOfBytesRead, // number of bytes read
ref lpOverlapped // overlapped buffer
);
}
public bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return CEWriteFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToWrite, // number of bytes to write
ref lpNumberOfBytesWritten, // number of bytes written
ref lpOverlapped // overlapped buffer
);
}
public bool CloseHandle(int hObject // handle to object
)
{
return CECloseHandle(hObject // handle to object
);
}
public bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat)
{
return CEClearCommError(hFile, // handle to file
ref lpErrors, ref lpStat);
}
public bool PurgeComm(int hFile, // handle to file
uint dwFlags)
{
return CEPurgeComm(hFile, // handle to file
dwFlags);
}
public bool SetupComm(int hFile, int dwInQueue, int dwOutQueue)
{
return CESetupComm(hFile, dwInQueue, dwOutQueue);
}
public int EscapeCommFunction(int hFile, UInt32 dwFunc)
{
return CEEscapeCommFunction(hFile, dwFunc);
}
P/Invoke#region P/Invoke
[DllImport("coredll", EntryPoint = "CreateFile")]
private static extern int CECreateFile(string lpFileName,
// file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("coredll", EntryPoint = "GetCommState")]
private static extern bool CEGetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "BuildCommDCB")]
private static extern bool CEBuildCommDCB(string lpDef,
// device-control string
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "SetCommState")]
private static extern bool CESetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "GetCommTimeouts")]
private static extern bool CEGetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("coredll", EntryPoint = "SetCommTimeouts")]
private static extern bool CESetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("coredll", EntryPoint = "ReadFile")]
private static extern bool CEReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("coredll", EntryPoint = "WriteFile")]
private static extern bool CEWriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("coredll", EntryPoint = "CloseHandle")]
private static extern bool CECloseHandle(int hObject
// handle to object
);
[DllImport("coredll", EntryPoint = "ClearCommError")]
private static extern bool CEClearCommError(int hFile,
// handle to file
ref int lpErrors, ref COMSTAT lpStat);
[DllImport("coredll", EntryPoint = "PurgeComm")]
private static extern bool CEPurgeComm(int hFile, // handle to file
uint dwFlags);
[DllImport("coredll", EntryPoint = "SetupComm")]
private static extern bool CESetupComm(int hFile, int dwInQueue,
int dwOutQueue);
[DllImport("coredll", EntryPoint = "EscapeCommFunction")]
private static extern int CEEscapeCommFunction(int hFile, UInt32 dwFunc);
#endregion
}
}
}
using System;
using System.Runtime.InteropServices;
namespace NativeDll
{
public class SerialPort
{
serial port api#region serial port api
//for ir control
private const int SETIR = 10;
private const int CLRIR = 11;
//win32 api constants
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;
private const int MAXBLOCK = 4096;
private const uint PURGE_TXABORT = 0x0001;
// Kill the pending/current writes to the comm port.
private const uint PURGE_RXABORT = 0x0002;
// Kill the pending/current reads to the comm port.
private const uint PURGE_TXCLEAR = 0x0004;
// Kill the transmit queue if there.
private const uint PURGE_RXCLEAR = 0x0008;
// Kill the typeahead buffer if there.
[StructLayout(LayoutKind.Sequential)]
private struct DCB
{
//taken from c struct in platform sdk
public int DCBlength; // sizeof(DCB)
public int BaudRate; // current baud rate
public int fBinary; // binary mode, no EOF check
public int fParity; // enable parity checking
public int fOutxCtsFlow; // CTS output flow control
public int fOutxDsrFlow; // DSR output flow control
public int fDtrControl; // DTR flow control type
public int fDsrSensitivity; // DSR sensitivity
public int fTXContinueOnXoff; // XOFF continues Tx
public int fOutX; // XON/XOFF out flow control
public int fInX; // XON/XOFF in flow control
public int fErrorChar; // enable error replacement
public int fNull; // enable null stripping
public int fRtsControl; // RTS flow control
public int fAbortOnError; // abort on error
public int fDummy2; // reserved
public ushort wReserved; // not currently used
public ushort XonLim; // transmit XON threshold
public ushort XoffLim; // transmit XOFF threshold
public byte ByteSize; // number of bits/byte, 4-8
public byte Parity; // 0-4=no,odd,even,mark,space
public byte StopBits; // 0,1,2 = 1, 1.5, 2
public char XonChar; // Tx and Rx XON character
public char XoffChar; // Tx and Rx XOFF character
public char ErrorChar; // error replacement character
public char EofChar; // end of input character
public char EvtChar; // received event character
public ushort wReserved1; // reserved; do not use
}
[StructLayout(LayoutKind.Sequential)]
private struct COMMTIMEOUTS
{
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
private struct OVERLAPPED
{
public int Internal;
public int InternalHigh;
public int Offset;
public int OffsetHigh;
public int hEvent;
}
[StructLayout(LayoutKind.Sequential)]
private struct COMSTAT
{
/**//*public int fCtsHold;
public int fDsrHold;
public int fRlsdHold;
public int fXoffHold;
public int fXoffSent;
public int fEof;
public int fTxim;
public int fReserved;
public int cbInQue;
public int cbOutQue;*/
// Should have a reverse, i don't know why!!!!!
public int cbOutQue;
public int cbInQue;
public int fReserved;
public int fTxim;
public int fEof;
public int fXoffSent;
public int fXoffHold;
public int fRlsdHold;
public int fDsrHold;
public int fCtsHold;
}
#endregion
private int hComm = INVALID_HANDLE_VALUE;
private bool bOpened = false;
private API api = null;
public SerialPort()
{
if (System.Environment.OSVersion.Platform != PlatformID.WinCE)
api = new PCAPI();
else
api = new PDAAPI();
}
public bool Opened
{
get
{
return bOpened;
}
}
/**//// <summary>
/// open port
/// </summary>
/// <param name="lpFileName"></param>
/// <param name="baudRate"></param>
/// <param name="parity"></param>
/// <param name="byteSize"></param>
/// <param name="stopBits"></param>
/// <returns></returns>
public bool OpenPort(string lpFileName, int baudRate, byte parity, byte
byteSize, byte stopBits)
{
// OPEN THE COMM PORT.
hComm = api.CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0,
0, OPEN_EXISTING, 0, 0);
// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (hComm == INVALID_HANDLE_VALUE)
{
return false;
}
api.SetupComm(hComm, MAXBLOCK, MAXBLOCK);
// SET THE COMM TIMEOUTS.
COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();
api.GetCommTimeouts(hComm, ref ctoCommPort);
ctoCommPort.ReadIntervalTimeout = Int32.MaxValue;
ctoCommPort.ReadTotalTimeoutConstant = 1000;
ctoCommPort.ReadTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutConstant = 1000;
api.SetCommTimeouts(hComm, ref ctoCommPort);
// SET BAUD RATE, PARITY, word SIZE, AND STOP BITS.
// THERE ARE OTHER WAYS OF DOING SETTING THESE BUT THIS IS THE EASIEST.
// IF YOU WANT TO LATER ADD CODE FOR OTHER BAUD RATES, REMEMBER
// THAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING.
// ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING.
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = baudRate;
dcbCommPort.Parity = parity;
dcbCommPort.ByteSize = byteSize;
dcbCommPort.StopBits = stopBits;
api.SetCommState(hComm, ref dcbCommPort);
api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);
bOpened = true;
return true;
}
/**//// <summary>
/// when set this property ,be sure the port is opened.
/// </summary>
public bool IREnable
{
set
{
if (bOpened)
{
if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
{
if (value)
{
api.EscapeCommFunction(hComm, SETIR);//enable ir
}
else
{
api.EscapeCommFunction(hComm, CLRIR);//disable ir
}
}
}
}
}
/**//// <summary>
/// get/set baudrate
/// </summary>
public int BaudRate
{
get
{
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
return dcbCommPort.BaudRate;
}
set
{
DCB dcbCommPort = new DCB();
dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
api.GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = value;
api.SetCommState(hComm, ref dcbCommPort);
//12/21/2006 wang
//api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
//api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);
}
}
/**//// <summary>
/// close port
/// </summary>
/// <returns></returns>
public bool ClosePort()
{
if (hComm == INVALID_HANDLE_VALUE)
{
return false;
}
if (api.CloseHandle(hComm))
{
hComm = INVALID_HANDLE_VALUE;
bOpened = false;
return true;
}
else
{
return false;
}
}
/**//// <summary>
/// write to port
/// </summary>
/// <param name="WriteBytes"></param>
/// <returns></returns>
public bool WritePort(byte[] WriteBytes)
{
bool flag = false;
COMSTAT ComStat = new COMSTAT();
int dwErrorFlags = 0;
int BytesWritten = 0;
OVERLAPPED ovlCommPort = new OVERLAPPED();
api.ClearCommError(hComm, ref dwErrorFlags, ref ComStat);
api.PurgeComm(hComm, PURGE_RXCLEAR | PURGE_RXABORT);
//api.PurgeComm(hComm, PURGE_TXCLEAR | PURGE_TXABORT);//19/10 2006 wang this is because of the hitachi mode writing bytes continuesly
flag = api.WriteFile(hComm, WriteBytes, WriteBytes.Length, ref BytesWritten, ref ovlCommPort);
return flag;
}
/**//// <summary>
/// read from port
/// </summary>
/// <param name="NumBytes"></param>
/// <param name="commRead"></param>
/// <returns></returns>
public bool ReadPort(int NumBytes, ref byte[] commRead)
{
bool flag = false;
COMSTAT ComStat = new COMSTAT();
int dwErrorFlags = 0;
int BytesRead = 0;
OVERLAPPED ovlCommPort = new OVERLAPPED();
api.ClearCommError(hComm, ref dwErrorFlags, ref ComStat);
flag = api.ReadFile(hComm, commRead, NumBytes, ref BytesRead, ref ovlCommPort);
return flag;
}
interface API
{
int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
bool GetCommState(int hFile, // handle to communications device
ref DCB lpDCB // device-control block
);
bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
);
bool SetCommState(int hFile, // handle to communications device
ref DCB lpDCB // device-control block
);
bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
bool CloseHandle(int hObject // handle to object
);
bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat);
bool PurgeComm(int hFile, // handle to file
uint dwFlags);
bool SetupComm(int hFile, int dwInQueue, int dwOutQueue);
int EscapeCommFunction(int hFile, UInt32 dwFunc);
}
class PCAPI : API
{
public int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
)
{
return WinCreateFile(lpFileName, // file name
dwDesiredAccess, // access mode
dwShareMode, // share mode
lpSecurityAttributes, // SD
dwCreationDisposition, // how to create
dwFlagsAndAttributes, // file attributes
hTemplateFile // handle to template file
);
}
public bool GetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return WinGetCommState(hFile,
// handle to communications device
ref lpDCB // device-control block
);
}
public bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
)
{
return WinBuildCommDCB(lpDef, // device-control string
ref lpDCB // device-control block
);
}
public bool SetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return WinSetCommState(hFile,
// handle to communications device
ref lpDCB // device-control block
);
}
public bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return WinGetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return WinSetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return WinReadFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToRead, // number of bytes to read
ref lpNumberOfBytesRead, // number of bytes read
ref lpOverlapped // overlapped buffer
);
}
public bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return WinWriteFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToWrite, // number of bytes to write
ref lpNumberOfBytesWritten, // number of bytes written
ref lpOverlapped // overlapped buffer
);
}
public bool CloseHandle(int hObject // handle to object
)
{
return WinCloseHandle(hObject // handle to object
);
}
public bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat)
{
return WinClearCommError(hFile, // handle to file
ref lpErrors, ref lpStat);
}
public bool PurgeComm(int hFile, // handle to file
uint dwFlags)
{
return WinPurgeComm(hFile, // handle to file
dwFlags);
}
public bool SetupComm(int hFile, int dwInQueue, int dwOutQueue)
{
return WinSetupComm(hFile, dwInQueue, dwOutQueue);
}
public int EscapeCommFunction(int hFile, UInt32 dwFunc)
{
return WinEscapeCommFunction(hFile, dwFunc);
}
P/Invoke#region P/Invoke
[DllImport("kernel32", EntryPoint = "CreateFile")]
private static extern int WinCreateFile(string lpFileName,
// file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("kernel32", EntryPoint = "GetCommState")]
private static extern bool WinGetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "BuildCommDCB")]
private static extern bool WinBuildCommDCB(string lpDef,
// device-control string
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "SetCommState")]
private static extern bool WinSetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("kernel32", EntryPoint = "GetCommTimeouts")]
private static extern bool WinGetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("kernel32", EntryPoint = "SetCommTimeouts")]
private static extern bool WinSetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("kernel32", EntryPoint = "ReadFile")]
private static extern bool WinReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("kernel32", EntryPoint = "WriteFile")]
private static extern bool WinWriteFile(int hFile,
// handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
private static extern bool WinCloseHandle(int hObject
// handle to object
);
[DllImport("kernel32", EntryPoint = "ClearCommError")]
private static extern bool WinClearCommError(int hFile,
// handle to file
ref int lpErrors, ref COMSTAT lpStat);
[DllImport("kernel32", EntryPoint = "PurgeComm")]
private static extern bool WinPurgeComm(int hFile,
// handle to file
uint dwFlags);
[DllImport("kernel32", EntryPoint = "SetupComm")]
private static extern bool WinSetupComm(int hFile, int dwInQueue,
int dwOutQueue);
[DllImport("kernel32", EntryPoint = "EscapeCommFunction")]
private static extern int WinEscapeCommFunction(int hFile, UInt32 dwFunc);
#endregion
}
class PDAAPI : API
{
public int CreateFile(string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
)
{
return CECreateFile(lpFileName, // file name
dwDesiredAccess, // access mode
dwShareMode, // share mode
lpSecurityAttributes, // SD
dwCreationDisposition, // how to create
dwFlagsAndAttributes, // file attributes
hTemplateFile // handle to template file
);
}
public bool GetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return CEGetCommState(hFile, // handle to communications device
ref lpDCB // device-control block
);
}
public bool BuildCommDCB(string lpDef, // device-control string
ref DCB lpDCB // device-control block
)
{
return CEBuildCommDCB(lpDef, // device-control string
ref lpDCB // device-control block
);
}
public bool SetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
)
{
return CESetCommState(hFile, // handle to communications device
ref lpDCB // device-control block
);
}
public bool GetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return CEGetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool SetCommTimeouts(int hFile, // handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
)
{
return CESetCommTimeouts(hFile, // handle to comm device
ref lpCommTimeouts // time-out values
);
}
public bool ReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return CEReadFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToRead, // number of bytes to read
ref lpNumberOfBytesRead, // number of bytes read
ref lpOverlapped // overlapped buffer
);
}
public bool WriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
)
{
return CEWriteFile(hFile, // handle to file
lpBuffer, // data buffer
nNumberOfBytesToWrite, // number of bytes to write
ref lpNumberOfBytesWritten, // number of bytes written
ref lpOverlapped // overlapped buffer
);
}
public bool CloseHandle(int hObject // handle to object
)
{
return CECloseHandle(hObject // handle to object
);
}
public bool ClearCommError(int hFile, // handle to file
ref int lpErrors, ref COMSTAT lpStat)
{
return CEClearCommError(hFile, // handle to file
ref lpErrors, ref lpStat);
}
public bool PurgeComm(int hFile, // handle to file
uint dwFlags)
{
return CEPurgeComm(hFile, // handle to file
dwFlags);
}
public bool SetupComm(int hFile, int dwInQueue, int dwOutQueue)
{
return CESetupComm(hFile, dwInQueue, dwOutQueue);
}
public int EscapeCommFunction(int hFile, UInt32 dwFunc)
{
return CEEscapeCommFunction(hFile, dwFunc);
}
P/Invoke#region P/Invoke
[DllImport("coredll", EntryPoint = "CreateFile")]
private static extern int CECreateFile(string lpFileName,
// file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("coredll", EntryPoint = "GetCommState")]
private static extern bool CEGetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "BuildCommDCB")]
private static extern bool CEBuildCommDCB(string lpDef,
// device-control string
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "SetCommState")]
private static extern bool CESetCommState(int hFile,
// handle to communications device
ref DCB lpDCB // device-control block
);
[DllImport("coredll", EntryPoint = "GetCommTimeouts")]
private static extern bool CEGetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("coredll", EntryPoint = "SetCommTimeouts")]
private static extern bool CESetCommTimeouts(int hFile,
// handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // time-out values
);
[DllImport("coredll", EntryPoint = "ReadFile")]
private static extern bool CEReadFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("coredll", EntryPoint = "WriteFile")]
private static extern bool CEWriteFile(int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref OVERLAPPED lpOverlapped // overlapped buffer
);
[DllImport("coredll", EntryPoint = "CloseHandle")]
private static extern bool CECloseHandle(int hObject
// handle to object
);
[DllImport("coredll", EntryPoint = "ClearCommError")]
private static extern bool CEClearCommError(int hFile,
// handle to file
ref int lpErrors, ref COMSTAT lpStat);
[DllImport("coredll", EntryPoint = "PurgeComm")]
private static extern bool CEPurgeComm(int hFile, // handle to file
uint dwFlags);
[DllImport("coredll", EntryPoint = "SetupComm")]
private static extern bool CESetupComm(int hFile, int dwInQueue,
int dwOutQueue);
[DllImport("coredll", EntryPoint = "EscapeCommFunction")]
private static extern int CEEscapeCommFunction(int hFile, UInt32 dwFunc);
#endregion
}
}
}
6. 补充
自定义的SerialPort类虽然对IR有较大的兼容性,但毕竟测试范围有限,不能保证对所有的设备都能够兼容。因此最初的想法是对.net自带的SerialPort对象调用EscapeCommFunction函数,但苦于无法得到对象的Handle对象,一直没有实现,如果可以的话,将能享受到更强大的串口功能。
当然,该类远未完善,希望大家能够尽可能的测试并给出意见:)