针对clickonce发布后的程序,优化处理省略弹出层
谷歌插件封装:
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一致/"] }