
一、初始化窗口
作用:
• 程序刚打开时显示
• 后台连接行情服务器
• 加载历史 K 线 / 行情数据
• 加载完成后自动进入主界面
二、当前已实现的核心功能
- 窗体基础功能
• 无边框拖动(顶部栏按住可移动窗口)
• 最小化按钮 •
关闭按钮
• 基于 Sunny.UI 样式的界面
- 初始化与线程加载
• 窗体一打开,自动启动后台线程
• 线程里调用 Common.Init() 初始化行情系统
• 把当前窗口句柄传给底层,用于接收消息
• 线程设为后台线程,程序退出自动结束
-
Windows 消息接收
-
WM_SHOW_INFO:显示提示文字(加载中...)
-
WM_NET_MSG_HQ:接收行情服务器的网络消息
这是 C# 与 C++ 底层 / 行情接口 通信的标准方式。
- 行情网络消息处理(最关键) 当前能处理这些服务器消息:
① 连接状态 • msg=1:开始加载数据 / 连接失败 • msg=2:服务器断开
② 数据推送(msg=3)
• cmd="2":心跳包(保持连接)
• cmd="10":实时行情推送
• cmd="11":K 线数据推送
• cmd="102":历史行情请求返回
• cmd="103":正在下载历史 K 线 → 显示 "正在加载数据 {合约代码}"
• cmd="104":历史数据全部下载完成 → 显示 "数据加载完成",并返回 DialogResult.OK → 自动关闭并进入主程序
• cmd="105":补充行情数据
• cmd="106":补充数据下载完成
- 安全更新 UI
• 封装了 UpdateControlSafely()
• 跨线程更新标签文字,不会报错、不会卡死
- 异常保护
• 所有消息处理都包了 try-catch
• 出错会弹出提示,不会导致整个程序崩溃 7. 资源释放
• 窗体关闭时尝试终止加载线程
• 避免线程泄漏
cs
namespace newQiHuo
{
public partial class FrmInit : Form
{
private Thread _loadDataThread; // 保存线程引用
private CancellationTokenSource _cts; // 线程取消令牌源,替代Abort
// 定义结构体(补充代码完整性,实际项目中应该在公共区域定义)
public struct ST_NetMsg
{
public int msg; // 消息类型
public int datalen; // 数据长度
public string data; // 数据内容
// 补充其他必要字段...
}
public FrmInit()
{
InitializeComponent();
_cts = new CancellationTokenSource(); // 初始化取消令牌
}
private void FrmInit_Load(object sender, EventArgs e)
{
Common.gRecvHQNetMsgHandle = this.Handle;
// 初始化线程并设置为后台线程
_loadDataThread = new Thread(() => InitLoadData(this.Handle, _cts.Token));
_loadDataThread.IsBackground = true; // 后台线程,程序退出时自动终止
_loadDataThread.Start();
}
// 初始化加载数据线程(增加取消令牌支持)
private static void InitLoadData(IntPtr handle, CancellationToken token)
{
try
{
Common.Init(handle);
// 轮询取消信号,优雅退出
while (!token.IsCancellationRequested)
{
Thread.Sleep(100); // 避免空循环占用CPU
}
}
catch (OperationCanceledException)
{
// 捕获取消异常,正常退出
Common.ShowMsg("数据加载线程已正常终止");
}
catch (Exception ex)
{
Common.ShowMsg($"数据加载线程异常:{ex.Message}");
}
}
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
case Global.WM_SHOW_INFO: // 显示消息
OnShowInfo(m.WParam, m.LParam);
break;
case Global.WM_NET_MSG_HQ:
OnNetMsgHQ(m.WParam, m.LParam);
break;
default:
base.DefWndProc(ref m);
break;
}
}
// 安全更新UI控件的通用方法
private void UpdateControlSafely(Action action)
{
if (this.IsDisposed) return; // 防止窗体已释放时调用Invoke
if (this.InvokeRequired) // 判断是否跨线程
{
// 使用BeginInvoke避免线程阻塞(如果不需要等待UI更新完成)
this.BeginInvoke(action);
}
else
{
action(); // 已经是UI线程,直接执行
}
}
// 行情网络消息处理
protected void OnNetMsgHQ(IntPtr wParam, IntPtr lParam)
{
try
{
// 空指针检查
if (lParam == IntPtr.Zero) return;
// 从非托管内存解析结构体
ST_NetMsg myNetMsg = (ST_NetMsg)Marshal.PtrToStructure(lParam, typeof(ST_NetMsg));
if (myNetMsg.msg == 1)
{
if (myNetMsg.datalen == 0)
{
UpdateControlSafely(() => lbInfo.Text = "开始加载数据...");
}
else
{
Common.ShowMsg("行情服务器连接失败");
}
}
else if (myNetMsg.msg == 2)
{
Common.ShowMsg("行情服务器连接断开");
}
else if (myNetMsg.msg == 3)
{
string strData = myNetMsg.data?.Trim() ?? string.Empty; // 空值保护
if (string.IsNullOrEmpty(strData)) return;
// 修复:正确的Split用法 - 移除空元素
string[] vec = strData.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (vec.Length < 3) return;
string cmd = vec[0];
if (cmd == "2") // 心跳返回
{
}
else if (cmd == "10") // 实时行情推送
{
string code = vec[1];
string timeStamp = vec[2];
}
else if (cmd == "11") // K线推送
{
}
else if (cmd == "102") // 获取历史行情请求返回
{
string errCode = vec[1];
string errMsg = vec[2];
}
else if (cmd == "103") // 历史行情数据
{
if (vec.Length < 12) return;
string code = vec[1];
string timeStamp = vec[2];
string period = vec[3];
// 安全更新UI
UpdateControlSafely(() => lbInfo.Text = $"正在加载数据 {code}");
}
else if (cmd == "104") // 历史数据下载完成
{
if (vec.Length < 3) return;
string errCode = vec[1];
string errMsg = vec[2];
Common.gKlineHistoryManage.DownloadHistoryFinished();
// 安全更新UI并设置DialogResult
UpdateControlSafely(() =>
{
lbInfo.Text = "数据加载完成";
this.DialogResult = DialogResult.OK;
});
}
else if (cmd == "105") // 补充数据
{
if (vec.Length < 12) return;
string code = vec[1];
string timeStamp = vec[2];
string period = vec[3];
}
else if (cmd == "106") // 补充数据下载完成
{
if (vec.Length < 7) return;
string errCode = vec[1];
string errMsg = vec[2];
string code = vec[3];
string period = vec[4];
}
}
}
catch (Exception ex)
{
// 异常捕获,避免消息处理崩溃
Common.ShowMsg($"行情消息处理出错:{ex.Message}");
}
}
// 显示消息
protected void OnShowInfo(IntPtr wParam, IntPtr lParam)
{
try
{
if (lParam == IntPtr.Zero) return; // 空指针检查
ST_NetMsg myNetMsg = (ST_NetMsg)Marshal.PtrToStructure(lParam, typeof(ST_NetMsg));
string strInfo = myNetMsg.data ?? string.Empty;
// 安全更新UI
UpdateControlSafely(() => lbInfo.Text = strInfo);
}
catch (Exception ex)
{
Common.ShowMsg($"信息显示出错:{ex.Message}");
}
}
private void butMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
private void butclos_Click(object sender, EventArgs e)
{
this.Close();
}
private Point formPoint;
private void Toptaskbar_MouseDown(object sender, MouseEventArgs e)
{
formPoint = new Point(e.X, e.Y);
}
private void Toptaskbar_MouseMov(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Location = new Point(this.Location.X + e.X - formPoint.X, this.Location.Y + e.Y - formPoint.Y);
}
}
// 窗体关闭时释放资源
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
// 优雅取消线程,替代Abort
if (_cts != null)
{
_cts.Cancel();
_cts.Dispose();
}
// 等待线程结束(可选,根据实际需求)
if (_loadDataThread != null && _loadDataThread.IsAlive)
{
// 最多等待1秒,避免程序卡死
_loadDataThread.Join(1000);
_loadDataThread = null;
}
}
// 窗体释放时的清理
protected override void Dispose(bool disposing)
{
if (disposing)
{
_cts?.Dispose();
components?.Dispose();
}
base.Dispose(disposing);
}
}
// 补充必要的公共类定义(实际项目中应该在单独文件)
public static class Common
{
public static IntPtr gRecvHQNetMsgHandle;
public static KlineHistoryManage gKlineHistoryManage = new KlineHistoryManage();
public static void Init(IntPtr handle)
{
// 实际初始化逻辑
}
public static void ShowMsg(string msg)
{
// 实际消息显示逻辑
MessageBox.Show(msg);
}
}
public class KlineHistoryManage
{
public void DownloadHistoryFinished()
{
// 实际逻辑
}
}
public static class Global
{
public const int WM_SHOW_INFO = 0x0400 + 100; // 自定义消息
public const int WM_NET_MSG_HQ = 0x0400 + 101;
}
}