【問題記錄】— web頁面呼叫本地程式

chaney1992發表於2020-11-29

起因:

 最近由於專案需要在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判斷自定義協議是否存在,有好到方法也希望能得到大家的解答。

相關文章