web页面调用本地程序
起因:
最近由于项目需要在web页面中调用本地部署的exe程序;进而对该功能实现做了对应了解;以及存在的问题进行记录。
要实现该功能就不得不说浏览器自定义协议;解决办法:那么它是什么呢?
浏览器自定义协议:
浏览器自定义协议,其实是微软提供 Asynchronous Pluggable Protocols;可以用来注册本地应用程序到 URI Scheme
https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85)
实现自定义协议方式—添加注册表:
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT协议名称] @="程序运行地址" "URL Protocol"="" [HKEY_CLASSES_ROOTcalldemoDefaultIcon] @="程序运行地址,1" [HKEY_CLASSES_ROOTcalldemoshell] [HKEY_CLASSES_ROOTcalldemoshellopen] [HKEY_CLASSES_ROOTcalldemoshellopencommand] @="程序地址" "%1""
自定义协议实现示例:
示例实现:实现一个本地Exe,并注册到注册表中;并运行效果。(程序比较简单,可以查看github)
程序实现写入注册表主要逻辑:
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
RegisterUrlProtocol();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var from = new Form1();
//显示输入参数
from.Args = args;
Application.Run(from);
}
/// <summary>
/// 注册自定义协议
/// </summary>
private static void RegisterUrlProtocol()
{
try
{
//检查是否注册自定义协议:如未注册则注册
Register register = new Register("calldemo", RegDomain.ClassesRoot);
if (!register.IsSubKeyExist("calldemo"))
{
//注册:
register.CreateSubKey();
register.WriteRegeditKey("", $"{Application.ExecutablePath}");
register.WriteRegeditKey("URL Protocol", "");
if (!register.IsSubKeyExist(@"calldemoDefaultIcon"))
{
register.CreateSubKey(@"calldemoDefaultIcon");
register.SubKey = @"calldemoDefaultIcon";
register.WriteRegeditKey("", $"{Application.ExecutablePath},1");
}
if (!register.IsSubKeyExist(@"calldemoshell"))
{
register.CreateSubKey(@"calldemoshell");
register.CreateSubKey(@"calldemoshellopen");
register.CreateSubKey(@"calldemoshellopencommand");
register.SubKey = @"calldemoshellopencommand";
//添加默认键
register.WriteRegeditKey("", $""{Application.ExecutablePath}" "%1"");
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
throw;
}
}
创建检验html:
<a href="calldemo:123qwe">UrlProtocolDemo</a>
运行效果:

github地址:https://github.com/cwsheng/URLProtocolDemo.git
问题记录:
1、关于js中检验浏览器自定义协议是否存在,现在没有教好的解决办法?
开源项目:https://github.com/ismailhabib/custom-protocol-detection(亲测无效,且不维护了)
https://github.com/Veryfirefly/custom-protocol-detection(原理同上,也无效)
问题:https://stackoverflow.com/questions/836777/how-to-detect-browsers-protocol-handlers
2、每次调用启动exe,都会新运行一个程序实例;可以通过程序实现判断该程序是否已经在运行。
#region 确保程序只运行一个实例
private static Process RunningInstance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
//遍历与当前进程名称相同的进程列表
foreach (Process process in processes)
{
//如果实例已经存在则忽略当前进程
if (process.Id != current.Id)
{
//保证要打开的进程同已经存在的进程来自同一文件路径
if (Assembly.GetExecutingAssembly().Location.Replace("/", "\") == current.MainModule.FileName)
{
//返回已经存在的进程
return process;
}
}
}
return null;
}
//3.已经有了就把它激活,并将其窗口放置最前端
private static void HandleRunningInstance(Process instance)
{
ShowWindowAsync(instance.MainWindowHandle, 1); //调用api函数,正常显示窗口
SetForegroundWindow(instance.MainWindowHandle); //将窗口放置最前端
}
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(System.IntPtr hWnd, int cmdShow);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(System.IntPtr hWnd);
#endregion
最后:
当然该方式不一定是唯一实现方式,也可以尝试使用WebAssembly实现本地运行程序逻辑,本次未进行验证
如果js判断自定义协议是否存在,有好到方法也希望能得到大家的解答。
