posts - 19, comments - 21, trackbacks - 1, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2011年9月18日

3. 使用javascript调用.Net对象

使用javascript调用.Net控件

在网页中,对于任何一个DOM对象,我们都可以使用document.getElementById方法获取该对象然后对它进行操作。而对于嵌入的.Net对象其实是一样的。

我们在前面的例子的基础上,给HelloIE控件增加一个函数

public void SendMessage(string text)

    {

this.label1.Text = text;

    } 

然后在页面中增加一个按钮,为按钮增加一个响应函数,获取HelloIE控件对象,并调用SendMessage函数。

<input id="Button1" type="button" onclick="SendMessageToWinControl();" value="Call SendMessage" />

<script type="text/javascript">

function SendMessageToWinControl()

    {                     

var winCtrl = document.getElementById("HelloIE");              

        winCtrl.SendMessage("Message sent from the HTML page!!");               

    }       

</script>

重新编译,然后打开一个新的浏览器浏览,点击Call SendMessage按钮,结果如下:

wps_clip_image-15365

如果点击Call SendMessage按钮后出现脚本错误“对象不支持此属性或方法”,是因为HelloIE控件类缺少一个最关键的代码:为该类添加[ComVisible(true)]属性。这也充分说明了该控件与IE的交互其实就是COM组件之间的通信,只不过是多经过一个中介:脚本引擎而已。

每次重新编译程序以后,一定要开一个新的浏览器,启动一个新的进程,才能把新的dll加载进来。如果只是使用刷新页面,浏览器依然会使用已经加载的旧的dll。

使用javascript调用非可视化.Net对象

可以嵌入网页的不仅仅是控件,所有的.Net类都可以。我们增加一个MathClass类,增加一个方法返回PI值。

    [ComVisible(true)]

public class MathClass

    {

public double GetPI()

        {

return Math.PI;

        }

    }

然后在页面中嵌入MathClass,增加一个按钮,点击显示PI值

<object id="MathClass" classid="http:bin/WindowsFormsControlLibrary.dll#WindowsFormsControlLibrary.MathClass" height="0" width="0" ></object>

<input id="Button2" type="button" onclick="GetPi();" value="Call MathClass" />

<script type="text/javascript">

function GetPi()

    {                     

var winCtrl = document.getElementById("MathClass");    

var pi=winCtrl.GetPI();

        alert(pi);                      

    }       

</script>

重编译项目,点击Call MathClass按钮效果如下:

wps_clip_image-5107

开发与调试技巧

如果.Net控件内部出现异常的话,比如没有提供默认的无参构造函数,该控件也会显示不出来,而且在浏览器中看不到任何提示,因此在开发过程中为了方便调试,我们可以再创建一个Windows项目,把控件拖到一个WinForm中进行调试。

为了让开发更加便利,我们可以在控件上增加一个标签显示当然程序集的版本号,并且让每次编译的Build号加1,这样我们可以通过版本号来判断加载的是不是最新的dll。如果嫌每次编译都要手动修改Build号麻烦的话,可以使用一个VS插件http://www.codeproject.com/KB/macros/versioningcontrolledbuild.aspx

wps_clip_image-15368

完整源码

/Files/xiaomin/DotNetHostWebSite3.rar

posted @ 2011-09-18 01:55 肖敏 阅读(1240) 评论(0) 编辑

1. 概述

对于许多复杂的应用,使用纯Html+javascript的方式难以满足要求。Html5正致力改善这一局面,在在这之前,大家普遍使用ActiveX技术。ActiveX的核心技术依然是COM,浏览器根据Object标签动态创建COM对象并与之交互。我们可以使用VC++与VB开发ActiveX组件。而在.NET时代,我们可以使用.NET支持的语言,利用.NET的优势,更加高效的开发出富客户应用。

如果我们现在要设计一个新的富客户端项目,SilverLight应该是首选方案。但是许多已有的项目不可能马上迁移或者根本不打算迁移到SliverLight。因此,探讨该技术架构依然有现实意义。

本文试图从最简单的例子开始,一步步涵盖所有跟浏览器内嵌.NET对象技术的各方面细节,包括一些有用的工具,并在每一节最后给出完整源代码。

2. Hello IE

我们从一个最简单的例子开始:让IE浏览器显示一个WinForm的控件,控件中显示一行文字“Hello IE”.

创建控件库

首先,创建一个WebApplication与一个Windows控件库。

wps_clip_image-14372

在Windows控件库中增加一个UserControl,取名为HelloIE,拖入一个标签控件,Text属性设为"Hello IE!".

创建宿主页面

为DotNetHostWebSite项目添加引用,把WindowsFormsControlLibrary引用进来。这样每次编译之后,WindowsFormsControlLibrary.dll便会复制到网站的bin目录中。

然后在DotNetHostWebSite中增加一个htm页面,加入<object>标签如下:

<object id="HelloIE" classid="http:bin/WindowsControlLibrary.dll#WindowsFormsControlLibrary.HelloIE" height="100" width="300"/>

关键属性为classid,语法为:

classid="http:[relativePath/]<winControlAssemblyName>.dll#<ControlNamespace>.<ControlClassName>".

(注:请注意使用相对路径的时候,是不用写双斜杠//的。当然也可以使用绝对路径http://[absolutelyPath],但是实际工作中一般不会使用这种方式,因为开发环境、测试环境跟真实环境往往都会使用不同的url)

最后,我们需要在IIS上创建虚拟目录,然后编译整个项目。

wps_clip_image-14392

程序已经完成。使用IE浏览器浏览http://localhost/DotNetHostWebSite/HelloIE.htm。结果如下:

wps_clip_image-14917

配置IIS

然而有些时候该UserControl显示失败,一般是由于IIS配置与.NET安全配置的问题。由于本例子没有做任何涉及安全的操作,因此暂时不用管.NET安全配置。正常的情况下,首先IE会根据object中提供的url发送一个Get请求,把dll下载下来。

wps_clip_image-16437

在两种情况下,这个请求将失败。

l 当前用户没有对该dll文件读取的权限,返回404错误。应保证用户有读取bin目录的权限。

l IIS配置中执行权限设为“脚本和可执行文件”,将返回500错误。应改为“纯脚本”。

wps_clip_image-4428

另外,我们也可以用一个简单的方法来测试,在浏览器地址栏中输入dll的完整地址,回车,如果出现文件下载对话框,表示dll下载没有问题。

wps_clip_image-8129

至此,我们完成了一个最简单的示例。

参考

http://www.cnblogs.com/zgff008/archive/2008/10/28/1321357.html

 

完整源码

 

/Files/xiaomin/DotNetHostWebSite1.rar 

posted @ 2011-09-18 01:42 肖敏 阅读(194) 评论(2) 编辑

2011年8月26日

        技巧1.使用EventHanlderList代替EventHandler 

 

        以Control的Init事件为例。通常我们会简单的用一个语句来定义事件:

public event EventHandler Init;

        然而我们看实际的代码却是:

[WebSysDescription("Control_OnInit")]
public event EventHandler Init
{
    add
    {
        
this.Events.AddHandler(EventInit, value);
    }
    remove
    {
        
this.Events.RemoveHandler(EventInit, value);
    }
}

         Events属性就是EventHandlerList类型的对象。

               这样做的原因有两个:

         1.第一种写法不管该事件有没有响应函数,都会创建一个相应的代理对象。假如一个类中有20个事件,将创建20个代理对象,而这20个代理对象有可能都用不上。

         2.第一种写法编译器将为每一个事件生成Add与Remove方法,这两个方法是线程安全的,因此它包含了一些线程同步的代码。对于根本不需要使用多线程的程序来说,这对性能也是一种损耗。

 

        技巧2.使用静态object代替string类型作为字典主键

               

        在上面的例子中,我们还发现另一个跟我们平常不一样的用法,就是this.Events.AddHandler(EventInit, value);语句中的EventInit属性。我们先看定义

internal static readonly object EventInit;

        它一个简单的object,在这里做主键来使用。我们一般使用字典的时候都习惯用字符串类型作为主键,因此我们会写成this.Events.AddHandler(“EventInit”, value);

        这样写会有两个缺点,第一字符串占内存多,第二容易出错。对于容易出错这一点,有必要展开讲。

        首先,字符串内容不会被编译器检查,非常容易写错,不仅仅是大小写的问题,即使我们把I写成L,编译器也能通过,而有时肉眼很难分辨。当然我们可以定义成常量,但是又回到内存占用的问题上了。

        第二,字符串是一个特殊的类,它的特性其实很多人不是特别了解。比如字符串的驻留,字符串的比较,字符串的哈希。如果集合类把字符串当做一般对象处理,就会出现问题。        

        如果我们使用静态object字段作为主键,则上述两个问题都不会存在。每个对象在内存中的地址都是唯一的,因此用来做主键是非常合适的。

        (ViewState使用string作为主键应该是考虑到序列化的问题)

 

        技巧3.使用BitVector代替bool类型字段

 

        在程序中我们经常使用bool类型的字段作为标志,比如Page是否支持视图状态,我们可以定义

public virtual bool EnableViewState{ getset; }

        这样,编译器会自动生成一个bool型的字段。然而我们看实际的源代码:

 

public virtual bool EnableViewState
{
    
get
    {
        
return !this.flags[disableViewState];
    }
    
set
    {
        
this.SetEnableViewStateInternal(value);
    }
}
 
private const int disableViewState = 4;

        关键在于flags对象。该对象是一个SimpleBitVector32类型。使用字典的方式存取标志位,一个SimpleBitVector32对象能保存32个标志位。相当于32个bool型字段。它内部其实也只存了一个int型的字段,但是它使用每一个位作为标志。这样相对于使用bool型字段来说,能大大节省内存。

 

 

 

 

posted @ 2011-08-26 11:19 肖敏 阅读(1215) 评论(2) 编辑

2011年7月29日

当使用js脚本打开一个模态窗口时,浏览器会使用客户端缓存。甚至当服务器端页面更新了,客户端也依然使用缓存中的页面。所以,尽量在窗口禁止客户端缓存

<META HTTP-EQUIV="pragma" CONTENT="no-cache"> 
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> 

posted @ 2011-07-29 10:43 肖敏 阅读(122) 评论(0) 编辑

2011年6月17日

      1.意图

      使多个对象处理同一个请求,这些对象形成一条链。每个对象拥有下一个对象的引用,该对象可以进行预处理,交给下一个对象处理,然后把处理结果进行后处理,再返回给前一对象。最终返回给客户端,形成一个“回路”。

      2.典型应用。

      Http Pipeline。客户端发送一个URL请求IIS接受到以后,由一系列的对象就行处理最终返回Html Stream给客户端。

      HttpApplication,Page都实现了IHttpHandler接口。并且,在这个对象链条中,下一个对象是由上一个对象动态创建的。比方页面请求和WebService会使用不同的HttpHandler处理。

      另外,IHttpHandler其实只有一个方法,并且没有返回值。每个HttpHandler都会将处理结果放在HttpContext对象中,通过对HttpContext直接操作来做预处理与后处理。

 

      Http Modules      

      HttpApplication有许多事件。Http Module 是通过对HttpApplication注册事件响应函数来实现对同一个HttpContext的处理的。

     

      AOP的拦截器。当一个方法被调用时,先由一系列的拦截器进行预处理,然后才真正调用该方法,然后再把结果返回给拦截器进行后处理。

      每个拦截器都实现了ICallHandler接口,该接口主要方法为

      public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

      在该方法内调用代理getNext(input,getNext)返回后一个拦截器执行的结果。

      在getNext代理执行前做预处理,执行后做后处理。例如LogCallHandler。

       

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            LogPreCall(input);//预处理
            Stopwatch sw 
= new Stopwatch();
            
if (includeCallTime)
            {
                sw.Start();
            }
            IMethodReturn result 
= getNext().Invoke(input, getNext);
            
if (includeCallTime)
            {
                sw.Stop();
            }
            LogPostCall(input, sw, result);//后处理

            
return result;
        }

 

      与HttpPipline不同的是,这里使用对返回值IMethodReturn进行后处理,再返回IMethodReturn 给上一个处理对象。显然受HttpPipline的启发,我们也可以使用一个Context对象把处理结果都放在里面,这样也许可读性会更强一点。

      3.结构。与职责链一样。

      4.与职责链的区别

      职责链的对象最终只有一个处理请求,而Pipeline是每个对象都会处理请求。职责链就像是局域网里的广播,只有一个网卡接受信息。而Pipeline像是流水线,每个对象负责一个动作,一起协作完成一个产品。

     

     

posted @ 2011-06-17 23:52 肖敏 阅读(1036) 评论(2) 编辑

2011年5月30日

      假如我们一个页面中有一组(2个)Radio Button,在页面初始化时写如下代码:

  

 protected void Page_Load(object sender, EventArgs e){
     
if (!this.IsPostBack)
     {
          
this.RadioButton1.Checked = true;
          
this.RadioButton2.Checked = true;
     }

    在页面呈现时,会自动将最后一个Radio Button设为Checked,其他的为UnChecked。并且再次点提交后,Radio Button的状态会与用户输入保持一致。

      然而,也有例外情况:当Radio Button为Disable的时候,会发生两个Radio Button都为Checked的情况。

  protected void Page_Load(object sender, EventArgs e){
     
if (!this.IsPostBack)
     {
         
this.RadioButton1.Checked = true;
         
this.RadioButton2.Checked = true;
         
this.RadioButton1.Enabled = false;
     }

 } 

 

      这种情况下提交表单,将发现,两个Radio Button都是Checked,因此我们在设Enabled=false的时候一定要先确定他的Checked值。或者在判断的时候再加上Disabled状态。

 

 

posted @ 2011-05-30 09:52 肖敏 阅读(136) 评论(0) 编辑

2011年5月11日

摘要: Mike Swanson的博客http://blogs.msdn.com/b/mswanson/archive/2008/11/03/pdc2008sessions.aspx列出了所有PDC2008的链接。但是美中不足的是,没有给出视频下载链接。 所以我在他的基础上修改了一下。现在可以直接右键-下载所有链接,下载完以后重命名-注释名作为文件名。我已经移除了不能作为文件名的字符(冒号与引号)。这个列表堪称最完美PDC2008下载链接。 另外在制作过程中发现Expresso这个工具还不错。http://www.ultrapico.com/Expresso.htm。 regexbuddy看来也不错.阅读全文

posted @ 2011-05-11 23:21 肖敏 阅读(88) 评论(0) 编辑

2011年4月29日

摘要: 当我们对数据表进行修改时,比如删除某个字段。我们必须保证所有用到该字段的存储过程都必须做相应的修改。因此我们如果能用一条SQL语句来查找出所有包含该字段名称的存储过程,那将是非常方便的: --查找包含某关键字的存储过程名selectOBJECT_NAME(id),idfromsyscommentswhereidin(selectobject_id(name)fromdbo.sysobjectswherextype='P')andtextlike'%FieldName%'groupbyid阅读全文

posted @ 2011-04-29 09:15 肖敏 阅读(218) 评论(0) 编辑

2011年3月14日

摘要: 1.微软Webcast《sql server 2000完结篇》。尽管微软Webcast出了很多关于Sql Server的系列课程,但是最为深入讲解Sql Server的还是这个课程,尽管名字是Sql Server2000,但是由于讲的全是内部机制,所以一点都不过时,绝对是深入学习Sql Server的首选。原理方面从关系引擎,存储引擎,日志与事物机制,调优方面索引、查询、过程调优都做了比较详细的介绍。2.《Microsoft SQL Server企业级平台管理实践》徐海蔚。作者是微软全球技术支持中心,数据库支持部技术主管,加入微软技术支持中心已经10年。我自己认为,这类面向实战的书比Sql .阅读全文

posted @ 2011-03-14 23:11 肖敏 阅读(403) 评论(0) 编辑

摘要: 最近看《Microsoft Sql Server企业级平台管理实践》,书中提到,意向锁是用来提高所冲突监测性能的(Page277),但是后面的解释只是一笔带过,语焉不详,后来在网上找到一篇牛新庄博士的文章《DB2和 Oracle的并发控制(锁)比较》有点豁然开朗,试用一个小例子重新阐释一遍: 假设有一个张表Student,有100万条数据,其中有三行: Name Age 张三 -11 李四 -13 王五 14 程序员A发现数据有错误,有一些学生的年龄有负数,他连接数据库,打算把所有的负数变成正数,并且希望在修改的时候,别人不可以读取数据。此时他对两行数据加了X排他锁。 程序员B发现有几个的姓.阅读全文

posted @ 2011-03-14 12:17 肖敏 阅读(220) 评论(0) 编辑