aspx网页访问服务器和客户端串口
aspx网页访问服务器端的串口,控制服务器方面的设备;通过自己制作的ActiveX的用户控件访问客户端串口,控制客户端的设备。
为了简单名称尽量使用默认值,也参考了网上一些内容, 环境 VS2010 ,win7 , IE8以上。
一 创建和测试用户控件的动态库
(一) 创建 类库
文件 -- 新建 -- 项目 -- 类库 -- ClassLibrary1 -- 确定
(二) 创建 用户控件
右击 解决方案资源管理器之下的 ClassLibrary1
添加 -- 新建项 -- 用户控件 -- UserControl1.cs -- 添加
添加 按钮 文本框 列表框 如下:
1 用户控件上放置 3个按钮btnOpen,btnSend,btnClose,
一个组合框comboBox_port,一个文本框textBox1,一个列表框listBox1
2 代码附加到最后
3 菜单 生成--生成ClassLibrary1,直到完全正确
3 菜单 生成--生成解决方案,直到完全正确
(三) 将用户控件添加到工具箱
1 鼠标放到左侧边栏的工具箱上--展开后--右击 公共控件 栏--选择项
2
浏览--C:\Users\x\Desktop\ClassLibrary1\ClassLibrary1\bin\Debug--
点选
ClassLibrary1.dll--打开--确定
3 用户控件放到工具箱 公共控件 栏内
(四) 添加测试的 From1
1 文件--添加--新建项目--Visual
C#--Windows--WindowFormApplication1--确定
2 点选From1 设计页面,从工具箱中拖用户控件到其上面
3 菜单 生成--生成解决方案,直到完全正确
二 C#开发 ActiveX控件
继续在上面的解决方案内操作
(一)设置属性
1 右击解决方案之下的类库ClassLibrary1 -- 属性
2 在【应用程序】选项页,点击 “程序集信息”
3 勾选 使程序集COM可见,确定
4 在【生成】选项页,勾选“为COM互操作注册”
5 菜单 生成 -- 生成解决方案,直到完全正确
(二)创建GUID
菜单 工具 -- 创建GUID -- 新建GUID -- 点选格式 5 -- 复制
粘贴到用户控件类的开头为:
namespace ClassLibrary1
{
[Guid("8E5DCEE1-A3BB-4A37-95C2-D4B99FA773FA")]
public partial class
UserControl1 : UserControl
{
注意这里的GUID,与(一)设置属性 3中的GUID不能完全一样,至少要一个字符不同,否则编译通不过,出现错误:
错误 1 未能将程序集“ClassLibrary1.dll”转换为类型库。
类型库导出程序在处理“ClassLibrary1.UserControl1,
ClassLibrary1”时遇到了错误。
错误: 找不到元素。 ClassLibrary1
(三)IObjectSafety接口
为了让ActiveX控件获得客户端的信任,控件类需要实现一个名为“IObjectSafety”接口,
1 右击解决方案之下的类库ClassLibrary1 -- 添加 -- 接口 -- 名称为 IObjectSafety --
添加
2 注意:IObjectSafety接口的GUID值不能修改,接口代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[ComImport,
Guid("A51D26F2-9057-4008-B910-9C21BE65D1E3")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
[PreserveSig]
int
GetInterfaceSafetyOptions(ref Guid riid,
[MarshalAs(UnmanagedType.U4)] ref int
pdwSupportedOptions,
[MarshalAs(UnmanagedType.U4)] ref int
pdwEnabledOptions);
[PreserveSig()]
int SetInterfaceSafetyOptions(ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int
dwOptionSetMask,
[MarshalAs(UnmanagedType.U4)] int
dwEnabledOptions);
}
}
(四) 改变用户控件类基于接口IObjectSafety, 并实现其接口函数,完整代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text.RegularEx pressions;
namespace ClassLibrary1
{
[Guid("8E5DCEE1-A3BB-4A37-95C2-D4B99FA773FA")]
public partial class
UserControl1 : UserControl, IObjectSafety
{
SerialPort serial_port = new SerialPort();
int recv_len = 0;
int[] crc_data1 = { 0, 0 };
byte[] in_data = { 0, 0, 0, 0, 0, 0, 0, 0
};
public UserControl1()
{
InitializeComponent();
string[]
ports = SerialPort.GetPortNames();
Array.Sort(ports);
comboBox_port.Items.AddRange(ports);
comboBox_port.SelectedIndex = comboBox_port.Items.Count > 0 ? 0
: -1;
}
private void btnOpen_Click(object sender,
EventArgs e)
{
try
{
if (serial_port.IsOpen)
serial_port.Close();
serial_port.PortName =
comboBox_port.Text.ToString();//选择串口号
serial_port.BaudRate =
9600;
serial_port.Parity =
Parity.None;
//奇偶效验
serial_port.StopBits =
StopBits.One; //效验
serial_port.DataBits = 8;
//每个字节的数据位长度
serial_port.DataReceived +=
new SerialDataReceivedEventHandler(serial_port_DataReceived);
serial_port.Open();
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
private void btnSend_Click(object sender,
EventArgs e)
{
List
< byte > buf = new List
< byte >
();//填充到这个临时列表中
string[]
str = textBox1.Text.ToString().Split(' ');//空格分割
//16进制
foreach
(string m in str)
{
buf.Add(byte.Parse(m,
System.Globalization.NumberStyles.HexNumber));
}
//转换列表为数组后发送
serial_port.Write(buf.ToArray(), 0, buf.Count);
//记录发送的字节数
// int
send_len = buf.Count;
}
private void btnClose_Click(object sender,
EventArgs e)
{
if
(serial_port.IsOpen) serial_port.Close();
}
private void serial_port_DataReceived(object
sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(100);
recv_len =
serial_port.Read(in_data, 0, 8);
this.BeginInvoke(new EventHandler(process_data));
}
private void process_data(object sender,
EventArgs e)
{
if
(in_data[0] == 1) //ok
{
string str = "";
for (int i = 0; i
< 8; i++)
{
str += (in_data[i].ToString("X2") + " ");
}
listBox1.Items.Add(str);
int crc1 = Crc16(in_data,
recv_len - 2);
int crc2 = (int)(in_data[6] +
in_data[7] * 256);
if (crc1 ==
crc2)//校验CRC
{
serial_port.Write(in_data, 0,
8);//正确的话,回答发来的
}
else
MessageBox.Show("校验码错误,数据无效!", "");
}
for (int i
= 0; i < 8; i++) in_data[i]
= 0;
}
private int Crc16(byte[] puchMsg, int
usDataLen)
{
System.UInt16 temp = 0xffff, temp1, i;
byte
j;
for (i =
0; i < usDataLen; i++)
{
temp ^= puchMsg[i];// *
(puchMsg++);
for (j = 0; j
< 8; j++)
{
temp1 = temp;
temp >>= 1;
if ((temp1 & 0x0001) == 0x0001)
{ temp ^= 0xa001; }
}
}
return
(temp);
}
//接口函数
#region IObjectSafety
private const string _IID_IDispatch =
"{00020400-0000-0000-C000-000000000046}";
private const string _IID_IDispatchEx =
"{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
private const string _IID_IPersistStorage =
"{0000010A-0000-0000-C000-000000000046}";
private const string _IID_IPersistStream =
"{00000109-0000-0000-C000-000000000046}";
private const string _IID_IPersistPropertyBag =
"{37D84F60-42CB-11CE-8135-00AA004BB851}";
private const int
INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
private const int
INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
private const int S_OK = 0;
private const int E_FAIL =
unchecked((int)0x80004005);
private const int E_NOINTERFACE =
unchecked((int)0x80004002);
private bool _fSafeForScripting = true;
private bool _fSafeForInitializing = true;
public int GetInterfaceSafetyOptions(ref Guid
riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
{
int Rslt =
E_FAIL;
string
strGUID = riid.ToString("B");
pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
switch
(strGUID)
{
case _IID_IDispatch:
case _IID_IDispatchEx:
Rslt = S_OK;
pdwEnabledOptions = 0;
if (_fSafeForScripting == true)
pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
break;
case
_IID_IPersistStorage:
case
_IID_IPersistStream:
case
_IID_IPersistPropertyBag:
Rslt = S_OK;
pdwEnabledOptions = 0;
if (_fSafeForInitializing == true)
pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
break;
default:
Rslt = E_NOINTERFACE;
break;
}
return
Rslt;
}
public int SetInterfaceSafetyOptions(ref Guid
riid, int dwOptionSetMask, int dwEnabledOptions)
{
int Rslt =
E_FAIL;
string
strGUID = riid.ToString("B");
switch
(strGUID)
{
case _IID_IDispatch:
case _IID_IDispatchEx:
if (((dwEnabledOptions & dwOptionSetMask) ==
INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
(_fSafeForScripting ==
true))
Rslt =
S_OK;
break;
case
_IID_IPersistStorage:
case
_IID_IPersistStream:
case
_IID_IPersistPropertyBag:
if (((dwEnabledOptions & dwOptionSetMask) ==
INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
(_fSafeForInitializing ==
true))
Rslt =
S_OK;
break;
default:
Rslt = E_NOINTERFACE;
break;
}
return
Rslt;
}
#endregion
}
}
(五)重新生成
菜单 生成 -- 生成解决方案,直到完全正确,并在From1中运行测试
三 制作安装包
上面的程序在Form1.cs下是可以执行的,不过目的是让其他用户在浏览器使用。
文件 -- 添加 -- 新建项目 -- 其他项目类型 -- 安装和部署 --Visual Studio
Installer
-- 安装项目 -- Setup1 -- 确定
1 选择 主输出
右击 应用程序文件夹 --
添加 -- 项目输出
项目 选择 ClassLibrary1,主输出 -- 确定
自动检测到依赖Microsoft.NET Framework
2 查看依赖包版本
在解决方案之下,找到Setup1,
双击“Microsoft.NET Framework”,打开启动条件页
右下边的属性中,改变依赖程序版本为,Version:.NET Framework 4。
3 设置“主输出”的属性
在解决方案之下,找到Setup1,
点击 主输出来来自ClassLibrary1(活动),在其属性中,
设置Register的值为:vsdrpCOM。
4 设置安装项目注册表版本
注意:这部分也可以不做。
右击 安装项目Setup1 -- 视图 -- 注册表,(也可以点击解决方案资源管理器之下的工具条)
打开如图:
在“HKEY_CLASSES_ROOT”下添加键
1
“CLSID”和“{GUID值}”(你的类的GUID),8E5DCEE1-A3BB-4A37-95C2-D4B99FA773FA
2 “InstalledVersion”,右键新建“字符串值”,写上版本,注意,版本用逗号“,”不是“.”号。
如下图所示:
InstalledVersion这个版本号要和以下三处设置一致:
(1)是ClassLibrary1工程属性的“程序集信息”一样,如下:
(2)和安装项目Setup1的属性一样
(3)和IE页面的 < object>标签一样,
3 选中“Setup1”右键 -- 生成。
就可以生成Setup1.msi和Setup.EXE文件了,双击两者之一就可以在你电脑上安装你写的ActiveX控件。
四 添加 web 应用程序
(一)HTML格式的文件调用
< object id="helloworld"
classid="clsid:8E5DCEE1-A3BB-4A37-95C2-D4B99FA773FA"
width="284" height="280"
codebase="ClassLibrary1.dll">
< /object>
(二)aspx方式的 web 应用程序
这才是我们的最终使用方式
1 文件 -- 添加 -- 新建项目 -- Visual C# -- Web -- ASP.NET Web 应用程序 --
WebApplication1 -- 确定
注意这里:
选择是 文件 -- 添加 -- 新建[项目],可以管理更大的网站。
而不是 文件 -- 添加 -- 新建[网站],这样管理是小的网站。
2 前端 Default.aspx 页面文件 添加控件
拖放一些按钮等到页面,其中的
runat="server",就代表执行在服务器端。
< div>
1 服务器端 的串口收发
< asp:Button ID="btnOpen"
runat="server" onclick="btnOpen_Click" Text="open" />
< asp:DropDownList
ID="comboBox_port" runat="server">
<
/asp:DropDownList>
< asp:Button ID="btnSend"
runat="server" onclick="btnSend_Click" Text="发送" />
< asp:TextBox ID="textBox1"
runat="server"> < /asp:TextBox>
< asp:Button ID="btnClose"
runat="server" onclick="btnClose_Click"
Text="close" />
< asp:ListBox ID="listBox1"
runat="server" Height="85px"
style="margin-top: 0px" Width="375px"> <
/asp:ListBox>
2 客户端 的串口收发
< object id="helloworld"
classid="clsid:8E5DCEE1-A3BB-4A37-95C2-D4B99FA773FA"
width="284" height="280"
codebase="ClassLibrary1.dll">
< /object>
< /div>
3 后台代码,与用户控件几乎一样
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO.Ports;
namespace WebApplication1
{
public partial class
_Default : System.Web.UI.Page
{
static SerialPort serial_port = new
SerialPort();
int recv_len = 0;
int[] crc_data1 = { 0, 0 };
byte[] in_data = { 0, 0, 0, 0, 0, 0, 0, 0
};
protected void Page_Load(object sender,
EventArgs e)
{
if
(!IsPostBack)
{
string[] ports =
SerialPort.GetPortNames();
comboBox_port.DataBind();
comboBox_port.SelectedIndex =
comboBox_port.Items.Count > 0 ? 0 : -1;
}
}
protected void btnOpen_Click(object sender,
EventArgs e)
{
try
{
if (serial_port.IsOpen)
serial_port.Close();
serial_port.PortName =
comboBox_port.Text.ToString();//选择串口号
serial_port.BaudRate =
9600;
serial_port.Parity =
Parity.None;
//奇偶效验
serial_port.StopBits =
StopBits.One; //效验
serial_port.DataBits = 8;
//每个字节的数据位长度
serial_port.DataReceived +=
new SerialDataReceivedEventHandler(serial_port_DataReceived);
serial_port.Open();
}
catch
(Exception ex)
{
//MessageBox.Show(ex.Message);
return;
}
}
protected void btnSend_Click(object sender,
EventArgs e)
{
List <
byte> buf = new List <
byte>();//填充到这个临时列表中
string str0
= textBox1.Text.ToString();
str0.TrimEnd();
string[]
str = str0.Split(' ');
//16进制添加
foreach
(string m in str)
{
if (m == "")
{
continue;
}
buf.Add(byte.Parse(m,
System.Globalization.NumberStyles.HexNumber));
}
//转换列表为数组后发送
serial_port.Write(buf.ToArray(), 0, buf.Count);
//记录发送的字节数
// n =
buf.Count;
}
protected void btnClose_Click(object sender,
EventArgs e)
{
if
(serial_port.IsOpen) serial_port.Close();
}
private void serial_port_DataReceived(object
sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(100);
recv_len =
serial_port.Read(in_data, 0, 8);
process_data();
}
private void process_data()
{
if
(in_data[0] == 1) //ok
{
string str = "";
for (int i = 0; i
< 8; i++)
{
str += (in_data[i].ToString("X2") + " ");
}
listBox1.Items.Add(str);
int crc1 = Crc16(in_data,
recv_len - 2);
int crc2 = (int)(in_data[6] +
in_data[7] * 256);
if (crc1 ==
crc2)//校验CRC
{
byte[] out_data = { 0x01, 0x03, 0x00, 0x00,
0x00, 0x01, 0x84, 0x0a }; //发送温度传感器数据
serial_port.Write(in_data, 0,
8);//正确的话,回答发来的
}
else
{
}
}
for (int i
= 0; i < 8; i++) in_data[i]
= 0;
}
private int Crc16(byte[] puchMsg, int
usDataLen)
{
System.UInt16 temp = 0xffff, temp1, i;
byte
j;
for (i =
0; i < usDataLen; i++)
{
temp ^=
puchMsg[i];
for (j = 0; j
< 8; j++)
{
temp1 = temp;
temp >>= 1;
if ((temp1 & 0x0001) == 0x0001)
{ temp ^= 0xa001; }
}
}
return
(temp);
}
}
}
4 生成发布网站
有关的生成发布网站的详细信息参看
1 右击 WebApplication1 --
重新生成
2 右击 WebApplication1 --
发布
发布方法,文件系统
目标位置,指定一个位置,比如
D:\vs2010\Web1
点选 发布前删除所有现有文件
按 发布,直到成功为止。
五 IE浏览器的设置
启动浏览器 -- 工具 --Internet选项
(一) 受信任站点设置
1 转到 安全 页 -- 点击 受信任的站点 -- 按
站点
2 将用到的自己网站, http://dell-pc-15 按 添加 加入
3 不要勾选 对该区域中的所有站点要求服务器验证(https:)
(二)设置自定义安全级别
1 转到 安全 页 -- 本地 Intranet -- 按 自定义级别
2 在ActiveX 控件和插件节,所有的都启用
六 执行效果
http://dell-pc-15:8030/default.aspx
1
其中上部的串口是服务器端的,有若干个,操作服务器的串口,控制服务器的设备
2 下部的串口是客户端自己的串口,只有一个,操作本地的串口,控制自己的设备
加载中,请稍候......