基于GTKSystem.Windows.Forms框架,我们可以实现真正的跨平台WinForm串口通讯应用:
首先需要正确配置项目文件以支持跨平台运行:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExeOutputType>
<TargetFramework>net8.0TargetFramework>
<UseWindowsForms>falseUseWindowsForms>
PropertyGroup>
<ItemGroup>
<PackageReference Include="GTKSystem.Windows.Forms" Version="3.24.24.105" />
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
ItemGroup>
Project>
using GTKSystem.Windows.Forms;
using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
///
/// 基于GTKSystem的跨平台串口通讯窗体
/// 支持Windows、Linux、macOS三大平台
///
public partial class CrossPlatformSerialForm : Form
{
private SerialPort serialPort;
private ComboBox cmbPortName;
private ComboBox cmbBaudRate;
private TextBox txtReceived;
private TextBox txtSend;
private Button btnConnect;
private Button btnSend;
private Label lblStatus;
private bool isConnected = false;
public CrossPlatformSerialForm()
{
InitializeComponent();
InitializeSerialPort();
LoadAvailablePorts();
}
///
/// 初始化界面组件
///
private void InitializeComponent()
{
this.Text = "跨平台串口通讯工具";
this.Size = new System.Drawing.Size(800, 600);
this.StartPosition = FormStartPosition.CenterScreen;
// 创建控件
CreateControls();
LayoutControls();
}
///
/// 创建界面控件
///
private void CreateControls()
{
// 串口配置区域
var lblPort = new Label { Text = "串口:", Location = new System.Drawing.Point(10, 15) };
cmbPortName = new ComboBox
{
Location = new System.Drawing.Point(60, 12),
Size = new System.Drawing.Size(100, 23),
DropDownStyle = ComboBoxStyle.DropDownList
};
var lblBaud = new Label { Text = "波特率:", Location = new System.Drawing.Point(180, 15) };
cmbBaudRate = new ComboBox
{
Location = new System.Drawing.Point(240, 12),
Size = new System.Drawing.Size(100, 23),
DropDownStyle = ComboBoxStyle.DropDownList
};
// 添加常用波特率
cmbBaudRate.Items.AddRange(new object[] { 9600, 19200, 38400, 57600, 115200 });
cmbBaudRate.SelectedIndex = 0;
btnConnect = new Button
{
Text = "连接",
Location = new System.Drawing.Point(360, 10),
Size = new System.Drawing.Size(80, 25)
};
btnConnect.Click += BtnConnect_Click;
lblStatus = new Label
{
Text = "状态: 未连接",
Location = new System.Drawing.Point(460, 15),
Size = new System.Drawing.Size(200, 20),
ForeColor = System.Drawing.Color.Red
};
// 数据接收区域
var lblReceive = new Label
{
Text = "接收数据:",
Location = new System.Drawing.Point(10, 50),
Size = new System.Drawing.Size(100, 20)
};
txtReceived = new TextBox
{
Location = new System.Drawing.Point(10, 75),
Size = new System.Drawing.Size(760, 300),
Multiline = true,
ReadOnly = true,
ScrollBars = ScrollBars.Vertical
};
// 数据发送区域
var lblSend = new Label
{
Text = "发送数据:",
Location = new System.Drawing.Point(10, 390),
Size = new System.Drawing.Size(100, 20)
};
txtSend = new TextBox
{
Location = new System.Drawing.Point(10, 415),
Size = new System.Drawing.Size(680, 25)
};
btnSend = new Button
{
Text = "发送",
Location = new System.Drawing.Point(700, 413),
Size = new System.Drawing.Size(70, 29),
Enabled = false
};
btnSend.Click += BtnSend_Click;
// 将控件添加到窗体
this.Controls.AddRange(new Control[]
{
lblPort, cmbPortName, lblBaud, cmbBaudRate, btnConnect, lblStatus,
lblReceive, txtReceived, lblSend, txtSend, btnSend
});
}
///
/// 布局控件(可选的美化布局)
///
private void LayoutControls()
{
// 可以在这里添加更复杂的布局逻辑
// GTKSystem支持大部分标准的WinForm布局特性
}
///
/// 初始化串口对象
///
private void InitializeSerialPort()
{
serialPort = new SerialPort();
serialPort.DataReceived += SerialPort_DataReceived;
serialPort.ErrorReceived += SerialPort_ErrorReceived;
}
///
/// 加载可用串口列表
/// 跨平台自动识别串口设备
///
private void LoadAvailablePorts()
{
try
{
cmbPortName.Items.Clear();
string[] ports = SerialPort.GetPortNames();
if (ports.Length == 0)
{
cmbPortName.Items.Add("无可用串口");
lblStatus.Text = "状态: 未找到可用串口";
lblStatus.ForeColor = System.Drawing.Color.Orange;
}
else
{
cmbPortName.Items.AddRange(ports);
cmbPortName.SelectedIndex = 0;
lblStatus.Text = $"状态: 找到 {ports.Length} 个串口";
lblStatus.ForeColor = System.Drawing.Color.Blue;
}
}
catch (Exception ex)
{
ShowError($"加载串口列表失败: {ex.Message}");
}
}
///
/// 连接/断开按钮事件处理
///
private void BtnConnect_Click(object sender, EventArgs e)
{
if (isConnected)
{
DisconnectSerial();
}
else
{
ConnectSerial();
}
}
///
/// 连接串口
///
private void ConnectSerial()
{
try
{
if (cmbPortName.SelectedItem == null)
{
ShowError("请选择串口");
return;
}
string portName = cmbPortName.SelectedItem.ToString();
if (portName == "无可用串口")
{
ShowError("没有可用的串口");
return;
}
// 配置串口参数
serialPort.PortName = portName;
serialPort.BaudRate = (int)cmbBaudRate.SelectedItem;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.Handshake = Handshake.None;
// 设置超时
serialPort.ReadTimeout = 3000;
serialPort.WriteTimeout = 3000;
// 打开串口
serialPort.Open();
isConnected = true;
// 更新界面状态
UpdateConnectionStatus(true);
// 清空接收区域
txtReceived.Clear();
ShowInfo($"成功连接到 {portName}");
}
catch (Exception ex)
{
ShowError($"连接失败: {ex.Message}");
isConnected = false;
UpdateConnectionStatus(false);
}
}
///
/// 断开串口连接
///
private void DisconnectSerial()
{
try
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
}
isConnected = false;
UpdateConnectionStatus(false);
ShowInfo("已断开连接");
}
catch (Exception ex)
{
ShowError($"断开连接失败: {ex.Message}");
}
}
///
/// 发送数据按钮事件处理
///
private void BtnSend_Click(object sender, EventArgs e)
{
SendData();
}
///
/// 发送数据
///
private void SendData()
{
try
{
if (!isConnected || !serialPort.IsOpen)
{
ShowError("请先连接串口");
return;
}
string textToSend = txtSend.Text;
if (string.IsNullOrEmpty(textToSend))
{
ShowError("请输入要发送的数据");
return;
}
// 发送数据
serialPort.WriteLine(textToSend);
// 在接收区域显示发送的数据
AppendReceivedText($"[发送] {DateTime.Now:HH:mm:ss} - {textToSend}");
// 清空发送文本框
txtSend.Clear();
}
catch (Exception ex)
{
ShowError($"发送数据失败: {ex.Message}");
}
}
///
/// 串口数据接收事件处理
///
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// 读取接收到的数据
string receivedData = serialPort.ReadExisting();
// 跨线程更新UI
this.Invoke(new Action(() =>
{
AppendReceivedText($"[接收] {DateTime.Now:HH:mm:ss} - {receivedData.Trim()}");
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
ShowError($"接收数据异常: {ex.Message}");
}));
}
}
///
/// 串口错误事件处理
///
private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
this.Invoke(new Action(() =>
{
ShowError($"串口错误: {e.EventType}");
}));
}
///
/// 更新连接状态
///
private void UpdateConnectionStatus(bool connected)
{
btnConnect.Text = connected ? "断开" : "连接";
btnSend.Enabled = connected;
cmbPortName.Enabled = !connected;
cmbBaudRate.Enabled = !connected;
lblStatus.Text = connected ? "状态: 已连接" : "状态: 未连接";
lblStatus.ForeColor = connected ? System.Drawing.Color.Green : System.Drawing.Color.Red;
}
///
/// 在接收文本框中追加文本
///
private void AppendReceivedText(string text)
{
txtReceived.AppendText(text + Environment.NewLine);
txtReceived.SelectionStart = txtReceived.Text.Length;
txtReceived.ScrollToCaret();
}
///
/// 显示错误消息
///
private void ShowError(string message)
{
MessageBox.Show(message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
///
/// 显示信息消息
///
private void ShowInfo(string message)
{
MessageBox.Show(message, "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
///
/// 窗体关闭时清理资源
///
protected override void OnFormClosed(FormClosedEventArgs e)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
}
serialPort?.Dispose();
base.OnFormClosed(e);
}
}
# 安装GTK运行时环境
sudo apt-get update
sudo apt-get install gtk-sharp3-dev
# 设置串口权限
sudo usermod -a -G dialout $USER
sudo chmod 666 /dev/ttyUSB*
sudo chmod 666 /dev/ttyACM*
# 运行应用程序
dotnet run
# 使用Homebrew安装GTK
brew install gtk+3
# 设置串口权限
sudo dseditgroup -o edit -a $USER -t user wheel
# 运行应用程序
dotnet run