通过URI Scheme实现从Web网页上打开本地C++应用程序(以腾讯会议为例,附完整实现源码)

通过URI Scheme实现从Web网页上打开本地C++应用程序(以腾讯会议为例,附完整实现源码)

目录

1、需求描述

2、选择URI Scheme实现

3、何为URI Scheme?

4、将自定义的URL Scheme信息写入注册表的C++源码实现

5、如何实现最开始的3种需求

6、后续需要考虑的细节问题


       之前陆续收到一些从Web页面上启动我们C++客户端软件的需求,希望我们能提供一些技术上的支持与协助,支持从Web网页上将我们的C++客户端软件启动起来。于是我大概地研究了相关的实现方法,下面把研究的过程与结果在此做一个分享,希望能给大家提供一个借鉴或参考。

C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达10000多个,欢迎订阅,持续更新...)https://blog.ZEEKLOG.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新500多篇,订阅量已达8000多个,欢迎订阅,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/article/details/140824370C++ 软件开发从入门到实战(重点专栏,专栏文章已更新300多篇,欢迎订阅,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.ZEEKLOG.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/category_2276111.html

1、需求描述

       最近多个第三方开发厂商为了快速集成我们的软件及系统(把我们的软件系统作为子系统,融入到他们的大型业务系统中),不想基于我们软件SDK做费时费力的二次开发,而是直接从Web网页上启动我们客户端软件。简单地归纳了一下,类似的需求可以分以下几类:

1)仅仅是从Web网页上将C++客户端软件启动起来,即将软件调起来就行了,没有后续自动操作。用户根据自己的需要,手动操作我们的软件。

2)从Web网页上将C++客户端软件启动起来,并给软件传递服务器地址、用户名和密码,让软件自动发起登陆,登录成功后显示登录后的界面。

3)从Web网页上将C++客户端软件启动起来,并且启动时传递一些信息,让软件执行指定的一些操作。比如给软件传递服务器地址、用户名和密码等信息,让软件自动发起登录,并加入到指定的会议中。

       其实上述需求可以简单的归结为,将C++客户端软件启动起来,并给C++客户端软件传递一些命令行参数,C++客户端软件解析出参数,执行指定的操作。

      以浏览器打开腾讯会议的会议链接为例,比如在IM软件中分享的腾讯会议的会议链接如下所示:

点击上面的会议链接,系统用系统中安装的浏览器打开链接(或者手动将上述会议链接拷贝到浏览器中打开),如下所示:

点击加入会议按钮,会弹出是否要打开本地安装的腾讯会议程序的提示框:

 点击打开腾讯会议,将本地安装的腾讯会议启动起来,并自动加入到指定的会议中:

2、选择URI Scheme实现

       如果是在C++程序中启动一个C++软件,会比较简单,只要获取一下目标软件的安装路径(可以从注册表中读取,安装程序时会将软件的安装路径写到注册表中),就能直接通过软件的全路径,直接将软件启动起来了。

       现在越来越多的系统都转向B/S架构,用户可以随处随地访问到系统中去,只要有网络有电脑就行了,不用再安装各种客户端软件了。就像我们上面提到的一些客户一样,因为某些业务场景的需要,可能需要从Web网页上启动系统中安装的基于C/S架构的客户端软件。

       Web网页一般都是在浏览器中打开的,出于安全的原因,Web浏览器既不能直接读写注册表,即无法通过访问注册表获取要启动软件的安装路径,所以无法像C++程序那样直接启动二进制文件,所以在Web网页中想启动本地的应用程序似乎遇到了问题。其实这并不是问题,我们使用URI Scheme技术与规范就能实现这样的需求

3、何为URI Scheme?

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAbGluay3liJ3miaw=,size_13,color_FFFFFF,t_70,g_se,x_16

       URI,全称是Uniform Resource Identifier,统一资源标志符。在Web开发领域,其表示的是Web上每一种可用的资源,如HTML文档、图片、视频等。URI Scheme,我们称之为URI方案,是一种技术规范,其中的URI是个更宽泛的概念,它可以是一个本地的文件,也可以是一个网络上的视频。

       从Web网页中启动本地应用程序的URI Scheme规范中,需要将本地应用程序的信息通过写注册表的方式注册到系统中,然后在网页中使用“SchemeName://”就可以只在启动本地程序了。具体的做法是,在注册表的HKEY_CLASSES_ROOT下创建一个自定义的SchemeName注册表节点,然后再在该节点下创建多个节点,并在给相关节点设置注册表键值。

       以QQ内嵌的QQGame为例,添加注册表信息的步骤如下:

1)在HKEY_CLASSES_ROOT下创建QQGameProtocol节点

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAbGluay3liJ3miaw=,size_17,color_FFFFFF,t_70,g_se,x_16

      QQGameProtocol就是对应的Scheme方案名称,也是Web页面上启动对应程序的URL的前缀名称,即QQGameProtocol://。然后给该节点添加一个URL Protocol名称的键值,将其Value设置为本地应用程序的完整路径。对于当前的QQGameProtocol,就是C:\Users\Public\Documents\Tencent\QQGameMicro\QQGwp.exe,如上图所示。

2)在QQGameProtocol根节点下创建DefaultIcon节点

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAbGluay3liJ3miaw=,size_17,color_FFFFFF,t_70,g_se,x_16

       给DefaultIcon节点设置默认的字符串键值(REG_SZ类型),其Value的格式为“应用程
序全路径,图标索引”的形式,该键值是用来指定该URI方案使用的图标。本例中的Value
为:C:\Users\Public\Documents\Tencent\QQGameMicro\QQGwp.exe,1,如上图所示。

3)在QQGameProtocol下创建shell节点

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAbGluay3liJ3miaw=,size_17,color_FFFFFF,t_70,g_se,x_16

       先在QQGameProtocol下创建shell节点,然后在shell节点下创建open节点,然后在open节点创建command节点。shell节点和open节点不需要设置键值,command节点需要设置键值,其键值用来指定启动目标应用程序时是否给目标程序传递命令行参数。

       一般只需要设置传递一个参数即可,比如当前Scheme下的"C:\Users\Public\Documents\Tencent\QQGameMicro\QQGwp.exe" "%1"。如果要传递多个参数,可以自定义一个组合格式,命令行只用一个参数即可。比如我们要给目标程序传递服务器地址、用户名和密码,可以采用这样的组合格式:

#serveraddr=192.168.72.135#username=admin1#password=123456

即将要传递的多个参数按指定的格式组合起来生成一个命令行字符串参数即可。

       当在Web页面上点击“SchemeName://”链接时,就会到系统注册表的HKEY_CLASSES_ROOT节点下查找SchemeName节点项,找到后取出目标应用程序的全路径,并查找传递的命令行参数个数,这样就能把本地的目标应用程序启动起来了。

       如果要给目标程序传递参数,则使用“SchemeName://参数”的形式。经测试发现,如果在command节点中设置了%1传递参数的标识,则Web网页中设置的URL必须要带参数,即“SchemeName://参数”。如果使用不带参数的URL:“SchemeName://”,则无法启动目标程序。

       那如何既要支持不传参数启动,也要支持传参数启动呢?难道要在注册表中创建两个不同的SchemeName节点?其实不用这么麻烦,使用一个带参数的SchemeName节点就够了,对于直接启动目标程序不带启动参数的,也可以携带一个标识参数,在程序中约定不传参数的标识符,比如noparam,当程序中解析出noparam,则表示是不带参数启动的,直接启动程序即可,不用做后续的操作。


        在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:【C++软件异常与异常排查从入门到精通系列教程】该精品技术专栏的订阅量已达到10000多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,已经更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总https://blog.ZEEKLOG.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法详细讲述了C++软件的调试方法与手段详细介绍分析C++软件问题的常用分析工具,以图文并茂的方式给出具体的项目问题实战分析实例(详细讲述分析排查过程,很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:【C/C++实战进阶】(该专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达8000多个,专栏文章已经更新到500多篇,持续更新中...)

C/C++实战进阶(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法C++11及以上新特性(开源代码中可能会用到很多新特性(比如WebRTC开源库),日常编码中也会用到部分新特性,面试时也会频繁地涉及到,学习新特性很有必要)、常用C++开源库的介绍与使用(比如SQLite、libcurl、libwebsockets、libevent、jsoncpp/RapidJson、Redis、RabbitMQ、MongoDB、MQTT、ZooKeeper、OpenCV、FFmpeg、SDL、GStreamer、Live555、ReactOS等)、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(引发C++软件异常的常见原因分析与总结、排查C++软件异常的手段与方法、分析C++软件异常的基础知识、使用常用软件分析工具分析C++软件问题、多个项目实战问题分析案例分享等)、设计模式(单例模式、工厂模式、观察者模式、状态模式等)、网络基础知识与网络问题分析进阶内容(实战问题分析实例分享)等。本专栏的内容都是建立在项目实践的基础上,来源于项目实战,服务于项目实战,很有实战参考价值!

专栏3:【分析C++软件问题的实用软件与高效工具实战案例集锦】

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro以及内存泄漏检测工具等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏4:【VC++常用功能代码封装】

VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5:【C/C++软件开发从入门到实战】(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,专栏文章已经更新到300多篇,持续更新中!欢迎订阅!) 

C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.ZEEKLOG.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。

4、将自定义的URL Scheme信息写入注册表的C++源码实现

       下面给出将自定义的URL Scheme信息写入注册表的C++源码实现:

BOOL WriteURISchemaReg() { // exe程序的完整路径 CString strExePath = m_strInstallPath + _T("xyzlink.exe"); // URI Scheme名称 CString strProtocolName = _T("XyzlinkProtocol"); HKEY hRootKey = NULL; DWORD dwKeyValue = 0; DWORD dwDisposition = 0; UCHAR szBuf[MAX_PATH] = { 0 }; // 1、在HKEY_CLASSES_ROOT下创建URI Schema相关注册表的根节点RootNode long lRet = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, ProtocalNodeName, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hRootKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { return FALSE; } // 给根节点RootNode设置值1 lRet = ::RegSetValueEx(hRootKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strProtocolName, strProtocolName.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 给根节点RootNode设置值2 CString strKey = _T("URL Protocol"); lRet = RegSetValueEx(hRootKey, strKey.GetBuffer(0), 0, REG_SZ, (LPBYTE)(LPCTSTR)strExePath, strExePath.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 2、在根节点RootNode下创建DefaultIcon节点 strKey = _T("DefaultIcon"); HKEY hDefaultIconKey = NULL; lRet = RegCreateKeyEx(hRootKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hDefaultIconKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 给RootNode\DefaultIcon节点设置值 CString strExePathPlus = strExePath + _T(",1"); lRet = RegSetValueEx(hDefaultIconKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strExePathPlus, strExePathPlus.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 3、在RootNode\DefaultIcon节点下创建子节点shell strKey = _T("shell"); HKEY hShellKey = NULL; lRet = RegCreateKeyEx(hDefaultIconKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hShellKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 4、在RootNode\DefaultIcon\shell节点下创建子节点open strKey = _T("open"); HKEY hOpenKey = NULL; lRet = RegCreateKeyEx(hShellKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hOpenKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 5、在RootNode\DefaultIcon\shell\open节点下创建子节点command strKey = _T("command"); HKEY hCommandKey = NULL; lRet = RegCreateKeyEx(hOpenKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hCommandKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 给command节点设置值(命令行参数) CString strCmdParam; strCmdParam.Format(_T("\"%s\" \"%%1\""), strExePath); lRet = RegSetValueEx(hCommandKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strCmdParam, strCmdParam.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hCommandKey); RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } RegCloseKey(hCommandKey); RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return TRUE; }

5、如何实现最开始的3种需求

       搞清楚了使用URI Scheme规范实现从Web页面中启动本地应用程序的方法,下面我们再回到最开始提出的3个需求,看看如何去实现。

       第一种需求不需要传递参数,后面两种需求则需要传递参数,我们使用一个带参数传递的Scheme节点即可。我们可以定义一个启动type类型标识launchtype,对于直接启动的,type为noparam。对于启动后发起自动登录的,type为autologin;对于启动后需要执行具体操作的,可以根据具体的业务,定义具体的type类型,这样更灵活。

       对于目标应用程序,则可以根据不同的type类型,解析对应的参数数据,并对参数的合法性进行校验。

       下面把Web网页的测试代码给出来,保存成.html文件,用浏览器打开即可:

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Start exe demo</title> </head> <body> <a href="SchemeName://">打开目标程序</a> </body > </html >

6、后续需要考虑的细节问题

       上面大概说了一下问题的解决办法和思路,其实还有很多细节需要去考虑。比如下面的几种场景:

1)程序可能没有安装

       如果目标应用程序没有安装,肯定是启动不起来的,是不是要检测启动失败的原因,然后自动跳转到安装程序的下载页面。

2)仅将目标程序启动起来,但目标程序已经运行

       一般情况下,很多程序都是单实例运行的,即只允许运行一个实例。假定目标程序是单实例运行的,点击Web页面中的启动程序的链接时,已经有个进程在运行了,目标程序中要弹出程序已经运行的提示,并将已经启动的程序拉到前端显示。

3)启动程序后需要有后续操作,但目标程序已经运行

       启动程序后需要有后续操作,比如自动发起登录,但此时目标应用程序已经运行。如果已启动的进程还没登录,是要自动发起登录?还是搁置不管?如果已启动的进程已经登录,则提示已经启动,并将已启动的主窗口拉到最前显示。如果目标程序已经启动且已经登录成功,则需要将命令行参数发给已启动的进程,让该进程执行要执行的操作,比如加入会议。

PS. 微软官方说明连接:

Registering an Application to a URI Scheme

Read more

腾讯云智能客服机器人Java集成实战:从接入到生产环境优化

最近在项目中接入了腾讯云的智能客服机器人,把整个集成过程和一些优化经验记录下来,希望能帮到有类似需求的同学。自己动手搭过客服系统的朋友都知道,从零开始搞一套,不仅开发周期长,而且智能语义理解这块的门槛太高了,效果还很难保证。直接集成成熟的SaaS服务,就成了一个快速又靠谱的选择。 1. 为什么选择腾讯云智能客服? 在技术选型阶段,我们对比了几家主流云厂商的方案。阿里云的智能客服功能也很强大,生态完善,但它的API设计风格和我们团队的历史技术栈适配起来有点别扭。AWS Lex的优势在于和海外其他AWS服务无缝集成,但国内访问的延迟和合规性是需要考虑的问题。腾讯云智能客服吸引我们的点主要有几个: * API设计友好:它的RESTful API文档清晰,错误码规范,并且提供了Java、Python等多种语言的SDK,上手速度快。 * 计费透明灵活:支持按调用量、按坐席等多种计费模式,初期可以先用调用量模式试水,成本可控。 * NLP能力本土化强:在中文场景下的意图识别和情感分析准确率不错,特别是针对一些行业术语和网络用语,理解得比较到位。 综合来看,对于国内业务为主、追求快速集

【STM32项目开源】基于STM32的智能家居环境监测系统

【STM32项目开源】基于STM32的智能家居环境监测系统

目录 一、设计背景和意义 1.1设计背景 1.2设计意义 二、实物效果展示 2.1实物图片 2.2实物演示视频 三、硬件功能简介 3.1项目功能详解 3.2元器件清单 四、主框图与软件流程图 五、硬件PCB展示 六、软件程序设计 七、项目资料包内容          资料获取:查看主页介绍“充哥单片机设计” 一、设计背景和意义 1.1设计背景         随着物联网(IoT)、嵌入式系统和云计算等技术的飞速发展,智能家居系统正在逐渐改变人们的生活方式。智能家居不仅仅是简单的远程开关控制,而是向着环境感知、自主判断、智能决策的方向不断演进。特别是在城市化进程加快、生活节奏加快的背景下,用户对生活便捷性、家庭安全性和环境舒适度的要求不断提高,这对智能家居系统的综合感知、智能响应能力提出了更高的要求。         当前市面上的智能家居产品多以分立模块存在,系统功能较为单一,

【XR技术介绍】一文理清 OpenVR、OpenXR、SteamVR 与各厂商 SDK等容易混淆的概念

【XR技术介绍】一文理清 OpenVR、OpenXR、SteamVR 与各厂商 SDK等容易混淆的概念

在虚拟现实、混合现实开发领域,OpenVR、OpenXR、SteamVR 以及各硬件厂商专属 SDK,是我们经常遇到的东西。是不是傻傻分不清楚,容易混淆它们的定位、归属、功能与适用场景,这些到底是标准协议?还是插件?还是开发工具包?本文将从概念定义、制定 / 开发主体、核心职能、技术关系、适用场景多个维度,系统拆解它们差异与关联,帮你建立完整的认知框架。 一、基础概念总览:先分清 “标准” 与 “实现” 在正式拆解前,先建立一个核心认知:OpenXR 与 OpenVR 是行业标准 / 接口规范,属于抽象的技术协议;SteamVR 是基于标准的 runtime 运行时实现,是可落地的软件平台;硬件厂商 SDK 则是设备专属的底层驱动与开发工具包,是硬件直连的桥梁。标准解决 “兼容统一” 问题,运行时与

SmolVLA高算力适配:TensorRT加速可行性分析与ONNX导出实操

SmolVLA高算力适配:TensorRT加速可行性分析与ONNX导出实操 1. 项目背景与核心价值 SmolVLA作为一款专为经济实惠机器人技术设计的紧凑型视觉-语言-动作模型,在资源受限环境下展现出了令人印象深刻的性能。这个约5亿参数的模型能够同时处理视觉输入、语言指令和动作输出,为机器人控制提供了端到端的解决方案。 在实际部署中,我们经常面临一个关键挑战:如何在保持模型精度的同时,进一步提升推理速度以满足实时控制需求?这就是TensorRT加速技术发挥作用的地方。通过将SmolVLA模型转换为TensorRT引擎,我们有望获得显著的性能提升,特别是在NVIDIA GPU硬件上。 本文将带你深入了解SmolVLA模型的TensorRT加速可行性,并提供详细的ONNX导出实操指南,帮助你在自己的机器人项目中实现更高效的推理性能。 2. TensorRT加速技术解析 2.1 TensorRT的核心优势 TensorRT是NVIDIA推出的高性能深度学习推理优化器和运行时库,它通过多种技术手段提升模型推理效率: * 图层融合:将多个连续的操作层合并为单个内核,减少内