期货软件开发「启动加载页 / 初始化窗口」

一、初始化窗口

作用:

• 程序刚打开时显示

• 后台连接行情服务器

• 加载历史 K 线 / 行情数据

• 加载完成后自动进入主界面

二、当前已实现的核心功能

  1. 窗体基础功能

• 无边框拖动(顶部栏按住可移动窗口)

• 最小化按钮 •

关闭按钮

• 基于 Sunny.UI 样式的界面

  1. 初始化与线程加载

• 窗体一打开,自动启动后台线程

• 线程里调用 Common.Init() 初始化行情系统

• 把当前窗口句柄传给底层,用于接收消息

• 线程设为后台线程,程序退出自动结束

  1. Windows 消息接收

  2. WM_SHOW_INFO:显示提示文字(加载中...)

  3. WM_NET_MSG_HQ:接收行情服务器的网络消息

这是 C# 与 C++ 底层 / 行情接口 通信的标准方式。

  1. 行情网络消息处理(最关键) 当前能处理这些服务器消息:

① 连接状态 • 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":补充数据下载完成

  1. 安全更新 UI

• 封装了 UpdateControlSafely()

• 跨线程更新标签文字,不会报错、不会卡死

  1. 异常保护

• 所有消息处理都包了 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;
    }
}
相关推荐
只与明月听2 小时前
RAG深入学习之Emabedding
前端·python·面试
We་ct2 小时前
React Scheduler & Lane 详解
前端·react.js·前端框架·reactjs·个人开发·任务调度·优先
kgduu2 小时前
js之json处理
前端·javascript·json
qq_390760392 小时前
简单的线程安全日志记录器
开发语言·数据库·c#
@木尘2 小时前
前端面试【 八股文篇】
前端·面试·职场和发展
吴佳浩3 小时前
OpenClaw、Claude Code 等 Agent 为什么都选择 Node.js?
前端·人工智能·langchain
小小小小宇3 小时前
React 19 useActionState 深度解析 & Vue 2.7 移植实战
前端
远山枫谷3 小时前
Vue2 vs Vue3 全面对比(含代码示例+迁移指南)
前端·vue.js
z止于至善3 小时前
服务器发送事件(SSE):前端实时通信的轻量解决方案
前端·web·服务器通信