如何在C#中模拟C函数sprintf_s的效果?

时间:2019-06-12 16:02:54

标签: c# c

我试图将我的前任用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;
    }

1 个答案:

答案 0 :(得分:1)

您不需要C#等高级语言中的sprintf系列功能,因为它们通常允许使用简单的=+=运算符进行字符串连接和赋值。

只需为其编写惯用的C#代码:

str = path + "\\SerialComms.ini";

Commenter @ itsme86指出,对于构建路径的任务,您应该使用Path.Combine

Path.Combine(path, "SerialComms.ini");