C#是微軟的產物,所以其生成的COM組件目前只能提供給IE使用。
實現網頁和本地系統雙向調用的方法IE使用ActiveX控件,而在Chrome、FireFox等瀏覽器有類似的NPAPI插件技術。
開發ActiveX較爲簡單,開發NPAPI教程較少。
微軟新生代瀏覽器Edge不再支持ActiveX控件,且除了IE其他瀏覽器基本不兼容ActiveX。
採用Windows系統服務啓動WebSocket服務,接收網頁客戶端發送的指令信息完成相關操作,實現多種瀏覽器調用ActiveX插件。
C#ActiveX插件製作https://blog.csdn.net/qq_25189723/article/details/101544749
所有測試都支持.Net3.5
Windows系統服務_C#WebSocket服務_C#調用ActiveX實現多種瀏覽器調用ActiveX插件例子下載
一、Windows服務
新建Windows服務項目
設計視圖中切換到代碼視圖
重寫OnStart和OnStop方法
public partial class EL100Service : ServiceBase
{
public EL100Service()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
WriteMessage("服務啓動");
}
protected override void OnStop()
{
WriteMessage("服務停止");
}
//寫文件驗證啓動
public void WriteMessage(string msg)
{
StreamWriter dout = new StreamWriter(@"D:\" + "EL100WindowsServiceLog.txt", true);
dout.Write("\r\n" + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + msg);
dout.Close();
}
}
設計視圖上右鍵 點 添加安裝程序
系統會自動生成“ProjectInstaller.cs”文件,在該文件的設計視圖界面會有兩個控件,一個是serviceProcessInstaller1,一個是serviceInstaller1
serviceInstaller1屬性中設置:
Description(系統服務的描述)
DisplayName (系統服務中顯示的名稱)
ServiceName(系統事件查看器裏的應用程序事件中來源名稱)
StartType(啓動服務的方式,分爲手動、自動和禁用)
serviceProcessInstaller1屬性設置:
Account 下拉設置成 LocalSystem
生成服務程序之後,使用InstallUtil.exe進行服務的安裝(該文件可能在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\)
安裝使用命令 InstallUtil EL100WindowsService.exe
卸載使用命令 InstallUtil -u EL100WindowsService.exe
啓動服務或手動啓動
NET START EL100ActiveXService
二、WebSocket
啓動服務
如建控制檯應用測試
websocket-sharp.dll
using WebSocketSharp.Server;
static void Main(string[] args)
{
WebSocketServer webSocketServer = new WebSocketServer(23981);
webSocketServer.AddWebSocketService<EL100ActiveXMessageHandler>("/el100activex");
webSocketServer.Start();
Console.WriteLine("服務啓動");
Console.ReadKey();
}
消息處理
using WebSocketSharp;
using WebSocketSharp.Server;
public class EL100ActiveXMessageHandler : WebSocketBehavior
{
protected override void OnOpen()
{
Console.WriteLine("建立連接" + ID);
Broadcast(string.Format("{0}上線了,共有{1}人在線", ID, Sessions.Count));
}
protected override void OnClose(CloseEventArgs e)
{
Console.WriteLine("連接關閉" + ID);
Broadcast(string.Format("{0}下線,共有{1}人在線", ID, Sessions.Count));
}
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
{
Console.WriteLine("錯誤!");
}
protected override void OnMessage(MessageEventArgs e)
{
Console.WriteLine("收到消息:" + e.Data);
Send(ID+":" +e.Data);
}
private void Broadcast(string msg)
{
Sessions.Broadcast(msg);
}
}
HTML測試
<html>
<head >
<title>測試WebSocket</title>
</head>
<body >
<div id="messages"></div>
<div>內容:<input type="text" id="content" value=""/><button onclick="Send()">發送</button></div>
</body>
<script language="javascript">
var websocket;
function initWebSocket() {
if ('WebSocket' in window) {
websocket = new WebSocket("ws://127.0.0.1:23981/el100activex");
//連接成功建立的回調方法
websocket.onopen = function (e) {
console.log("WebSocket連接成功");
console.log(e);
};
//連接關閉的回調方法
websocket.onclose = function () {
console.log("WebSocket連接關閉");
// 斷線重連
initWebSocket();
}
//連接發生錯誤的回調方法
websocket.onerror = function () {
console.log("WebSocket連接發生錯誤");
};
//接收到消息的回調方法
websocket.onmessage = function (e) {
console.log("收到",e.data);
var oldvalue = document.getElementById("messages").innerHTML;
document.getElementById("messages").innerHTML = oldvalue + e.data + "<br/>";
}
}
else {
console.info("當前瀏覽器 Not support websocket");
alert('當前瀏覽器 Not support websocket')
}
}
initWebSocket();
function sendMsg(msg){
console.info("msg="+msg);
websocket.send(msg);
}
function Send(){
if(websocket==null || websocket.readyState!=1){
alert('請先連接成功')
}
else{
var text = document.getElementById("content").value;
sendMsg(text);
}
}
</script>
</html>
三、C#調用ActiveX
可建控制檯應用測試
using System;
using System.Reflection;
private static Type type;
private static object activexobj;
static void Main(string[] args)
{
try {
if (type == null) {
Console.WriteLine("查找插件");
//根據classId獲取ActiveX類
type = Type.GetTypeFromCLSID(new Guid("881BF978-EE89-4D08-9396-D5BE3F253615"));
if (type == null)
{
Console.WriteLine("插件不存在");
return;
}
Console.WriteLine("插件存在");
}
if (activexobj == null) {
Console.WriteLine("初始化插件");
//創建類的實例,第二個參數是object數組,就是你的構造方法裏面的參數,
//null即爲無參構造方法,也可以這麼寫:
//activexobj = Activator.CreateInstance(type);
activexobj = Activator.CreateInstance(type, null);
if (activexobj == null)
{
Console.WriteLine("插件初始化失敗");
return;
}
Console.WriteLine("插件初始化成功");
}
//獲取實例的指定方法,根據方法名,還有其他重載,也可以根據參數找
MethodInfo methodInfo = type.GetMethod("GetStr");
if (methodInfo == null) {
Console.WriteLine("方法錯誤");
return;
}
Console.WriteLine("方法正確");
//調用該方法的參數,按順序
object[] GetComPortStr_para = new object[] { "測試參數" };
//調用方法,返回值是object
String operate_result = (String)methodInfo.Invoke(activexobj, GetComPortStr_para);
operate_result = operate_result.Replace("\"", "\\\"");
Console.WriteLine(operate_result);
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
Console.ReadKey();
}
常見錯誤
System.Runtime.InteropServices.COMException (0x80040154): 檢索 COM 類工廠中 CLSID 爲 {881BF978-EE89-4D08-9396-D5BE3F253615} 的組件時失敗,原因是出現以下錯誤: 80040154。
-->
不存在插件。
或項目右鍵屬性-生成-平臺目標x86。
將websocket服務和調用ActiveX插件集成到Windows服務裏就可以實現多種瀏覽器網頁調用ActiveX插件了。
ActiveX插件實現的功能也可以直接在Windows服務裏實現,只是已經開發完ActiveX只是爲了適應其他瀏覽器就直接調用了。
四、通配傳參調用
以參數及返回類型都爲字符串爲例。
特殊的可以switch。
網頁發送方法和參數(可多個參數)的json字符串
function sendMsg(operatemethod, operateparameter){
console.info("operatemethod="+operatemethod +" operateparameter="+operateparameter);
var send_object = {};
$(send_object).attr("operatemethod", operatemethod);
var operateparameter_object = {};
if(operateparameter!=null && operateparameter!=""){
$(operateparameter_object).attr("arg0", operateparameter);
}
$(send_object).attr("operateparameter", operateparameter_object);
websocket.send(JSON.stringify(send_object));
}
C#使用 Json.Net35和Newtonsoft.Json(之前用的Json.Net35文件小,但實現不了嵌套JSON的解析,換Newtonsoft之前實現的序列化也沒改。)
JObject rece_JObj = JObject.Parse(e.Data);
operatemethod_str = rece_JObj["operatemethod"].ToString();
methodInfo = type.GetMethod(operatemethod_str);
if (methodInfo == null)
{
SendErrorMsg(operatemethod_str, "方法錯誤");
isTesting = false; return;
}
//調用該方法的參數,按順序
//object[] GetComPortStr_para = new object[] { };
List<string> operateparameterList = new List<string>();
var operateparameter_obj = rece_JObj["operateparameter"];
if (operateparameter_obj == null)
{
//SendDebugMsg(operatemethod_str, "無參");
}
else
{
JObject operateparameter = JObject.Parse(operateparameter_obj.ToString());
foreach (JProperty jProperty in operateparameter.Properties())
{
SendDebugMsg(operatemethod_str, "key:" + jProperty.Name + " value:" + jProperty.Value);
operateparameterList.Add(jProperty.Value.ToString());
}
}
//調用方法,返回值是object,我的方法返回void,所以不寫
String operate_result = (String)methodInfo.Invoke(activexobj, operateparameterList.ToArray());
operate_result = operate_result.Replace("\"", "\\\"");
SendSuccMsg(operatemethod_str, operate_result);
private void SendMsg(string code, string operatemethod, string msg)
{
fileOperate.WriteMessage("發送消息:" + msg);
var data = new JsonDto() { code = code, msg = msg, operatemethod = operatemethod };
Send(Json.JsonParser.Serialize(data));
}
private void SendSuccMsg(string operatemethod, string msg)
{
SendMsg("1", operatemethod, msg);
}
class JsonDto
{
public string code { get; set; }
public string msg { get; set; }
public string operatemethod { get; set; }
public object operateparameter { get; set; }
}