WinForm內嵌Unity(Socket通信)

最近有需求要实现WinForm和Unity交互,也就是通信,查过几个方案有用UnityWebPlayer Control组件的(由于版本问题所以我没尝试),也有把Winform打包成dll动态链接库然后unity内引入的,还有打包Unity.exe然后Winform内嵌入的,后面两种都可以。

一.Winform打包成dll动态链接库然后unity内引入

        1.总之先把界面画出来(大概有个样子)

        2.后台代码(我这里是winform充当服务器,unity充当客户端来连接实现socket通信)

                2.1 Winform:建立SocketServer类

    public class SocketServer
    {
        public Socket serverSocket;
        public Socket clientSocket;

        private string _ip = string.Empty;
        private int _port = 12345;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ip">监听的IP</param>
        /// <param name="port">监听的端口</param>
        public SocketServer(string ip, int port)
        {
            this._ip = ip;
            this._port = port;
        }
        public SocketServer(int port)
        {
            this._ip = "0.0.0.0";
            this._port = port;
        }
        static List<Socket> userOnline = new List<Socket>();
        private static readonly object textsLock;
        public Queue<string> texts = new Queue<string>();
        public void StartListen()
        {
            try
            {
                //1.0 实例化套接字(IP4寻找协议,流式协议,TCP协议)
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //2.0 创建IP对象
                IPAddress address = IPAddress.Parse(_ip);
                //3.0 创建网络端口,包括ip和端口
                IPEndPoint endPoint = new IPEndPoint(address, _port);
                //4.0 绑定套接字
                serverSocket.Bind(endPoint);
                //5.0 设置最大连接数量
                serverSocket.Listen(int.MaxValue);
                //MessageBox.Show(serverSocket.LocalEndPoint.ToString());
                //6.0 开始监听
                Thread thread = new Thread(ListenClientConnect);
                thread.IsBackground = true;
                thread.Start();

            }
            catch (Exception ex)
            {

            }
        }
        /// <summary>
        /// 监听客户端连接
        /// </summary>
        private void ListenClientConnect()
        {
            try
            {
                while (true)
                {
                    //阻塞当前的线程直到某个客户端连接,连接上则返回一个新的Socket(即客户端)
                    clientSocket = serverSocket.Accept();
                    userOnline.Add(clientSocket);//每连接上一个客户端,就将该客户端添加至客户端列表
                    Thread thread = new Thread(ReceiveMessage);//每连接上一个客户端,启动一个线程(用于接受客户端信息)
                    thread.Start(clientSocket);
                }
            }
            catch (Exception)
            {

            }
        }
        /// <summary>
        /// 接收客户端消息
        /// </summary>
        /// <param name="socket">来自客户端的socket</param>
        private void ReceiveMessage(object socket)
        {
            Socket clientSocket = (Socket)socket;
            byte[] buffer = new byte[1024 * 1024 * 2];
            while (true)
            {
                try
                {
                    //获取从客户端发来的数据
                    int length = clientSocket.Receive(buffer);
                    lock (textsLock)//如果textsLock没有被锁上,则可执行内部代码并主动锁上(PS:有序的执行,避免资源冲突)
                    {
                        texts.Enqueue(Encoding.UTF8.GetString(buffer, 0, length));//接收到消息则存入texts
                    }
                    //Console.WriteLine("接收客户端{0},消息{1}", clientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(buffer, 0, length));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                    break;
                }
            }
        }
    }

                2.2 Winform:在Form加载时开启服务,线程监听客户端(unity)连接 

        private void Form1_Load(object sender, EventArgs e)
        {
            socketServer = new SocketServer(12345);
            socketServer.StartListen();
            context = SynchronizationContext.Current;//同步上下文用
            Thread thread = new Thread(ShowMessage);//开启用于接收消息的线程
            thread.IsBackground = true;
            thread.Start();
        }
        private void button4_Click(object sender, EventArgs e)
        {
            Socket clientSocket = socketServer.clientSocket;//当前连接的客户端
            if (clientSocket != null)//如果客户端对象不为空
            {
                clientSocket.Send(Encoding.UTF8.GetBytes(textBox1.Text));//发送消息
            }
        }
        private void ShowMessage()//用于接收消息(线程启动)
        {
            while (true)
            {
                Thread.Sleep(200);
                if (socketServer.texts.Count > 0)
                {
                    //同步上下文显示消息在TextBox1
                    context.Send(e =>
                    {
                        textBox1.Text = socketServer.texts.Dequeue();
                    }, null);
                }
            }
        }

                  2.3 Unity:建立SocketClient类

public class SocketClient
{
    private string _ip = string.Empty;
    private int _port = 12345;
    public Socket clientSocket = null;
    SynchronizationContext context;//同步上下文
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="ip">连接服务器的IP</param>
    /// <param name="port">连接服务器的端口</param>
    public SocketClient(string ip, int port)
    {
        this._ip = ip;
        this._port = port;
    }
    public SocketClient(int port)
    {
        this._ip = "127.0.0.1";
        this._port = port;
    }

    /// <summary>
    /// 开启服务,连接服务端
    /// </summary>
    public void StartClient(GameObject gameObject)
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress address = IPAddress.Parse(_ip);
        IPEndPoint endPoint = new IPEndPoint(address, _port);
        clientSocket.Connect(endPoint);
        Debug.Log("連接成功");

        Thread thread = new Thread(new ParameterizedThreadStart(ReceiveMessage));
        thread.Start(gameObject);
        context = SynchronizationContext.Current;
    }
    public void ReceiveMessage(object gameObject)
    {
        byte[] buffer = new byte[1024 * 1024 * 2];
        while (true)
        {
            //阻塞当前线程直到收到消息
            int length = clientSocket.Receive(buffer);
            string text = Encoding.UTF8.GetString(buffer, 0, length);
            //直接写 cs.GetComponent<Text>().text = text; 会报错提示只能在主线程调用
            //同步上下文
            context.Send(e =>
            {
                ((GameObject)e).GetComponent<Text>().text = text;
            }, gameObject);

        }
    }
}

                2.4 然后创建一个脚本(csMain)把他绑定到一个空物体上

                 csMain代码:

using System.Windows.Forms;//免得你们找不到 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Windows.Forms.dll
using ChangeSite;

    public static Form form;
    void Start()
    {
        form = new Form1();
        form.Show();
        socketClient = new SocketClient(12345);
        socketClient.StartClient(this.cs);
    }
    private void OnDestroy()
    {
        form.Close();
    }

二、打包Unity项目成exe程序然后Winform内嵌入

        Unity打包exe后项目文件如下:

        然后在winform中用代码启动这个exe,并且嵌入到panel里面。

        

        Unity打包成客户端进行连接,代码和上面的一致,下面的是Winform作为服务端,在加载方法中进行监听客户端的连接,连接成功即可通信。具体代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Common;

namespace ChangeSite
{
    public partial class Form1 : Form
    {
        private SocketServer socketServer;
        EmbeddedExeTool fr = null;
        private SynchronizationContext context;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            socketServer = new SocketServer(12345);
            socketServer.StartListen();
            context = SynchronizationContext.Current;//同步上下文用
            Thread thread = new Thread(ShowMessage);//开启用于接收消息的线程
            thread.IsBackground = true;
            thread.Start();
        }
        private void btn_start_Click(object sender, EventArgs e)
        {
            panel1.Controls.Clear();
            if (fr != null && fr.IsStarted)//如果重新啟動(即fr不為空),則關閉
            {
                fr.Stop();
            }
            string path = Directory.GetCurrentDirectory() + @"../../../\Unity\ChangeSite\ChangeSite.exe";
            fr = new EmbeddedExeTool(panel1, "");
            fr.Start(path);
        }
        private void btn_send_Click(object sender, EventArgs e)
        {
            Socket clientSocket = socketServer.clientSocket;//当前连接的客户端
            if (clientSocket != null)//如果客户端对象不为空
            {
                clientSocket.Send(Encoding.UTF8.GetBytes(textBox1.Text));//发送消息
            }
        }
        private void ShowMessage()//用于接收消息
        {
            while (true)
            {
                Thread.Sleep(200);
                if (socketServer.texts.Count > 0)
                {
                    //同步上下文显示消息在TextBox1
                    //MessageBox.Show(Thread.CurrentThread.ManagedThreadId + "");
                    context.Send(e =>
                    {
                        textBox1.Text = socketServer.texts.Dequeue();
                    }, null);
                }
                //if (socketServer.clientSocket != null)
                //{
                //    byte[] buffer = new byte[1024 * 1024 * 2];
                //    int length = socketServer.clientSocket.Receive(buffer);
                //    //lock (textsLock)//如果textsLock没有被锁上,则可执行内部代码并主动锁上(PS:有序的执行,避免资源冲突
                //    if (length != 0)
                //    {
                //        //同步上下文显示消息在TextBox1
                //        context.Send(e =>
                //        {
                //            textBox1.Text = Encoding.UTF8.GetString(buffer, 0, length);
                //        }, null);
                //    }
                //}
            }
        }

        /// <summary>
        /// 嵌入外部exe
        /// </summary>
        public class EmbeddedExeTool
        {
            EventHandler appIdleEvent = null;
            Control ParentCon = null;
            string strGUID = "";

            public EmbeddedExeTool(Control C, string Titlestr)
            {
                appIdleEvent = new EventHandler(Application_Idle);
                ParentCon = C;
                strGUID = Titlestr;
            }

            /// <summary>
            /// 将属性<code>AppFilename</code>指向的应用程序打开并嵌入此容器
            /// </summary>
            public IntPtr Start(string FileNameStr)
            {
                if (m_AppProcess != null)
                {
                    Stop();
                }
                try
                {
                    ProcessStartInfo info = new ProcessStartInfo(FileNameStr);
                    info.UseShellExecute = true;
                    info.WindowStyle = ProcessWindowStyle.Minimized;
                    m_AppProcess = System.Diagnostics.Process.Start(info);
                    m_AppProcess.WaitForInputIdle();
                    Application.Idle += appIdleEvent;
                    Application.ApplicationExit += m_AppProcess_Exited;

                }
                catch
                {
                    if (m_AppProcess != null)
                    {
                        if (!m_AppProcess.HasExited)
                            m_AppProcess.Kill();
                        m_AppProcess = null;
                    }
                }
                return m_AppProcess.Handle;

            }
            /// <summary>
            /// 确保应用程序嵌入此容器
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void Application_Idle(object sender, EventArgs e)
            {
                if (this.m_AppProcess == null || this.m_AppProcess.HasExited)
                {
                    this.m_AppProcess = null;
                    Application.Idle -= appIdleEvent;
                    return;
                }

                while (m_AppProcess.MainWindowHandle == IntPtr.Zero)
                {
                    Thread.Sleep(100);
                    m_AppProcess.Refresh();
                }
                Application.Idle -= appIdleEvent;

                EmbedProcess(m_AppProcess, ParentCon);
            }
            /// <summary>
            /// 应用程序结束运行时要清除这里的标识
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void m_AppProcess_Exited(object sender, EventArgs e)
            {
                if (m_AppProcess != null && !m_AppProcess.HasExited)
                {
                    m_AppProcess.Kill();
                    m_AppProcess = null;
                }
            }
            /// <summary>
            /// 将属性<code>AppFilename</code>指向的应用程序关闭
            /// </summary>
            public void Stop()
            {
                if (m_AppProcess != null)// && m_AppProcess.MainWindowHandle != IntPtr.Zero)
                {
                    try
                    {
                        if (!m_AppProcess.HasExited)
                        {
                            m_AppProcess.Kill();
                            Application.ApplicationExit -= m_AppProcess_Exited;//每次重新啟動都減一次調用
                        }
                    }
                    catch (Exception)
                    {
                    }
                    m_AppProcess = null;
                }
            }


            #region 属性
            /// <summary>
            /// application process
            /// </summary>
            Process m_AppProcess = null;

            /// <summary>
            /// 标识内嵌程序是否已经启动
            /// </summary>
            public bool IsStarted { get { return (this.m_AppProcess != null); } }

            #endregion 属性

            #region Win32 API
            [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
                 CharSet = CharSet.Unicode, ExactSpelling = true,
                 CallingConvention = CallingConvention.StdCall)]
            private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);

            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

            [DllImport("user32.dll", SetLastError = true)]
            private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

            [DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
            private static extern long GetWindowLong(IntPtr hwnd, int nIndex);

            public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
            {
                if (IntPtr.Size == 4)
                {
                    return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
                }
                return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
            }
            [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
            public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
            [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
            public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);

            [DllImport("user32.dll", SetLastError = true)]
            private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);

            [DllImport("user32.dll", SetLastError = true)]
            private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

            [DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
            private static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam);

            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr GetParent(IntPtr hwnd);

            [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
            static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

            private const int SWP_NOOWNERZORDER = 0x200;
            private const int SWP_NOREDRAW = 0x8;
            private const int SWP_NOZORDER = 0x4;
            private const int SWP_SHOWWINDOW = 0x0040;
            private const int WS_EX_MDICHILD = 0x40;
            private const int SWP_FRAMECHANGED = 0x20;
            private const int SWP_NOACTIVATE = 0x10;
            private const int SWP_ASYNCWINDOWPOS = 0x4000;
            private const int SWP_NOMOVE = 0x2;
            private const int SWP_NOSIZE = 0x1;
            private const int GWL_STYLE = (-16);
            private const int WS_VISIBLE = 0x10000000;
            private const int WM_CLOSE = 0x10;
            private const int WS_CHILD = 0x40000000;

            private const int SW_HIDE = 0; //{隐藏, 并且任务栏也没有最小化图标}
            private const int SW_SHOWNORMAL = 1; //{用最近的大小和位置显示, 激活}
            private const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL}
            private const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}
            private const int SW_SHOWMAXIMIZED = 3; //{最大化, 激活}
            private const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED}
            private const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置显示, 不激活}
            private const int SW_SHOW = 5; //{同 SW_SHOWNORMAL}
            private const int SW_MINIMIZE = 6; //{最小化, 不激活}
            private const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE}
            private const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE}
            private const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL}
            private const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL}
            private const int SW_MAX = 10; //{同 SW_SHOWNORMAL}

            #endregion Win32 API

            /// <summary>
            /// 将指定的程序嵌入指定的控件
            /// </summary>
            private void EmbedProcess(Process app, Control control)
            {
                // Get the main handle
                if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
                try
                {
                    // Put it into this form
                    SetParent(app.MainWindowHandle, control.Handle);
                }
                catch (Exception)
                { }
                try
                {
                    // Remove border and whatnot               
                    SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
                    SendMessage(app.MainWindowHandle, WM_SETTEXT, IntPtr.Zero, strGUID);
                }
                catch (Exception)
                { }
                try
                {
                    // Move the window to overlay it on this window
                    MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
                }
                catch (Exception)
                { }
            }

            [DllImport("User32.dll", EntryPoint = "SendMessage")]
            private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);

            const int WM_SETTEXT = 0x000C;
        }
    }
}

 然后效果也很nice!

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: Winform Framework 是一个用于 Windows 应用程序开发的框架,而 Unity 是一个专门用于游戏开发的引擎。将 Unity 内嵌到 Winform 中,可以实现更丰富的交互式图形界面和更多的3D场景效果,使得开发者可以更轻松地开发出具有更高质量的富客户端程序。 在内嵌 UnityWinform 中时,需要注意以下几点: 1. Unity 版本的问题:Unity 的版本和 Winform 的版本兼容性需要分析确定,在开发时需要同时考虑两种框架的版本问题。 2. 窗口和游戏的互动:开发者需要思考如何让两个框架之间的窗口和游戏实时交互。例如如何将窗口参数传递给游戏,或游戏如何通知窗口进行修改。 3. 控件和游戏的融合:开发者需要思考如何将 Winform 中的控件融合到 Unity 游戏中。例如如何将 Winform 中的按钮与 Unity 游戏做出绑定。 总之,内嵌 UnityWinform 中可以实现更强大更丰富的应用体验和效果,但同时也需要考虑开发难度和版本兼容性等问题。开发者需要在实际开发中根据具体情况进行合理的方案设计。 ### 回答2: Winform框架是一个Windows平台的应用程序框架。它是一个强大的平台,可以用来创建Windows桌面应用程序。Unity是一个强大的游戏引擎,用于创建3D和2D游戏、模拟和其他交互式内容。 将Unity内嵌到Winform框架中可以实现将Unity的游戏引擎和Winform的UI框架相结合,让游戏引擎和普通的Windows应用程序完美结合。这使得Winform框架中的应用程序可以拥有更加炫酷的交互效果,而Unity游戏引擎也可以嵌入到其他应用程序中使用。此外,这种结合还可以使Unity游戏引擎能够更好地运行在Windows操作系统上。 在实现Unity内嵌Winform框架的过程中,需要借助一些第三方控件,如“UnityWinForms”。UnityWinForms是一个开源的工具包,可以在Winform框架中嵌入Unity引擎,实现UnityWinform框架的无缝结合。此外,还需要在工程中嵌入Unity引擎的相关文件,如dll等。 总的来说,在Winform框架中实现Unity内嵌,可以将两个强大的平台结合起来,实现更加丰富和全面的应用程序。 ### 回答3: Winform Framework 是 Microsoft 提供的一种用于在 Windows 操作系统上创建本地应用程序的框架。Unity 是一种用于游戏开发的跨平台 3D 游戏引擎。将 Unity 嵌入 Winform Framework 可以使得开发者能够更方便地开发基于 Unity 的游戏应用程序。 Unity 提供了一种名为“Unity Editor”的编辑器工具,该工具可让开发者创建和编辑 3D 游戏场景、物体和其他元素。为了将 Unity 嵌入 Winform Framework,开发者需要使用一些额外的工具和技术。其中最常用的是使用 WindowsFormsHost 控件。这个控件可以将 Winform 应用程序的表单与 WPF(Winodws Presentation Foundation)应用程序混合在一起。 通过使用 WindowsFormsHost 控件,开发者可以将 Unity 编辑器内部的 3D 场景嵌入到 Winform 应用程序中的窗口中。此外,还可以使用 WindowsFormsHost 控件与 Winform 应用程序之间进行数据交换和通信。 将 Unity 嵌入到 Winform Framework 中可以为开发者提供许多便利。首先,开发者可以使用 Winform 应用程序中的其他控件,如按钮、文本框和分组框等,来控制 Unity 场景的各个方面。其次,在调试和测试应用程序时,将 Unity 嵌入到 Winform Framework 中可以使得开发者更方便地查看 Unity 应用程序的内部状态和调试信息。最后,由于 Winform 应用程序已经成为了 Windows 操作系统的一部分,所以将 Unity 嵌入到 Winform Framework 中可以使得开发者更容易将他们的游戏应用程序发布到 Windows 平台上。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值