基于vue3+ts的前端网页,实现网页点击按钮打开本地exe运行文件的完整实例

核心原理

使用 URL Protocol 协议注册 + 前端跳转协议 的方式实现,兼容主流浏览器(需用户提前注册协议)。

完整实现步骤

1. 注册自定义 URL 协议(Windows 系统)

创建 open-app.reg 文件,内容如下:

复制代码
Windows Registry Editor Version 5.00

; 删除旧的(如果存在)
[-HKEY_CLASSES_ROOT\myapp]

[HKEY_CLASSES_ROOT\myapp]
@="URL:MyApp Protocol v2"
"URL Protocol"=""
"EditFlags"=dword:00000002  ; 强制重新关联

[HKEY_CLASSES_ROOT\myapp\DefaultIcon]
@="C:\\Path\\To\\YourApp.exe,0"

[HKEY_CLASSES_ROOT\myapp\shell]
[HKEY_CLASSES_ROOT\myapp\shell\open]
[HKEY_CLASSES_ROOT\myapp\shell\open\command]
@="\"C:\\Path\\To\\YourApp.exe\" \"%1\""

说明:

  • 替换 C:\Path\To\YourApp.exe 为实际 exe 路径(如 C:\ProgramFiles\MyApp\app.exe)
  • 双击运行该文件完成注册

2.Vue3 + TypeScript 前端实现

复制代码
<template>
  <button @click="openLocalApp" :disabled="isOpening">
    {{ isOpening ? '正在启动...' : '启动本地应用' }}
  </button>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  data() {
    return {
      isOpening: false,
      fallbackShown: false
    };
  },
  methods: {
    async openLocalApp() {
      if (this.isOpening) return;
      
      this.isOpening = true;
      
      // 方法1:使用 iframe(最兼容)
      const hiddenFrame = document.createElement('iframe');
      hiddenFrame.style.display = 'none';
      hiddenFrame.src = 'myapp://launch?timestamp=' + Date.now(); // 添加时间戳避免缓存
      document.body.appendChild(hiddenFrame);
      
      // // 方法2:使用 window.open(备用)
      // const fallbackTimer = setTimeout(() => {
      //   if (!this.fallbackShown) {
      //     this.fallbackShown = true;
      //     window.open('myapp://launch', '_blank');
      //   }
      // }, 100);
      
      // 监听各种可能的事件来判断应用是否启动
      const cleanup = () => {
        this.isOpening = false;
        // clearTimeout(fallbackTimer);
        clearTimeout(failTimer);
        hiddenFrame.remove();
        window.removeEventListener('blur', blurHandler);
        document.removeEventListener('visibilitychange', visibilityHandler);
      };
      
      const blurHandler = () => {
        console.log('应用启动成功 - 窗口失去焦点');
        cleanup();
        
        // 可选:显示成功提示
        setTimeout(() => {
          alert('应用启动成功!');
        }, 100);
      };
      
      const visibilityHandler = () => {
        if (document.hidden) {
          console.log('应用启动成功 - 页面被隐藏');
          cleanup();
        }
      };
      
      window.addEventListener('blur', blurHandler, { once: true });
      document.addEventListener('visibilitychange', visibilityHandler, { once: true });
      
      // 超时处理
      const failTimer = setTimeout(() => {
        if (this.isOpening) {
          cleanup();
          
          // 提供详细的错误帮助
          const userChoice = confirm(
            '应用启动可能需要额外权限。\n\n' +
            '可能的原因:\n' +
            '1. 防火墙或安全软件阻止\n' +
            '2. 需要手动允许协议启动\n' +
            '3. 应用路径不正确\n\n' +
            '点击"确定"查看详细解决方案'
          );
          
          if (userChoice) {
            window.open('/help#protocol-guide', '_blank');
          }
        }
      }, 5000); // 5秒超时
    }
  }
});
</script>
相关推荐
kyriewen2 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒2 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
小林攻城狮3 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦3 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer3 小时前
Webpack vs Vite 深度对比分析
前端·webpack
转转技术团队3 小时前
验证码识别实战:前端不写页面,改训模型了?
前端
MomentYY3 小时前
Temperature:AI 的“脑洞旋钮”
前端·llm·ai编程
远航_4 小时前
OpenSpec 完整详细介绍
前端·后端
召钱熏4 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
SkyWalking中文站4 小时前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控