我试图将我的前任用C编写的一些旧代码移植到C#中。我尝试使用P / invoke方式,但是遇到sprint_s的问题。关于如何解决此问题或使用C#的SerialPort类编写它的任何建议?
[StructLayout(LayoutKind.Sequential)]
internal struct Dcb
{
internal uint DCBLength;
internal uint BaudRate;
private BitVector32 Flags;
private ushort wReserved; // not currently used
internal ushort XonLim; // transmit XON threshold
internal ushort XoffLim; // transmit XOFF threshold
internal byte ByteSize;
internal Parity Parity;
internal StopBits StopBits;
internal sbyte XonChar; // Tx and Rx XON character
internal sbyte XoffChar; // Tx and Rx XOFF character
internal sbyte ErrorChar; // error replacement character
internal sbyte EofChar; // end of input character
internal sbyte EvtChar; // received event character
private ushort wReserved1; // reserved; do not use
private static readonly int fBinary;
private static readonly int fParity;
private static readonly int fOutxCtsFlow;
private static readonly int fOutxDsrFlow;
private static readonly BitVector32.Section fDtrControl;
private static readonly int fDsrSensitivity;
private static readonly int fTXContinueOnXoff;
private static readonly int fOutX;
private static readonly int fInX;
private static readonly int fErrorChar;
private static readonly int fNull;
private static readonly BitVector32.Section fRtsControl;
private static readonly int fAbortOnError;
static Dcb()
{
// Create Boolean Mask
int previousMask;
fBinary = BitVector32.CreateMask();
fParity = BitVector32.CreateMask(fBinary);
fOutxCtsFlow = BitVector32.CreateMask(fParity);
fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
previousMask = BitVector32.CreateMask(fOutxDsrFlow);
previousMask = BitVector32.CreateMask(previousMask);
fDsrSensitivity = BitVector32.CreateMask(previousMask);
fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
fInX = BitVector32.CreateMask(fOutX);
fErrorChar = BitVector32.CreateMask(fInX);
fNull = BitVector32.CreateMask(fErrorChar);
previousMask = BitVector32.CreateMask(fNull);
previousMask = BitVector32.CreateMask(previousMask);
fAbortOnError = BitVector32.CreateMask(previousMask);
// Create section Mask
BitVector32.Section previousSection;
previousSection = BitVector32.CreateSection(1);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fDtrControl = BitVector32.CreateSection(2, previousSection);
previousSection = BitVector32.CreateSection(1, fDtrControl);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fRtsControl = BitVector32.CreateSection(3, previousSection);
previousSection = BitVector32.CreateSection(1, fRtsControl);
}
public bool Binary
{
get { return Flags[fBinary]; }
set { Flags[fBinary] = value; }
}
public bool CheckParity
{
get { return Flags[fParity]; }
set { Flags[fParity] = value; }
}
public bool OutxCtsFlow
{
get { return Flags[fOutxCtsFlow]; }
set { Flags[fOutxCtsFlow] = value; }
}
public bool OutxDsrFlow
{
get { return Flags[fOutxDsrFlow]; }
set { Flags[fOutxDsrFlow] = value; }
}
public DtrControl DtrControl
{
get { return (DtrControl)Flags[fDtrControl]; }
set { Flags[fDtrControl] = (int)value; }
}
public bool DsrSensitivity
{
get { return Flags[fDsrSensitivity]; }
set { Flags[fDsrSensitivity] = value; }
}
public bool TxContinueOnXoff
{
get { return Flags[fTXContinueOnXoff]; }
set { Flags[fTXContinueOnXoff] = value; }
}
public bool OutX
{
get { return Flags[fOutX]; }
set { Flags[fOutX] = value; }
}
public bool InX
{
get { return Flags[fInX]; }
set { Flags[fInX] = value; }
}
public bool ReplaceErrorChar
{
get { return Flags[fErrorChar]; }
set { Flags[fErrorChar] = value; }
}
public bool Null
{
get { return Flags[fNull]; }
set { Flags[fNull] = value; }
}
public RtsControl RtsControl
{
get { return (RtsControl)Flags[fRtsControl]; }
set { Flags[fRtsControl] = (int)value; }
}
public bool AbortOnError
{
get { return Flags[fAbortOnError]; }
set { Flags[fAbortOnError] = value; }
}
}
public enum DtrControl : int
{
/// <summary>
/// Disables the DTR line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the DTR line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables DTR handshaking. If handshaking is enabled, it is an error for the application to adjust the line by
/// using the EscapeCommFunction function.
/// </summary>
Handshake = 2
}
public enum RtsControl : int
{
/// <summary>
/// Disables the RTS line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the RTS line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer
/// is less than one-half full and lowers the RTS line when the buffer is more than
/// three-quarters full. If handshaking is enabled, it is an error for the application to
/// adjust the line by using the EscapeCommFunction function.
/// </summary>
Handshake = 2,
/// <summary>
/// Specifies that the RTS line will be high if bytes are available for transmission. After
/// all buffered bytes have been sent, the RTS line will be low.
/// </summary>
Toggle = 3
}
public enum Parity : byte
{
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
public enum StopBits : byte
{
One = 0,
OnePointFive = 1,
Two = 2
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(IntPtr handle,
byte[] buffer, uint toRead, ref uint read, IntPtr lpOverLapped);
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int byteCount);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);
struct COMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
struct FILE
{
IntPtr _ptr;
int _cnt;
IntPtr _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
IntPtr _tmpfname;
};
[DllImport("kernel32.dll")]
static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
IntPtr lpOverLapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FlushFileBuffers(IntPtr handle);
public bool InitSerialComms()
{
FILE file = new FILE();
COMMTIMEOUTS timeouts;
Dcb dcb = new Dcb();
long len;
char[] name = new char[10];
char[] settings = new char[40];
string str;
// Form the initialization file name
sprintf_s(str, 800, "%s\\SerialComms.ini", path);
// Open the initialization file
fopen_s(&file, str, "r");
// Check for errors
if (file)
{
Console.WriteLine("Error: cannot open file %s\n");
return false;
}
// Scan the serial port name
fgets(name, 10, file);
len = strlen(name);
name[len - 1] = 0;
// Scan the serial port settings
fgets(settings, 40, file);
len = settings.Length;
settings[len - 1] = 0;
// Scan the timeout settings
fgets(str, 40, file); len = strlen(str); string[len - 1] = 0;
sscanf_s(str, "%d,%d,%d,%d,%d",
&timeouts.ReadIntervalTimeout,
&timeouts.ReadTotalTimeoutConstant,
&timeouts.ReadTotalTimeoutMultiplier,
&timeouts.WriteTotalTimeoutConstant,
&timeouts.WriteTotalTimeoutMultiplier);
// Close the initialization file
fclose(file);
// Open the serial port
port = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
// Check for errors
if (port == INVALID_HANDLE_VALUE)
{
// Report the error and return
fprintf(stderr, "Error: cannot open serial port %s\n", name);
fflush(stderr);
return false;
}
// Build the serial port device control block
MemSet(dcb., 0, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!BuildCommDCB(settings, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot create device control block for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Configure the serial port
if (!SetCommState(port, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot configure serial port %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Set the timeouts for the serial port
if (!SetCommTimeouts(port, &timeouts))
{
// Report the error and return
fprintf(stderr, "Error: cannot set timeouts for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Success
return true;
}
bool ReceiveReply(IntPtr port, ref byte[] reply, ref byte num)
{
uint num_read = 0;
uint num_to_read = 255;
ushort crc = 0XFFFF;
byte i, j;
// Clear the reply buffer
//reply = new byte[255];
num = 0;
// Read the data
if (!ReadFile(port, reply, num_to_read, ref num_read, IntPtr.Zero)) return false;
// Check number of bytes that were read
if (num_read < 2) return false;
// Check number of bytes that were read
if (num_read > 255) return false;
// Form the CRC
for (i = 0; i < num_read - 2; i++)
{
crc ^= reply[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1;
//TODO: risky flag check
if (flag == 0) crc ^= 0XA001;
}
}
// Check the CRC
if (reply[i++] != (crc & 0X00FF)) return false;
if (reply[i++] != (crc & 0XFF00) >> 8) return false;
num = (byte)(num_read - 2);
// Success
return true;
}
public static bool SendRequest(IntPtr port, ref byte[] request, ref byte num)
{
ushort crc = 0XFFFF;
byte i, j;
// Check number of bytes
if (num > 253) return false;
// Set number of bytes to write
uint num_to_write = num;
// Form the CRC
for (i = 0; i < num_to_write; i++)
{
crc ^= request[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1; if (flag == 0) crc = (ushort) (crc ^ 0XA001);
}
}
// Set the CRC bytes in the request
request[num_to_write++] = (byte) (crc & 0X00FF);
request[num_to_write++] = (byte) ((crc & 0XFF00) >> 8);
// Send the request
if (!WriteFile(port, request, num_to_write, out uint _, IntPtr.Zero)) return false;
string text = request.ToString().Substring(0, (int) num_to_write).Replace("\r\n", " ");
// Flush the serial line
if (!FlushFileBuffers(port)) return false;
// Success
return true;
}
答案 0 :(得分:1)
您不需要C#等高级语言中的sprintf
系列功能,因为它们通常允许使用简单的=
和+=
运算符进行字符串连接和赋值。
只需为其编写惯用的C#代码:
str = path + "\\SerialComms.ini";
Commenter @ itsme86指出,对于构建路径的任务,您应该使用Path.Combine
:
Path.Combine(path, "SerialComms.ini");