摘要:從網頁中啟動本地應用程序的規范中,需要將本地應用程序的信息通過寫注冊表的方式注冊到系統中,然后在網頁中使用就可以只在啟動本地程序了。如果使用不帶參數的,則無法啟動目標程序。
目錄
4、將自定義的URL Scheme信息寫入注冊表的C++源碼實現
? ? ? ?最近接連收到一些關于從web頁面上啟動我們C++軟件的需求,希望我們能提供一些技術上的支持與協助。于是我們大概地研究了相關的實現方法,下面把研究的過程與結果在此做一個分享,希望能給朋友們提供一個借鑒或參考。
? ? ? ?最近很多第三方開發廠商為了快速集成我們的系統及軟件(作為子系統融入到他們的大型web業務系統中),不想基于SDK做費時費力的二次開發,提出直接從web網頁啟動我們軟件的需求。簡單地歸納了一下,類似的需求可以分以下幾類:
1)僅僅是從web網頁上將C++軟件啟動起來,即將軟件調起來就行了,沒有后續操作;
2)從web網頁上將C++軟件啟動起來,并且啟動時傳遞服務器地址和賬戶信息,讓軟件自動發起登陸;
3)從web網頁上將C++軟件啟動起來,并且啟動時傳遞一些信息,讓軟件執行指定的一些操作。
? ? ? ?其實上述需求可以簡單的歸結為,將C++軟件啟動起來,并給C++軟件傳遞一些命令行參數,C++軟件解析出參數,執行指定的操作。
? ? ? ?如果是C++程序要啟動C++軟件,會比較簡單,只要讀一下安裝程序時寫入的程序安裝路徑,就能直接通過軟件的全路徑,直接就可以將C++軟件啟動起來。
? ? ? ?現在越來越多的系統都轉向了B/S架構,用戶可以隨處隨地訪問到系統里面去,只要有網絡有電腦就行了,不用再安裝各種客戶端軟件了。但就像我們上面提到的一些客戶一樣,因為某些業務的需要,需要從web網頁上啟動基于C/S架構的客戶端軟件。
? ? ? ?web網頁一般都是在瀏覽器中的打開的,出于安全的原因,web瀏覽器既不能直接讀寫注冊表,也不能直接啟動二進制文件,所以在web網頁中想啟動本地的應用程序似乎遇到了問題。其實這并不是問題,我們使用URI Scheme技術就能輕松地實現這樣的需求。
? ? ? ?URI,全稱是Uniform Resource Identifier,統一資源標志符。在web開發領域,其表示的是web上每一種可用的資源,如HTML文檔、圖片、視頻等。URI Scheme,我們稱之為URI方案,是一種技術規范,其中的URI是個更寬泛的概念,它可以是一個本地的文件,也可以是一個網絡上的視頻。
? ? ? ?從web網頁中啟動本地應用程序的URI Scheme規范中,需要將本地應用程序的信息通過寫注冊表的方式注冊到系統中,然后在網頁中使用“SchemeName://”就可以只在啟動本地程序了。具體的做法是,在注冊表的HKEY_CLASSES_ROOT下創建一個自定義的SchemeName注冊表節點,然后再在該節點下創建多個節點,并在給相關節點設置注冊表鍵值。
? ? ? ?以QQ內嵌的QQGame為例,添加注冊表信息的步驟如下:
1)在HKEY_CLASSES_ROOT下創建QQGameProtocol節點
? ? ? QQGameProtocol就是對應的Scheme方案名稱,也是web頁面上啟動對應程序的URL的前綴名稱,即QQGameProtocol://。然后給該節點添加一個URL Protocol名稱的鍵值,將其Value設置為本地應用程序的完整路徑。對于當前的QQGameProtocol,就是C:/Users/Public/Documents/Tencent/QQGameMicro/QQGwp.exe,如上圖所示。
2)在QQGameProtocol根節點下創建DefaultIcon節點
? ? ? ?給DefaultIcon節點設置默認的字符串鍵值(REG_SZ類型),其Value的格式為“應用程
序全路徑,圖標索引”的形式,該鍵值是用來指定該URI方案使用的圖標。本例中的Value
為:C:/Users/Public/Documents/Tencent/QQGameMicro/QQGwp.exe,1,如上圖所示。
3)在QQGameProtocol下創建shell節點
? ? ? ?先在QQGameProtocol下創建shell節點,然后在shell節點下創建open節點,然后在open節點創建command節點。shell節點和open節點不需要設置鍵值,command節點需要設置鍵值,其鍵值用來指定啟動目標應用程序時是否給目標程序傳遞命令行參數。
? ? ? ?一般只需要設置傳遞一個參數即可,比如當前Scheme下的"C:/Users/Public/Documents/Tencent/QQGameMicro/QQGwp.exe" "%1"。如果要傳遞多個參數,可以自定義一個組合格式,命令行只用一個參數即可。比如我們要給目標程序傳遞服務器地址、用戶名和密碼,可以采用這樣的組合格式:
#serveraddr=192.168.72.135#username=admin1#password=123456
即將要傳遞的多個參數按指定的格式組合起來生成一個命令行字符串參數即可。
? ? ? ?當在web頁面上點擊“SchemeName://”鏈接時,就會到系統注冊表的HKEY_CLASSES_ROOT節點下查找SchemeName節點項,找到后取出目標應用程序的全路徑,并查找傳遞的命令行參數個數,這樣就能把本地的目標應用程序啟動起來了。
? ? ? ?如果要給目標程序傳遞參數,則使用“SchemeName://參數”的形式。經測試發現,如果在command節點中設置了%1傳遞參數的標識,則web網頁中設置的URL必須要帶參數,即“SchemeName://參數”。如果使用不帶參數的URL:“SchemeName://”,則無法啟動目標程序。
? ? ? ?那如何既要支持不傳參數啟動,也要支持傳參數啟動呢?難道要在注冊表中創建兩個不同的SchemeName節點?其實不用這么麻煩,使用一個帶參數的SchemeName節點就夠了,對于直接啟動目標程序不帶啟動參數的,也可以攜帶一個標識參數,在程序中約定不傳參數的標識符,比如noparam,當程序中解析出noparam,則表示是不帶參數啟動的,直接啟動程序即可,不用做后續的操作。
? ? ? ?下面給出將自定義的URL Scheme信息寫入注冊表的C++源碼實現:
BOOL WriteURISchemaReg(){ // exe程序的完整路徑 CString strExePath = m_strInstallPath + _T("xyzlink.exe"); // URI Scheme名稱 CString strProtocolName = _T("XyzlinkProtocol"); HKEY hRootKey = NULL; DWORD dwKeyValue = 0; DWORD dwDisposition = 0; UCHAR szBuf[MAX_PATH] = { 0 }; // 1、在HKEY_CLASSES_ROOT下創建URI Schema相關注冊表的根節點RootNode long lRet = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, ProtocalNodeName, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hRootKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { return FALSE; } // 給根節點RootNode設置值1 lRet = ::RegSetValueEx(hRootKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strProtocolName, strProtocolName.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 給根節點RootNode設置值2 CString strKey = _T("URL Protocol"); lRet = RegSetValueEx(hRootKey, strKey.GetBuffer(0), 0, REG_SZ, (LPBYTE)(LPCTSTR)strExePath, strExePath.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 2、在根節點RootNode下創建DefaultIcon節點 strKey = _T("DefaultIcon"); HKEY hDefaultIconKey = NULL; lRet = RegCreateKeyEx(hRootKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hDefaultIconKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hRootKey); return FALSE; } // 給RootNode/DefaultIcon節點設置值 CString strExePathPlus = strExePath + _T(",1"); lRet = RegSetValueEx(hDefaultIconKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strExePathPlus, strExePathPlus.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 3、在RootNode/DefaultIcon節點下創建子節點shell strKey = _T("shell"); HKEY hShellKey = NULL; lRet = RegCreateKeyEx(hDefaultIconKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hShellKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 4、在RootNode/DefaultIcon/shell節點下創建子節點open strKey = _T("open"); HKEY hOpenKey = NULL; lRet = RegCreateKeyEx(hShellKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hOpenKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 5、在RootNode/DefaultIcon/shell/open節點下創建子節點command strKey = _T("command"); HKEY hCommandKey = NULL; lRet = RegCreateKeyEx(hOpenKey, strKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hCommandKey, &dwDisposition); if (lRet != ERROR_SUCCESS) { RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } // 給command節點設置值(命令行參數) CString strCmdParam; strCmdParam.Format(_T("/"%s/" /"%%1/""), strExePath); lRet = RegSetValueEx(hCommandKey, NULL, 0, REG_SZ, (LPBYTE)(LPCTSTR)strCmdParam, strCmdParam.GetLength() * sizeof(TCHAR)); if (lRet != ERROR_SUCCESS) { RegCloseKey(hCommandKey); RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return FALSE; } RegCloseKey(hCommandKey); RegCloseKey(hOpenKey); RegCloseKey(hDefaultIconKey); RegCloseKey(hRootKey); return TRUE;}
? ? ? ?搞清楚了使用URI Scheme規范實現從web頁面中啟動本地應用程序的方法,下面我們再回到最開始提出的3個需求,看看如何去實現。
? ? ? ?第一種需求不需要傳遞參數,后面兩種需求則需要傳遞參數,我們使用一個帶參數傳遞的Scheme節點即可。我們可以定義一個啟動type類型標識launchtype,對于直接啟動的,type為noparam。對于啟動后發起自動登錄的,type為autologin;對于啟動后需要執行具體操作的,可以根據具體的業務,定義具體的type類型,這樣更靈活。
? ? ? ?對于目標應用程序,則可以根據不同的type類型,解析對應的參數數據,并對參數的合法性進行校驗。
? ? ? ?下面把web網頁的測試代碼給出來,保存成.html文件,用瀏覽器打開即可:
Start exe demo 打開目標程序
? ? ? ?上面大概說了一下問題的解決辦法和思路,其實還有很多細節需要去考慮。比如下面的幾種場景:
1)程序可能沒有安裝
? ? ? ?如果目標應用程序沒有安裝,肯定是啟動不起來的,是不是要檢測啟動失敗的原因,然后自動跳轉到安裝程序的下載頁面。
2)僅將目標程序啟動起來,但目標程序已經運行
? ? ? ?一般情況下,很多程序都是單實例運行的,即只允許運行一個實例。假定目標程序是單實例運行的,點擊web頁面中的啟動程序的鏈接時,已經有個進程在運行了,目標程序中要彈出程序已經運行的提示,并將已經啟動的程序拉到前端顯示。
3)啟動程序后需要有后續操作,但目標程序已經運行
? ? ? ?啟動程序后需要有后續操作,比如自動發起登錄,但此時目標應用程序已經運行。如果已啟動的進程還沒登錄,是要自動發起登錄?還是擱置不管?如果已啟動的進程已經登錄,則提示已經啟動,并將已啟動的主窗口拉到最前顯示。如果目標程序已經啟動且已經登錄成功,則需要將命令行參數發給已啟動的進程,讓該進程執行要執行的操作,比如加入會議。
PS:微軟官方說明連接:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/125389.html
摘要:五瀏覽器繪制網頁繪制過程主要是結構與樣式的結合,以及行為動態效果的展現。之后會寫系列文章,歡迎圍觀主要參考文章基礎進階詳解與編碼前端面試題從到頁面展現,這之中發生了什么圖解 流程概述: 地址欄輸入URL ——> 域名解析 ——> 服務器處理請求 ——> 瀏覽器處理響應 ——> 瀏覽器繪制網頁 一.地址欄輸入URL 認識URL showImg(https://segmentfault....
閱讀 3735·2023-01-11 11:02
閱讀 4244·2023-01-11 11:02
閱讀 3050·2023-01-11 11:02
閱讀 5180·2023-01-11 11:02
閱讀 4737·2023-01-11 11:02
閱讀 5534·2023-01-11 11:02
閱讀 5313·2023-01-11 11:02
閱讀 3990·2023-01-11 11:02