谷歌插件封裝:
1. VSCode新建文件目錄
2. 創建manifest.json文件
{ "version": "1.0", //插件版本 "manifest_version": 2, //版本號,由google指定爲2 ,必須是2 "name": "插件名稱", //插件名稱 "description": "插件描述", //插件描述 "icons": { //插件圖標 "128": "icons/header_128_128.png", "64": "icons/header_64_64.png", "48": "icons/header_48_48.png", "32": "icons/header_32_32.png", "16": "icons/header_16_16.png" }, "background": { "persistent": true, "scripts": [ "background.js" ] }, "permissions": [ "webRequest", "webRequestBlocking", "nativeMessaging", "http://*/*", "https://*/*" ], "update_url": "https://wwww.baidu.com",//插件更新地址 "browser_action": { "default_icon": "icons/header_16_16.png", //插件圖標 "default_popup": "index.html" //點擊圖標後彈出的html互動文件 }, "web_accessible_resources": [ "icons/header_16_16.png" ], "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" //希望能在 Chrome 插件中使用 Vue 放開內容安全策略,纔可以使用evel,new Function等函數 }
3. 新增background.js
var nativeHostName = "客戶端APPName"; // 需要跟後端客戶端一致 // 根據配置文件連接到本地程序 var port = chrome.runtime.connectNative(nativeHostName); port.onDisconnect.addListener(() => { console.log( "連接到客戶端服務失敗: " + chrome.runtime.lastError.message ); port = null; }); // 點擊url地址鏈接後進行判斷,發現是打開clickonce的程序攜帶{clickonce:url}的參數發送到客戶端,客戶端會提前註冊到註冊表,找到註冊表的json文件,再調用json文件配置的客戶端地址 chrome.webRequest.onBeforeRequest.addListener( function (details) { if (details.url.indexOf(".application") != -1) { var el = document.createElement("a"); el.href = details.url; if (el.pathname.match(/\.application$/)) { chrome.runtime.sendNativeMessage(nativeHostName, { clickonce: details.url, }); return { redirectUrl: "javascript:void(0)" }; } } }, { urls: ["http://*/*", "https://*/*"] }, ["blocking"] ); // 下載啓動exe程序自動執行到註冊表,注意這裏的plugin.html,其實是一個插件下載頁面,進入後自動下載後端exe客戶端 chrome.runtime.onInstalled.addListener(function (details) { if (details.reason == "install" || details.reason == "update") { chrome.tabs.create({ url: chrome.extension.getURL("plugin.html") }); } });
4. 新增默認啓動頁面index.html
<html> <head> <title>Plugin</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--需要引用background後臺執行的js文件--> <script type="text/javascript" src="background.js"></script> </head> <body> <h3>插件初始化成功!</h3> </body> </html>
5. 新增plugin.html,
<html>
<head>
<title>Plugin</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
// 自動下載後端服務的插件
window.onload = function() {
var anchorObj = document.body.children.namedItem('helper-download');
// {"clickonce":"http://域名/winform部署到iis的clickonce的app url地址"}
anchorObj.href = chrome.extension.getURL('/exe/c#封裝的註冊到註冊表地址的一個exe程序.exe');
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
var allowDefault = anchorObj.dispatchEvent(evt);
};
</script>
</head>
<body>
<h3>下載安裝插件!</h3>
</body>
</html>
6. c#封裝執行程序
internal static class Program { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { string text = NativeClickOnce.OpenStandardStreamIn(); if (!string.IsNullOrEmpty(text)) { if (text.StartsWith("{\"clickonce\":\"") && NativeClickOnce.StartClickOnce(text)) { NativeClickOnce.OpenStandardStreamOut("{\"result\":\"OK\""); } return; } string[] commandLineArgs = Environment.GetCommandLineArgs(); if (commandLineArgs.Length <= 1) { Install.Installer(); return; } if (commandLineArgs[1].Equals("/Uninstall")) { Install.Uninstall(); return; } Install.Installer(); } }
public class NativeClickOnce { public static bool StartClickOnce(string URI_Native) { string str = URI_Native.Substring("{\"clickonce\":\"".Length, URI_Native.Length - "{\"clickonce\":\"".Length - "\"}".Length); new Process { StartInfo = new ProcessStartInfo("PresentationHost.exe", "-LaunchApplication " + str) }.Start(); return true; } public static string OpenStandardStreamIn() { Stream stream = Console.OpenStandardInput(); byte[] array = new byte[4]; stream.Read(array, 0, 4); int num = BitConverter.ToInt32(array, 0); string text = ""; for (int i = 0; i < num; i++) { text += (char)stream.ReadByte(); } return text; } public static void OpenStandardStreamOut(string stringData) { int length = stringData.Length; Stream stream = Console.OpenStandardOutput(); stream.WriteByte((byte)(length & 255)); stream.WriteByte((byte)(length >> 8 & 255)); stream.WriteByte((byte)(length >> 16 & 255)); stream.WriteByte((byte)(length >> 24 & 255)); // 成功與否 Console.Write(stringData); } }
public class Install {
private static string GooleExtID = "這個是插件安裝後的ID,跟註冊後的json文件裏面的要一致"; private static string AppName = "要跟js寫一致";//跟js通信 ,必須字母小寫,否則會報錯 private static string InstallDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Menarva\\Plugin"); private static string jsonInstall = Path.Combine(Install.InstallDir, "plugin.json"); public static void Installer() { try { Directory.CreateDirectory(Install.InstallDir); string text = Path.Combine(Install.InstallDir, Assembly.GetExecutingAssembly().ManifestModule.ScopeName); File.Copy(Assembly.GetExecutingAssembly().Location, text, true); string[] value = new string[] { "{{", " \"name\": \"{0}\",", " \"description\": \"{1}\",", " \"path\": \"{2}\",", " \"type\": \"stdio\",", " \"allowed_origins\": [\"chrome-extension://{3}/\"]", "}}" }; string format = string.Join("\n", value); File.WriteAllText(Install.jsonInstall, string.Format(format, new object[] { AppName, "Plugin for Chrome", text.Replace("\\", "\\\\"), Install.GooleExtID })); Registry.SetValue($"HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\{AppName}", null, Install.jsonInstall); string keyName = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + Assembly.GetExecutingAssembly().ManifestModule.ScopeName; Registry.SetValue(keyName, "Comments", "Plugin for Chrome"); Registry.SetValue(keyName, "DisplayName", "Plugin for Chrome"); Registry.SetValue(keyName, "DisplayIcon", text); Registry.SetValue(keyName, "Publisher", "Menarva Ltd"); Registry.SetValue(keyName, "NoModify", 1); Registry.SetValue(keyName, "NoRepair", 1); Registry.SetValue(keyName, "UninstallString", text + " /Uninstall"); MessageBox.Show("Plugin for Chrome successfully!"); } catch (Exception ex) { MessageBox.Show("Plugin Installer error:" + ex.Message); } } public static void Uninstall() { try { Registry.CurrentUser.DeleteSubKeyTree($"Software\\Google\\Chrome\\NativeMessagingHosts\\{AppName}"); Registry.CurrentUser.DeleteSubKeyTree("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + Assembly.GetExecutingAssembly().ManifestModule.ScopeName); File.Delete(Install.jsonInstall); MessageBox.Show("Plugin for Chrome\", \"The plugin was uninstalled successfully!"); ProcessStartInfo startInfo = new ProcessStartInfo { Arguments = string.Format("/C choice /C Y /N /D Y /T 3 & rd /S /Q \"{0}\"", Install.InstallDir), WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, FileName = "cmd.exe" }; Process.Start(startInfo); } catch (Exception ex) { MessageBox.Show("Plugin Uninstall error:"+ex.Message); } } }
7. 打包成一個exe程序
7.1 安裝依賴包ILMerge
7.2 自動生成的就是一個exe,如果依賴了多個dll,需要配置,百度參考ILMerge的其他文檔
7.3 將exe拖動到Vue的插件開發文件目錄裏面
7.4 安裝瀏覽器插件,點擊加載已解壓的擴展程序,選擇當前Vs Code開發的項目目錄地址
7.5 注意安裝後查看擴展程序ID是否跟C#程序的GooleExtID是否一致,必須要一致纔可以,否則會報錯,這裏程序會下載c#寫的exe程序,需要點擊註冊,註冊成功後即可使用
7.6 也可以使用打包的方式進行,選擇項目目錄,進行打包,打包後會生成2個文件,一個*.crx文件,一個*.pem文件,*.crx文件可以直接拖動到瀏覽器進行安裝,注意安裝之前需要開啓開發者模式
7.7 *.crx安裝後會提示如下,以及ID不可控,這裏需要注意。暫時沒有找到解決辦法
7.8 然後就可以通過a標籤點擊跳轉到click once部署的winform、wpf程序進行直接打開exe程序
let herf = `${url}/APPClient/*****.application?攜帶參數傳遞到winfrom/wpf`; window.open(herf, '_parent');
// _blank 、_self 、_parent、_top
<a href="clickOnce部署的APP地址/參數" target="_parent">打開xxx系統</a>
7.9 安裝後可以查看註冊表地址
地址:計算機\HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\你的AppName
點擊右側默認的屬性,會顯示存儲的json位置
7.10 打開json位置如下:
地址:C:\Users\29561\AppData\Local\Menarva
該位置下面有你的exe程序,一個json文件
7.11 json文件內容如下
{ "name": "你的AppName", "description": "Plugin for Chrome", "path": "C:\\Users\\29561\\AppData\\Local\\Menarva\\Plugin\\xxxPlugin.exe", "type": "stdio", "allowed_origins": ["chrome-extension://你的插件ID,必須要跟瀏覽器安裝後看到的擴展程序的ID一致/"] }