起因:
最近由於項目需要在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_ROOT\calldemo\DefaultIcon] @="程序運行地址,1" [HKEY_CLASSES_ROOT\calldemo\shell] [HKEY_CLASSES_ROOT\calldemo\shell\open] [HKEY_CLASSES_ROOT\calldemo\shell\open\command] @="程序地址" \"%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(@"calldemo\DefaultIcon")) { register.CreateSubKey(@"calldemo\DefaultIcon"); register.SubKey = @"calldemo\DefaultIcon"; register.WriteRegeditKey("", $"{Application.ExecutablePath},1"); } if (!register.IsSubKeyExist(@"calldemo\shell")) { register.CreateSubKey(@"calldemo\shell"); register.CreateSubKey(@"calldemo\shell\open"); register.CreateSubKey(@"calldemo\shell\open\command"); register.SubKey = @"calldemo\shell\open\command"; //添加默認鍵 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判斷自定義協議是否存在,有好到方法也希望能得到大家的解答。