【问题记录】— 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判断自定义协议是否存在,有好到方法也希望能得到大家的解答。

原文地址:https://www.cnblogs.com/cwsheng/p/14056848.html