2023年3月

在我的第一篇blog发表之后,得到大家的支持,我的工作得到了认可,感到很开心。

当然,很多朋友也提出了很多出色的见地。我想在这里说明一下我对这些问题的理解。

我所完成的控件是一个用来获取客户端摄像头图片并保存在特殊文件夹里,然后上传到服务器的ActiveX控件。给大家一个截图。 显示图片的就是那个控件。


之所以当初想到用C#写ActiveX控件,一是自己不会用VB/C++写ActiveX控件,二是由于简单的WinForm控件由于.net安全性的限制,无法在客户端实现复杂的操作。因为ActiveX控件是以本地用户的身份运行,所以可以突破.net安全性的限制。而这才是《用C#编写ActiveX控件》的真正优势所在。

其实用C#写ActiveX控件的原理很简单,就是使用了.net平台和COM的互操作性。在我的《用C#编写ActiveX控件》中,修改项目属性的目的就是将.net控件注册为ActiveX控件。这样,你就可以把这个控件完全当作ActiveX控件来对待了。比如,可以使用JS和VBS来调用,也可以使用C++来调用。唯一的遗憾,就是这样的ActiveX控件需要客户端安装.net framework。

由于最近比较忙,一是四六级考试,二是快要期末考了,所以那篇文章一直没有写完。在四六级考试之后,我一定把文章补充完整。


C#
编写
ActiveX
控件(二)


Homer



在我的上一篇 blog 中,已经实现了一个最基本的 ActiveX 控件。当然,我们编写的任务还没有完成。首先,我们先尝试实现和 JS 的交互能力。




我们在
Demo
中加

ShowMessage
方法:


public

void
ShowMessage(
string
msg)



{

if
(msg
!=

null
)



{

MessageBox.Show(msg);

}



}



我们重新编译。在重新访问页面之前,我们先来修改
html
代码:


<
body
bgcolor
='#223344'>

<object id
="helloworld"


classid

="clsid:9551B223-6188-4387-B293-C7D9D8173E3A"
Width
="184"
Height
="96"


>


</
object
>


<
br
>


<
input
type
='button'
onclick
='helloworld.ShowMessage(“Hello
World!”)' value
='Click'>

</body
>


现在,重新访问
http://localhost/helloworld.htm
,单击
Click
按钮,应该可以实现交互了。



但是结果却很遗憾,我们发现
IE
跳出了对话框,如图所示:


单击确定之后,我们发现JS报错。根据提示,我们判断可以通过修改IE的设置使控件运行。打开IE的 工具——〉Internet选项——〉安全——〉本地Intranet——〉自定义级别——〉对没有标记为安全的ActiveX控件进行初始化和运行,将其值设为启用。我们刷新页面,现在终于可以正确运行了。


当然,我们不能指望我们的客户和我们一样修改这个值。毕竟,一是操作麻烦,二是给电脑带来了很大的安全风险。在互联网上搜索之后,发现必须要实现
IObjectSafety
接口,把
ActiveX
控件标记为安全的
ActiveX
控件。在搜索
MSDN
之后,我找到了
IObjectSafety
接口的定义。这就好办了。首先我们自己用
C#
实现这个接口:



[Guid(
"
CB5BDC81-93C1-11CF-8F20-00805F2CD064
"
),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public

interface
IObjectSafety



{

//
methods



void
GetInterfacceSafyOptions(

System.Int32 riid,

out
System.Int32 pdwSupportedOptions,

out
System.Int32 pdwEnabledOptions);

void
SetInterfaceSafetyOptions(

System.Int32 riid,

System.Int32 dwOptionsSetMask,

System.Int32 dwEnabledOptions);

}


注意,这个 GUID 是不能改的。然后,我们在 Demo 类里面实现这个接口。增加一下代码:



IObjectSafety 成员


重新编译,然后将 IE 里面的设置改回来。现在,我们发现,和 JS 的交互已经没有问题了。

这样,一个最基本的
ActiveX
控件已经写好了。你可以在这个控件的基础上增加任何你需要的功能。到这里,编写控件的任务已经完成了,我们的下一个目标就是发布它。


在前面我们已经完成了ActiveX控件的开发,接下来的就是发布它了。


首先,我们建立一个windows安装项目,并将ActiveX控件的主输出添加到项目输出中。然后,改动ActiveX控件的主输出文件,将其Register属性改为vsdrpCOM.如图:

下一步,我们改动项目属性,将引导程序更改为
Web
引导程序。很遗憾的是,在
Web
引导程序设置 中的安装文件夹
URL
中必须使用绝对路径,不能使用相对路径。这意味着生成安装程序的时候就必须确定路径,不是很方便。在示例中,我使用了
localhost
,在发布中可以改为实际的域名。


现在我们生成安装程序,并把相应得程序拷贝到正确的目录中(本例中为默认网站目录下的
ActiveX
文件夹中)。我们可以直接执行
Setup.Exe
文件,以验证安装文件的正确性。在我的机器上正确执行了,成功了!



现在我们又要重新改动
helloworld.htm
文件了。修改后的结果如下:

<body bgcolor='#223344'>

<object id="helloworld"


classid="clsid:9551B223-6188-4387-B293-C7D9D8173E

3A

" Width="184" Height="96" codebase="ActiveX/Setup.Exe"

>

</object>

<br>

<input type='button' onclick='helloworld.ShowMessage("Hello World!")' value='Click'>

</body>

注意,我们在
object
块中加入了
codebase
属性,这就是制定的下载控件的位置,可以使用相对路径。别忙,我们现在还不能正确请求这个页面,因为我们还没有对我们的控件进行签名。签名可以采用两种方式,一种是在上面生成安装程序的时候签名,另一种是使用
sn.exe
签名。推荐大家使用后者,因为可以提供更多选项。本人很懒,就不多写了,大家可以参考
csdn
上的文章
《发布
ActiveX

。先给给大家提个醒,在申请证书的时候选择
高级证书申请。



至此,《用
C#
编写
ActiveX
控件》完结。大家有什么问题,可以和我留言。

NEW:
源码下载

见过太多的大网站都已使用了静态网页。从性能上考虑,这当然是这类网站不二选择。虽然一直以来都很希望能够实现这个功能,但是毕竟没有很急切的需求,所以一直搁置下来。终于,现在的一个项目决定使用静态网页生成技术,我也狠下心来解决这个问题。

曾经思考过很多种方案,但是一一否决了。一种方案是使用XML方案,从CSDN剽窃过来的思路,用XML文件保存数据,然后定义一个XSL,在客户端解析。这种方案最大的缺点是无法处理复杂的页面布局。设想一个很复杂的页面,你很难定义出合适的XSL,而且在客户端的开销也可能不被接受。另一种是使用JavaScript,将数据作为JS文件存储,但是并不是所有的客户端都支持JavaScript,而且我认为这种结构不好,也不容易管理。最后一种方式是使用模板。很多人在模板中定义特殊的字符串,然后一点点的进行替换,效率低,容易错,很难支持页面版面和内容的变更。

综合思考了这些问题,我还是使用了定义模板的方式,但是和前面所说的模板方式不同。我期望能实现一些自定义的数据控件,使用在模板中。在生成静态页面的过程中,首先解析原始的模板文件,自动将控件识别出来,然后使用自定义的数据结构对这些数据控件进行自动绑定。很多高手一看就明白,这种方式和Asp.Net的方式有几分相似。其实我的灵感就来自它,当然以我的水平,达不到它的境界。采用这种方式,你可以在一定范围内自由改变设计而不需改变程序。即使对版式的改变很大,如果数据没有变动或变动很小,程序则不需变化或只需很小的变化。

毕竟我经验尚浅,必有很多不合适的地方,希望大家能提出静态网页生成的更好思路,也欢迎大家批评指正。

说了半天废话,现在言归正传,我们来考虑怎么实现这个想法。如上所述,我把整个过程分为两个步骤:先解析模板,然后数据绑定。现阶段,我只实现了模板解析。在这里先提出自定义数据控件的设计,我使用了html的格式:<flag name=value>body</flag>。 这样,我就可以使用和解析html语言一致的方式解析自定义控件了。

在实际实现之前,我翻看了编译原理方面的书籍,大略看了一下其中的词法分析。这玩意真的挺复杂,没看懂。不过不管怎么样,还是有收获的,特别是那个有限自动机所谓的状态转换启发了我。先不考虑特殊的html语法,一般的html标签都是形如<flag name=value>body</flag>的格式。我定义了字符扫描的五种状态:空状态,正在查找控件,正在查找控件头部,正在查找控件内容,正在查找控件结尾。(注:在写这篇文章的时候,我觉得“正在查找控件”是多余的,但是暂且这样吧)这样几种状态的转换依赖我定义的五种边界字符:非边界字符,开始边界字符,结束边界字符,封闭边界字符,简短形式的结束边界字符。所有的这些都以枚举的形式定义在源码中。

时间不早了,我要回家了,今天先写到这里。源码结构我觉得还是比较清晰的,注释也比较多,如果有人有兴趣详细研究它的实现方式的话,直接看源代码应该就可以了。如果还是不太明白或者我有时间的话,会接下去把思路完整的写下来。

BTW:现在基本完成了解析部分。结构已经完成了,但是很有很多Bug,调试很麻烦,希望有心人能够多多反馈。

晚安了,拜拜!

源码下载