這個問題涉及到很多知識,而且要想把這個Runtime做好很繞。
下面我就說一下我的思路:
(以下內容以Windows平臺為基礎,Mac平臺和Linux平臺還得去調查一下,才能確定是否可行)
首先,我們先區分三類使用者:
- Runtime建設者(就是我們)
- Runtime使用者(就是使用Runtime的開發者)
- 終端使用者(就是使用Runtime開發者開發的應用的那些使用者)
接下來我們就以Runtime建設者的視角來審視這項工作
首先我們要為Runtime使用者提供一個專有的打包工具,我們就叫它:打包工具。這個打包工具還內建了幾個可執行程式,我們給他們起個名字,分別叫:
- 最終安裝程式
- 最終執行程式
- 最終解除安裝程式
好,我們一個一個聊他們的職責
打包工具的職責
按Runtime使用者的要求修改最終執行程式的圖示、應用簽名、版本、版權、檔名等資源資訊;
按Runtime使用者的要求修改最終解除安裝程式的圖示、應用簽名、版本、版權、檔名等資源資訊;
把最終執行程式、最終解除安裝程式與Runtime使用者開發好的HTML/CSS/JS等靜態檔案放到一起,壓縮成一個壓縮包,我們叫他:資原始檔
把這個資原始檔以資源的形式封裝到最終安裝程式中;
按Runtime使用者的要求修改這個最終安裝程式的圖示、應用簽名、版本、版權、檔名等資源資訊;
(修改資源的程式碼,後文有介紹)
這幾個工作完成之後,Runtime使用者就可以把這個最終安裝程式分發給終端使用者了。
最終安裝程式的職責
這個最終安裝程式在終端使用者的電腦上執行時,會完成以下工作:
檢查終端使用者的登錄檔,看其是否安裝了我們的Electron Runtime
如果沒有安裝,則下載Electron的發行版,釋放到一個特定目錄下,並在登錄檔記下來。
在這個特定目錄下記錄當前應用的資訊(解除安裝當前應用時要用到);
把自身的資源釋放到終端使用者指定的目錄內,也就是前文說的資原始檔
解壓縮資原始檔得到最終執行程式、最終解除安裝程式和Runtime使用者開發的HTML/CSS/JS等靜態檔案
寫登錄檔記錄最終解除安裝程式的位置,這樣使用者就可以在控制皮膚裡解除安裝我們的程式了。
按終端使用者的要求,建立開始選單圖示、桌面圖示,這些圖示均指向最終執行程式
(讀取資源的程式碼,後文有介紹)
如果終端使用者工作在沒有網路的環境下,那麼我們也可以允許Runtime使用者把Electron Runtime打包到最終安裝程式內,這是打包工具的職責。
如果擔心Electron官方提供的下載地址速度慢,可以考慮使用國內映象地址:npmmirror.com/mirrors/electron/
最終執行程式的職責
檢查使用者登錄檔,找到Electron Runtime的放置路徑
啟動Electron Runtime並把當前應用的入口程式當做引數傳給Electron.exe,應用入口程式就是Runtime使用者開發HTML/CSS/JS等靜態檔案之一,
electron.exe path/to/entry.js
最終解除安裝程式的職責
刪除安裝目錄下的檔案
刪除登錄檔的解除安裝程式資訊
刪除Electron Runtime所在目錄下的應用程式資訊,如果發現沒有別的應用在依賴Electron Runtime了,那麼就把Electron Runtime所在目錄也刪掉。
把一個檔案作為資源寫入一個可執行程式的程式碼如下所示:
HANDLE hFile; DWORD dwFileSize,dwBytesRead; LPBYTE lpBuffer; char szFile[MAX_PATH+1] = {0}; ::GetDlgItemText(hwnd,EditId,szFile,MAX_PATH); hFile = CreateFile(szFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); dwFileSize = GetFileSize(hFile, NULL); lpBuffer = new BYTE[dwFileSize]; ReadFile(hFile, lpBuffer, dwFileSize, &dwBytesRead, NULL); HANDLE hResource = BeginUpdateResource(szFilePath, FALSE); UpdateResource(hResource,RT_RCDATA,MAKEINTRESOURCE(EditId),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPVOID)lpBuffer,dwFileSize); EndUpdateResource(hResource, FALSE); delete [] lpBuffer; CloseHandle(hFile); return 1;
可執行程式讀取自身資源,把資源寫到指定路徑下的程式碼如下:
HMODULE hInstance = ::GetModuleHandle(NULL); TCHAR szFilePath[MAX_PATH + 1]; GetPath(szFilePath,resourceName,hInstance); HRSRC hResID = ::FindResource(hInstance,resourceID,RT_RCDATA); HGLOBAL hRes = ::LoadResource(hInstance,hResID); LPVOID pRes = ::LockResource(hRes); DWORD dwResSize = ::SizeofResource(hInstance,hResID); if(!dwResSize) return 0; HANDLE hResFile = CreateFile(szFilePath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); DWORD dwWritten = 0; WriteFile(hResFile,pRes,dwResSize,&dwWritten,NULL); CloseHandle(hResFile); if(dwResSize == dwWritten) return 1; return 0;
這兩段程式碼是從我的一個專案中摘抄出來的,僅供參考。
遺留的問題
我們並沒有考慮多Electron版本共存的問題;
此方案高度依賴Windows API,跨平臺實現差異肯定會比較大;
應用程式啟動後,工作列的圖示是Electron Runtime的圖示,而非Runtime使用者指定的圖示(這是有解決辦法的);