草庐IT

【虚拟仿真】Unity3D实现从浏览器拉起本地exe程序并传参数

恬静的小魔龙 2023-05-26 原文

推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

最近有项目需求,从浏览器调起来本地的exe程序,并且还要传参、传数据。

研究了一下,总结出来。

流程图如下所示:

二、正文

2-1、实现方法

浏览器实现拉起本地exe的方法,就是向系统中添加一个注册表,注册表找到指定路径下的程序,拉起。

这个注册表就是类似于HTTP的私有协议(本地有效),可以拉起本地exe程序。

注册表如下:

[HKEY_CLASSES_ROOT\virtualcourse.test]
[HKEY_CLASSES_ROOT\test\DefaultIcon]
@="F:\test\mytest,1"
[HKEY_CLASSES_ROOT\test\shell]
[HKEY_CLASSES_ROOT\test\shell\open]
[HKEY_CLASSES_ROOT\test\shell\open\command]
@="F:\test\mytest" "%1" 

DefaultIcon 是默认程序的icon位置
command 是记录程序的位置

注册表的结构:

2-2、制作注册表程序

新建个控制台程序,搭建一下UI:

编辑代码:

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(txtCourseID.Text.Trim()))
                {
                    MessageBox.Show("课程ID不能为空");
                    return;
                }
                string strPrimaryKey = "virtualcourse." + txtCourseID.Text.Trim();
                RegistryKey key = Registry.ClassesRoot;
                RegistryKey regPrimaryKey = key.CreateSubKey(strPrimaryKey);
                regPrimaryKey.SetValue("", strPrimaryKey + " Protocol");
                regPrimaryKey.SetValue("URL Protocol", "");
                RegistryKey regDefaultIconKey = key.CreateSubKey(strPrimaryKey + "\\DefaultIcon");
                string strExePathName = Application.StartupPath + "\\" + Application.ProductName;
                regDefaultIconKey.SetValue("", strExePathName + ",1");
                RegistryKey regshellKey = key.CreateSubKey(strPrimaryKey + "\\shell");
                RegistryKey regshellopenKey = key.CreateSubKey(strPrimaryKey + "\\shell\\open");
                RegistryKey regshellopencommandKey = key.CreateSubKey(strPrimaryKey + "\\shell\\open\\command");
                regshellopencommandKey.SetValue("", string.Format("\"{0}\" \"%1\"", strExePathName));
                key.Close();
                MessageBox.Show("生成注册表成功!");
                this.btnAddReg.Enabled = false;
                this.txtCourseID.ReadOnly = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("生成注册表失败:" + ex.Message);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(txtCourseID.Text.Trim()))
                {
                    MessageBox.Show("课程唯一ID不能为空");
                    return;
                }
                string strPrimaryKey = "virtualcourse." + txtCourseID.Text.Trim();
                RegistryKey delKey = Registry.ClassesRoot;
                RegistryKey regPrimaryKey = delKey.OpenSubKey(strPrimaryKey, true);
                //判断要删除的regPrimaryKey是否存在
                if (regPrimaryKey != null)
                {
                    delKey.DeleteSubKeyTree(strPrimaryKey, true);
                }
                delKey.Close();
                MessageBox.Show("删除注册表成功!");
                this.btnAddReg.Enabled = true;
                this.txtCourseID.ReadOnly = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show("删除注册表失败:" + ex.Message);
            }
        }
    }
}

生成解决方案:

在bin→Debug目录下运行程序:

以管理员的身份运行:

随便写入课程ID,点击生成注册表即可:

PS:也可以手动添加注册表,就是有点麻烦,在实际开发中,需要用到打包工具,将添加注册表的事项添加到安装过程中,就可以在安装完程序后,注册表也添加完成。

2-3、HTML网页调用本地exe

首先,新建个.txt文件,编辑后再改成.html后缀名即可,代码参考:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>web打开本地exe</title>
    <script src="vue.js"></script>
</head>
<body>
    <div id="app">
        <span>请输入课程ID:</span><input type="text" name="" id="" v-model="courseId" style="width: 400px;">
        <a :href="hrefValue" @click="getHrefValue" style="display: inline-block;">测试打开本地应用并传参</a>
    </div>
    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                courseId:'',
                paramValue: 'token=123456789',
                hrefValue:'',
            },
            methods: {
                getHrefValue()
                {
                    this.hrefValue='virtualcourse.'+this.courseId+'://'+this.paramValue;
                }
            },
            mounted() {
                // this.getHrefValue();
            },
        })
    </script>
</body>
</html>

将打包后的程序放到添加注册表的程序目录下,因为在生成注册表的时候,将这个目录记录进去了:

当然,也可以手动改这个路径。

2-4、生成Unity的exe程序

新建个Unity项目,新建场景,搭建场景:

新建脚本命名为JSCallUnity.cs,编辑代码:

using System;
using UnityEngine;
using UnityEngine.UI;

public class JSCallUnity : MonoBehaviour
{
    public InputField m_Input;

    void Start()
    {
        // 从控制台接收参数
        ReadEnvironmentData();
    }

    private void ReadEnvironmentData()
    {
    	//用来接收HTML发来的数据
        string[] CommandLineArgs = Environment.GetCommandLineArgs();
        if (CommandLineArgs.Length < 2 || CommandLineArgs[1] == "")
        {
            m_Input.text = "没有接收到参数";
            Application.Quit();//启动时没有参数则退出
        }
        else
        {
            ParseInitData(CommandLineArgs[1]);
        }
    }
	
	//解析参数
    void ParseInitData(string data)
    {
        //解析参数
        m_Input.text = data;
    }
}

注意:
主要的方法就是Environment.GetCommandLineArgs();用来接收HTML发来的命令行参数数据。
官方解释:返回包含当前进程的命令行参数的字符串数组。
返回值是一个string[],当Length>1的时候就是带参数在UnityEditor模式
也就是编辑器模式会有默认参数返回,Length是大于1的

拖进去:

打包为exe:

复制到生成注册表的程序中,将命名改成生成注册表的生成的exe名字:


打开HTMl文件,输入生成的注册表的课程ID,点击测试打开本地应用:

运行结果:

后面就是对数据进行处理的事情了,这个就跟后端的程序沟通如何定义数据类型,如何解析数据即可了,这里就不展开讲了。

三、后记

本篇文章实现了从生成注册表,然后用浏览器通过注册表HTTP协议拉起本地的EXE程序。

然后Unity程序中使用了Environment.GetCommandLineArgs();方法获取到HTML发过来的数据。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

有关【虚拟仿真】Unity3D实现从浏览器拉起本地exe程序并传参数的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  3. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  4. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  5. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  6. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  9. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  10. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

随机推荐