最近项目中,因为需要在WEB页面上操作串口,包括串口查询、打开、发送指令、接收数据、关闭串口等功能。如下所示:

C# ActiveX开发及安装部署

  考虑使用ActiveX来实现。因为以前没有这方面的经验,开发过程中也是遇到各种问题。废话不多说,下面进入正题:

  1:打开VS2008,新建项目,以下是具体代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Windows.Forms;
  4 using System.IO.Ports;
  5 using System.IO;
  6 using System.Reflection;
  7 using System.Runtime.InteropServices;
  8 using mshtml;
  9 using System.Text;
 10 using Microsoft.Win32;
 11 using System.Threading;
 12 
 13 namespace WebSerial
 14 {
 15     [ProgId("WebSerial")]//控件名称
 16     [ClassInterface(ClassInterfaceType.AutoDual), ComSourceInterfaces(typeof(ControlEvents))]
 17     [Guid("6C6A0DE4-193A-48f5-BA91-3C180558785B")]//控件的GUID,用于COM注册和HTML中Object对象classid引用
 18     [ComVisible(true)]
 19     public partial class SerialPortControl : UserControl,IObjectSafety
 20     {
 21         ExSerialPort serialPort;
 22         List<byte> buffer = new List<byte>(4096);
 23 
 24         /// <summary>
 25         /// 是否准备关闭串口
 26         /// </summary>
 27         private static bool m_IsTryToClosePort = false;
 28         /// <summary>
 29         /// 是否正在接收数据
 30         /// </summary>
 31         private static bool m_IsReceiving = false;
 32         public SerialPortControl()
 33         {
 34 
 35         }
 36 
 37         /// <summary>
 38         /// 获取本地串口列表,以逗号隔开
 39         /// </summary>
 40         /// <returns></returns>
 41         public string getComPorts()
 42         {
 43             string ports = "";
 44             foreach (string s in ExSerialPort.GetPortNames())
 45             {
 46                 ports += "," + s;
 47             }
 48             return ports.Length > 0 ? ports.Substring(1) : "";
 49         }
 50 
 51         /// <summary>
 52         /// 以指定串口号和波特率连接串口
 53         /// </summary>
 54         /// <param name="com">端口号</param>
 55         /// <param name="baudRate">波特率</param>
 56         /// <returns></returns>
 57         [ComVisible(true)]
 58         public string connect(string com, int baudRate)
 59         {
 60             close();
 61             serialPort = null;
 62             serialPort = new ExSerialPort(com);
 63             serialPort.BaudRate = baudRate;
 64             serialPort.Parity = Parity.None;
 65             serialPort.DataBits = 8;
 66             serialPort.Encoding = Encoding.ASCII;
 67             serialPort.ReceivedBytesThreshold = 5;
 68             serialPort.ReadBufferSize = 102400;
 69 
 70             try
 71             {
 72                 serialPort.Open();
 73                 if (serialPort.IsOpen)
 74                 {
 75                     m_IsTryToClosePort = false;
 76                     this.clear();
 77                     serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
 78                     return "true";
 79                 }
 80             }
 81             catch { }
 82 
 83             return "false";
 84         }
 85 
 86         /// <summary>
 87         /// 清理串口数据并关闭串口
 88         /// </summary>
 89         [ComVisible(true)]
 90         public void close()
 91         {
 92             m_IsTryToClosePort = true;
 93             while (m_IsReceiving)
 94             {
 95                 Application.DoEvents();
 96             }
 97 
 98             if (serialPort != null)
 99             {
100                 serialPort.Dispose();
101             }
102         }
103 
104         /// <summary>
105         /// 清理串口数据
106         /// </summary>
107         [ComVisible(true)]
108         public void clear()
109         {
110             if (serialPort != null && serialPort.IsOpen)
111             {
112                 serialPort.Clear();
113             }
114         }
115 
116         /// <summary>
117         /// 发送字符串
118         /// </summary>
119         /// <param name="s"></param>
120         [ComVisible(true)]
121         public void writeString(string hexString)
122         {
123             if (serialPort != null && serialPort.IsOpen)
124             {
125                 byte[] bytes = strToToHexByte(hexString);
126                 serialPort.Write(bytes, 0, bytes.Length);
127             }
128         }
129 
130         /// <summary>
131         /// 字符串转16进制字节数组
132         /// </summary>
133         /// <param name="hexString"></param>
134         /// <returns></returns>
135         private static byte[] strToToHexByte(string hexString)
136         {
137             hexString = hexString.Replace(" ", "");
138             if ((hexString.Length % 2) != 0)
139                 hexString += " ";
140             byte[] returnBytes = new byte[hexString.Length / 2];
141             for (int i = 0; i < returnBytes.Length; i++)
142                 returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
143             return returnBytes;
144         }
145 
146         ///  <summary>  
147         /// 字节数组转字符串16进制  
148         ///  </summary>  
149         ///  <param name="InBytes"> 二进制字节 </param>  
150         ///  <returns>类似"01 02 0F" </returns>  
151         public static string ByteToString(byte[] InBytes)
152         {
153             string StringOut = "";
154             foreach (byte InByte in InBytes)
155             {
156                 //StringOut += String.Format("{0:X2}", InByte) + " ";  
157                 StringOut += " " + InByte.ToString("X").PadLeft(2, '0');
158             }
159 
160             return StringOut.Trim();
161         }
162 
163         /// <summary>
164         /// 接收数据
165         /// </summary>
166         /// <param name="sender"></param>
167         /// <param name="e"></param>
168         void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
169         {
170             if (m_IsTryToClosePort)
171             {
172                 return;
173             }
174 
175             m_IsReceiving = true;
176 
177             try
178             {
179                 int n = serialPort.BytesToRead;
180                 if (n > 0)
181                 {
182                     byte[] buf = new byte[n];
183                     serialPort.Read(buf, 0, n);
184                     string dataString = ByteToString(buf);
185                     Receive(dataString);
186                 }
187 
188                 ////1.缓存数据
189                 //buffer.AddRange(buf);
190                 ////2.完整性判断
191                 //while (buffer.Count >= 5) //至少包含帧头(2字节)、长度(1字节)、校验位(1字节);根据设计不同而不同
192                 //{
193                 //    //2.1 查找数据头
194                 //    if (buffer[0] == 0xff && buffer[1] == 0x55) //传输数据有帧头,用于判断
195                 //    {
196                 //        int len = buffer[2];
197                 //        if (buffer.Count < len + 4) //数据区尚未接收完整
198                 //        {
199                 //            break;
200                 //        }
201                 //        //得到完整的数据,复制到ReceiveBytes中进行校验
202                 //        byte[] ReceiveBytes = new byte[len + 4];
203                 //        buffer.CopyTo(0, ReceiveBytes, 0, len + 4);
204 
205                 //        byte checkByte = ReceiveBytes[len + 3];//获取校验字节
206                 //        byte realCheckByte = 0x00;
207                 //        realCheckByte -= buffer[2];
208                 //        for (int packIndex = 0; packIndex < len; packIndex++)//将后面的数据加起来
209                 //        {
210                 //            realCheckByte -= ReceiveBytes[packIndex + 3];
211                 //        }
212 
213                 //        if (checkByte == realCheckByte)//验证,看数据是否合格
214                 //        {
215                 //            string dataString = ByteToString(ReceiveBytes);                            
216                 //            Receive(dataString);
217                 //        }
218                 //        buffer.RemoveRange(0, len + 4);
219                 //    }
220                 //    else //帧头不正确时,清除
221                 //    {
222                 //        buffer.RemoveAt(0);
223                 //    }
224 
225                 //}
226             }
227             finally
228             {
229                 m_IsReceiving = false;
230             }
231         }
232 
233         public event ControlEventHandler OnReceive;
234         [ComVisible(true)]
235         private void Receive(string dataString)
236         {
237             if (OnReceive != null)
238             {
239                 OnReceive(dataString); //Calling event that will be catched in JS
240             }
241         }
242 
243         ///    <summary>
244         ///    Register the class as a    control    and    set    it's CodeBase entry
245         ///    </summary>
246         ///    <param name="key">The registry key of the control</param>
247         [ComRegisterFunction()]
248         public static void RegisterClass(string key)
249         {
250             // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
251             StringBuilder sb = new StringBuilder(key);
252 
253             sb.Replace(@"HKEY_CLASSES_ROOT\", "");
254             // Open the CLSID\{guid} key for write access
255             RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);
256 
257             // And create    the    'Control' key -    this allows    it to show up in
258             // the ActiveX control container
259             RegistryKey ctrl = k.CreateSubKey("Control");
260             ctrl.Close();
261 
262             // Next create the CodeBase entry    - needed if    not    string named and GACced.
263             RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
264             inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
265             inprocServer32.Close();
266             // Finally close the main    key
267             k.Close();
268             MessageBox.Show("Registered");
269         }
270 
271         ///    <summary>
272         ///    Called to unregister the control
273         ///    </summary>
274         ///    <param name="key">Tke registry key</param>
275         [ComUnregisterFunction()]
276         public static void UnregisterClass(string key)
277         {
278             StringBuilder sb = new StringBuilder(key);
279             sb.Replace(@"HKEY_CLASSES_ROOT\", "");
280 
281             // Open    HKCR\CLSID\{guid} for write    access
282             RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);
283 
284             // Delete the 'Control'    key, but don't throw an    exception if it    does not exist
285             k.DeleteSubKey("Control", false);
286 
287             // Next    open up    InprocServer32
288             //RegistryKey    inprocServer32 = 
289             k.OpenSubKey("InprocServer32", true);
290 
291             // And delete the CodeBase key,    again not throwing if missing
292             k.DeleteSubKey("CodeBase", false);
293 
294             // Finally close the main key
295             k.Close();
296             MessageBox.Show("UnRegistered");
297         }
298 
299         #region IObjectSafety 成员
300         public void GetInterfacceSafyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
301         {
302             pdwSupportedOptions = 1;
303             pdwEnabledOptions = 2;
304         }
305         public void SetInterfaceSafetyOptions(int riid, int dwOptionsSetMask, int dwEnabledOptions)
306         {
307             throw new NotImplementedException();
308         }
309         #endregion
310 
311         #region IObjectSafety 成员
312 
313         private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
314         private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
315         private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
316         private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
317         private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
318 
319         private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
320         private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
321         private const int S_OK = 0;
322         private const int E_FAIL = unchecked((int)0x80004005);
323         private const int E_NOINTERFACE = unchecked((int)0x80004002);
324 
325         private bool _fSafeForScripting = true;
326         private bool _fSafeForInitializing = true;
327 
328 
329         public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
330         {
331             int Rslt = E_FAIL;
332 
333             string strGUID = riid.ToString("B");
334             pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
335             switch (strGUID)
336             {
337                 case _IID_IDispatch:
338                 case _IID_IDispatchEx:
339                     Rslt = S_OK;
340                     pdwEnabledOptions = 0;
341                     if (_fSafeForScripting == true)
342                         pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
343                     break;
344                 case _IID_IPersistStorage:
345                 case _IID_IPersistStream:
346                 case _IID_IPersistPropertyBag:
347                     Rslt = S_OK;
348                     pdwEnabledOptions = 0;
349                     if (_fSafeForInitializing == true)
350                         pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
351                     break;
352                 default:
353                     Rslt = E_NOINTERFACE;
354                     break;
355             }
356 
357             return Rslt;
358         }
359 
360         public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
361         {
362             int Rslt = E_FAIL;
363 
364             string strGUID = riid.ToString("B");
365             switch (strGUID)
366             {
367                 case _IID_IDispatch:
368                 case _IID_IDispatchEx:
369                     if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
370                          (_fSafeForScripting == true))
371                         Rslt = S_OK;
372                     break;
373                 case _IID_IPersistStorage:
374                 case _IID_IPersistStream:
375                 case _IID_IPersistPropertyBag:
376                     if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
377                          (_fSafeForInitializing == true))
378                         Rslt = S_OK;
379                     break;
380                 default:
381                     Rslt = E_NOINTERFACE;
382                     break;
383             }
384 
385             return Rslt;
386         }
387 
388         #endregion
389     }
390 
391     /// <summary>
392     /// Event handler for events that will be visible from JavaScript
393     /// </summary>
394     public delegate void ControlEventHandler(string dataString);
395 
396     /// <summary>
397     /// This interface shows events to javascript
398     /// </summary>
399     [Guid("68BD4E0D-D7BC-4cf6-BEB7-CAB950161E79")]
400     [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
401     public interface ControlEvents
402     {
403         //Add a DispIdAttribute to any members in the source interface to specify the COM DispId.
404         [DispId(0x60020001)]
405         void OnReceive(string dataString); //This method will be visible from JS
406     }
407 
408     public class ExSerialPort : SerialPort
409     {
410         public ExSerialPort(string name)
411             : base(name)
412         {
413         }
414 
415         public void Clear()
416         {
417             try
418             {
419                 if (this.IsOpen)
420                 {
421                     this.DiscardInBuffer();
422                     this.DiscardOutBuffer();
423                 }
424             }
425             catch { }
426         }
427 
428         protected override void Dispose(bool disposing)
429         {
430             Clear();
431 
432             var stream = (Stream)typeof(SerialPort).GetField("internalSerialStream", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
433 
434             if (stream != null)
435             {
436                 stream.Dispose();
437             }
438 
439             base.Dispose(disposing);
440         }
441     }
442 }
View Code

相关文章:

  • 2021-12-10
  • 2022-02-11
  • 2021-09-27
  • 2021-12-01
  • 2021-06-10
  • 2021-10-01
猜你喜欢
  • 2021-07-20
  • 2022-12-23
  • 2021-06-24
  • 2022-12-23
  • 2022-12-23
  • 2021-10-20
  • 2022-12-23
相关资源
相似解决方案