网上银行“安全登录控件”分析

国内的一些银行的网上银行系统为了用户信息的安全,在登录页面上使用了名为“安全登录控件”的东西,取代了传统的 HTML 的输入控件(Input),下面就对用户量较大的招商银行和工商银行的登录界面及“安全登录控件”做一下对比。

1、招商银行

招商银行的网上银行系统做的比较早,用户也很大,深受大部分用户欢迎,其在安全控制手段上也一直比较严格。

在进入正题之前,先来一点题外话:招商银行的有些安全控制办法很值得我们学习,例如专业版证书,据我猜想,可能是在标准的 X.509 证书上又做了一些安全保护,即对标准的证书又进行加密(加密方式不是公开的),这样在计算机中保存/备份的证书是不能通过 Windows 提供的程序去打开的,只有在专业版软件中输入正确的密码后才能导入,这样即遵循了标准,保证了扩展性,又在安全措施上大大加强了。

招商银行的大众版不象专业版那样版本更新很快(可见招商银行的专业版用户量很大,而大众版并不是其发展的重点),大众版自诞生之日起,界面一直比较简单朴素,但基本功能都具备。通过对大众版登录界面的的分析,得到了一下信息:

1) 在登录界面上有数字验证码来防止暴力攻击

2) 安全登录控件中包含两个控件:帐户和密码输入控件

3) 用户输入登录信息后,提交时 Javascript 从安全登录控件中取出的帐户和密码是经过加密的,即在 SSL 加密的基础上,又做了自定义的加密,完全做到了端到端的保密通讯(这一点可以通过 ieHTTPHeaders 查看出来)

4) 在登录 Form 中还有一个 ClienNo 和 SerialNo 等字段,这些值被设置为安全登录控件的 Option,估计是做为加密所用 Key 的一部分

5) 登录 Form 的提交地址是一个 DLL(ISAPI?),Web Server IIS 5.0 (招商银行的主页似乎运用了 Content Management Server ,从其页面源码中可以看出来)

6) 安全登录控件的 CAB 包(CMBEdit.CAB),大小有 117 KB,包含控件 CMBEdit.DLL ,应该是使用 VC 编写的,版本 1.1,经过 VeriSign 代码签名

7)最关键的是,不管是 MVM基于 .NET 的键盘 Hook 程序,还是另外一个基于 VB 的键盘 Hook 程序,都无法捕捉用户在安全登录控件里输入的键值,这也许是最重要的一点

8) 由于在页面中使用 ActiveX ,可能给一些用户带来登录问题(如安全设置不同或安装 XP SP2 都可能阻止 ActiveX 的安装),招商银行的页面上明确给出了解决登录的链接文档。

总之,招商银行的网上银行登录界面在安全上的确做的不错。

2、中国工商银行

工商银行网上银行近两年发展很快,成了其不可或缺的一个重要渠道,且被国外一些媒体评为中国最佳网上银行。

1) 工商银行的个人网上银行系统登录界面中没有使用验证码来防止暴力攻击,这种成本非常低,但安全性回报很高的做法竟然都不用(扣1分)

2) 登录界面中,只有密码域使用了安全控件,难道帐号信息就不重要(扣1分)

3) 在 ieHTTPHeaders 捕捉到的登录 Form 提交的信息中,帐号和密码是未加密的明文,且没有任何其它的信息来防止安全攻击(扣3分)

4) 有意思的是,在登录界面的 HTTP Response 信息中,不但有 IIS 5.0 的信息,还有 WebSphere Application  Server 4.0 的信息,看来其 Web Server 是 IIS,而 Application Server 又是 WebSphere ,这一对组合 …

5) 登录安全控件的 CAB 包(AxSafeControls.CAB) 大小约为 174 K,经过 VeriSign 代码签名,里面有三个 DLL :InputControl.dll 似乎就是界面控件,msvcp60.dll 应该是Microsoft  C++ Runtime Library,SubmitControl.dll 应该是与提交有关的控件,在 SubmitControl.dll 中发现有 addPair 方法,应该与加密有关,在页面提交的 Javascript 中,也确实发现是SubmitControl 发挥了作用,但为什么就是明文的呢? (扣2分)

6) 在登录页面的源码中,发现了一段被注释的 Javascript 代码,仔细一看,原来是以前登录界面未使用安全登录控件时,使用 Html Input 控件时的处理代码,界面改了之后,处理代码没有删除,只被注释,这些源代码中暴露系统的一些信息,很不专业,也很危险,因为通过分析就可以发现,其界面更改前后的服务器端代码没有任何变化(扣2分)

7) 最关键的是,虽然 VB 的键盘 Hook 程序不能 Hook 密码域中输入的值,但是基于.NET 的键盘 Hook 程序竟然完全可以捕捉到用户输入的任何键值,这难道就是大家前几天讨论的捕捉的作用域问题?这样的话,安全控件几乎就成了摆设(扣10分)

8) 没有在登录页面中给出如何解决登录问题的链接文档,可能使得很多用户由于 ActiveX 不能正确安装而不能正确登录系统

经过对登录界面的一些初步比较,就能发现两者在安全性上的差别,也说明了安全是要靠实实在在的技术手段去保证的。

以上信息基于公开的、可获取的现状提供,不提供其它明示与保证。

Hotmail upgrade to 2G ?!

今天在登录 Hotmail 的时候,发现自己的 25 M 邮箱升级到 2G ,比展波给我的 1G Gmail 大出整整一倍,我也不是 MSN 会员呀,这让人难为情 … ,后来看到,有不少人也和我一样幸运

前几天,曾经有人写了一个 GMail Drive shell extension,可以将 Gamil 邮箱虚拟成一个硬盘分区GMail Drive,文件系统为GMailFS,这样你就有 1G 的 Web Drive 了,使用挺方便,速度也不慢,即然有 2G 的 Hotmail 了,那 Hotmail Drive shell extension 估计也不远了,Hotmail 是可以使用 WebDAV 来访问的,估计可以使用这个协议来完成这个 extension,升级后的Hotmail 的附件限制是 20 M,这是不是也意味着 Hotmail Drive 是的单个文件也可达 20 M?

Gmail 虽然很大,但比较慢,而且经常登录不上去,希望 Hotmail 的升级不会带来类似问题。

Microsoft 证书服务

很多高级安全技术归根结底都要使用到 PKI,而认证机构(CA) 和数字证书又是 PKI 中的两个关键部分,所以要想应用这些高级安全技术,就需要建立一套基于 PKI 的 CA ,以便进行数字证书的发放、验证、吊销等功能。

最近较为详细的试用了 Windows Server 自带的证书服务,发现 Microsoft 可能对 Windows 中的证书服务没有足够的重视,以至从 NT 4 开始,证书服务提供的功能基本没有增强:简单的证书申请页面、简陋的服务器管理界面(MMC),证书申请的Web界面还是基于 ASP ,要在 IIS 6上设置允许 ASP 执行 ……

一般来说,建立 CA 时,要采用层次架构,我们经常在通过 SSL 访问 Internet 上的一些服务器时,查看其数字证书,都可以发现 CA 的层次结构:首先是根证书服务器(Root CA),它只面向其它子 CA 发放证书,而不向用户发放用于应用程序的各种证书。象 Verisign ,一般都在根 CA 下分好几个子 CA ,每个子 CA 面向不同的用户或应用类型,发放指定功能类型的证书。

Microsoft 证书服务在安装时就需要指定 CA Server 的类型(企业根 CA、企业从属 CA、独立根 CA、独立从属 CA 等),一旦指定后,就不能更改,这倒也合情合理,但只能安装证书服务的一个实例,这样就无法在一台机器上建立 CA 的层次结构了(虽然 Microsoft 曾声称它们的 CA 可以实现 40 级的结构,可这是以 40 台服务器为代价的……),希望 Microsoft 以后能象 SQL Server 一样,在一台 CA 上实现多个实例,这样建立根 CA 后,可根据需要再建立几个子 CA ,如果以后在一台机器性能不能满足要求的话,就可以平滑地移植到其它服务器上。

另外,证书服务器的申请界面(ASP)比较简陋,且在用户申请证书后,管理员不能及时得到通知,此外现有的证书页面中不能包含一些描述信息,如用户说明此证书的用途、用户的联系方式、期限要求等,这些信息才是证书服务器管理员最需要了解的信息,而现有的 MMC 管理界面中提供的有用信息很少。

以上几个缺陷倒也简单,借着还有点 ASP 的知识,自力更生,给每个页面包含(Include)了页首(Header)和页尾(Footer)文件,并设置了CSS ,让界面看上去更舒服,而且也与企业内部网上的其它 Web 应用在 UI 上保持一致。同时,修改几个提交页面,使得用户在向证书服务提交申请的同时,向指定的管理员邮箱发送通知邮件,这样管理员就能及时到证书服务管理界面中进行相关后续操作(如有人对此代码感兴趣,可以在 Comment 中索取)。

据有人说,OpenCA 具有在一台机器上实现多级 CA 的功能,等过后试试,比较烦人的是,还需要安装一台 Linux 虚拟机。

动态控件的状态问题 (续2)

上次的第二个问题是,为什么动态生成的DropDownList控件,在PostBack后,在Page_Load里其选择的项没有被设置。

拿TestDyn1.aspx为例,如果你在第一个(静态)下拉框里选择2,在第二个(动态)下拉框里选择3,然后按Click Me按钮,你的输出是这样的

[Page_Load]静态:1
[Page_Load]动态:0
[Button_Click]静态:1
[Button_Click]动态:2

不管你选什么,第二项总是
[Page_Load]动态:0

即,动态下拉框的选项在Page_Load没有被正确设置,但在Button的Click事件里被正确设置了。

大家知道,表单控件(TextBox, CheckBox, DropDownList, ListBox,….) 的输入值或被选状态与ViewState无关,而是在Load Postback Data阶段被设置的,因为它们都实现了IPostBackDataHandler接口。

上次说到动态控件被加入父控件的Controls集合时,会通过阶段“追赶(catch-up)”过程来赶上父控件当前的阶段,如果你仔细看一下前一个贴里leighsword和microhelper贴的Control的AddedControl方法,你将看到

control.InitRecursive(control1);

control.LoadViewStateRecursive(obj1);

control.LoadRecursive();

并没有涉及Load Postback Data。那么这个阶段是什么时候被执行的呢?如果你参考Reflector(也可以参考上一个贴的2个回贴)里System.Web.UI.Page的ProcessRequestMain()方法,在去掉了那些Trace语句后是这样的:

base.InitRecursive(null);

if (this.IsPostBack)
{
      this.LoadPageViewState();

    //注意,这里是._requestValueCollection
      this.ProcessPostData(this._requestValueCollection, true); //第二个参数表明是否是在Load前调用的
}
  
base.LoadRecursive();

if (this.IsPostBack)
{

//注意,这里是._leftoverPostData,即,尚未被处理的PostData
 this.ProcessPostData(this._leftoverPostData, false);
 
 this.RaiseChangedEvents();
 
 this.RaisePostBackEvent(this._requestValueCollection);
 
}

base.PreRenderRecursiveInternal();

this.SavePageViewState();

base.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));

ProcessPostData会根据Request.Form里每对名字/值,看是否有实现了IPostBackDataHandler接口的对应名字的控件,有的话,就会调用该控件的LoadPostData方法,譬如DropDownList的LoadPostData是这样的

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{
      string[] textArray1 = postCollection.GetValues(postDataKey);
      if (textArray1 != null)
      {
            int num1 = this.Items.FindByValueInternal(textArray1[0]);
            if (this.SelectedIndex != num1)
            {
                  this.SelectedIndex = num1;
                  return true;
            }
      }
      return false;
}
 
从上面可见,ProcessPostData在Load前被执行了一次,在Load后又会被执行一次。看上去有点怪,但这正是系统给你的方便,允许你在Load里动态生成控件,并让那些实现了IPostBackDataHandler接口的控件获取用户输入的值或选择的状态。

在我们当前的情形下,我们的动态控件是在Load里生成的,错过了第一次ProcessPostData,所以在Page_Load里其选项还没有被正确设置,但第二次ProcessPostData让其获取了用户输入的值或选择的状态,所以在Button的Click事件里被正确设置了。

这也意味着,如果我们的表单控件是在Load之后生成的,譬如你的控件是在PreRender事件里生成的,

void Page_PreRender(Object sender, EventArgs e)
{
   DropDownList ddlDynamic2 = new DropDownList();
   ddlDynamic2.ID = “ddlDynamic2”;

   form1.Controls.Add(ddlDynamic2);

   if (!IsPostBack)
   {
      for (int i=1; i <=3; i++)
          ddlDynamic2.Items.Add(new ListItem(i.ToString(), i.ToString()));
   }
   else
     Response.Write(“[Page_Load]动态2:” + ddlDynamic2.SelectedIndex + “<BR>”);
}

那么尽管它可以恢复ViewState,但因为它错过了2次ProcessPostData机会,它不可能获取用户输入的值或选择的状态。同时这些控件也不会触发Changed Events 与 Postback Events。 当然,你尽可以使用Request.Form来获取用户输入的值或选择的状态,但这跟我们的讨论无关。

所以,如果你需要产生动态控件,而且需要获取用户设置的输入值或触发Changed Events 与 Postback Events事件的话,最好在Load阶段或之前生成。

关于ASP.NET里Page事件及其次序的细节,请参考MVP Paul Wilson的文章
Page Events: Order and PostBack

也请参考

The ASP.NET Page Object Model

Page Life-cycle in ASP.NET

Visual Studio Team System课程资料下载

由于昨天神州网讯的网络出现故障,VSTS课程准备在下周一上午十时进行重讲。为了避免届时网络出现问题,特提供以下资料(PPT+Video)供大家下载。

由于微软只在今年TechED上提供过两门Team System的课程,很多人对该系统并不太了解。在这儿做一个简单的简介:简单来说,Team System扩展了Visual Stduio的开发功能,提供软件生命周期的全程管理,包括需求分析、架构设计、开发、测试等等。

同时,配合Team System,微软解决方案框架(Microsoft Solution Framework,MSF)也已经升级到4.0。在Team System里面,内建了极限编程(XP)、MSF-Agile、MSF-Common等标准软件过程,同时,大家也可以自己定义自己的开发流程,比如团队角色,各角色任务等。如果大家感觉以下的开发流程不适合自己的团队,可以进行自定义,使其更加符合公司需要。

在Team System当中,对于测试方法的功能,得到了极大的增强,压力测试、测试覆盖率、单元测试、测试案例管理都在Team System Test Version实现。

其开发上面的改进,可以参考介绍Whidbey(VS.NET 2005)。

另外,集成了Reporting Service,Team System可以提供了报表展现,以各种图表来分析软件过程中的各种输出,如Bug数量等。

Windows SharePoint Service也被集成到Team System当中,这样,在Team System当中,可以方便的创建团队门户站点,为大家提供文档共享、问题讨论的地方。

在客户端工具中,除了Visual Studio继续充当开发工具外,还将Project,Excel引入,成为项目管理工具。

Team System目前尚在开发当中,在Visual Studio 2005 Beta 1 Refresh当中是可以下载的(仅面对MSDN宇宙版订户及特定测试人员),由于其使用的Yukon及VS.NET的.NET Framework Build号不同,所以服务器端必须使用两台机器(有人已经将其安装在一台机器上)安装,而为了体验各种角色的功能,还应该有三个客户端,分别是测试人员版、开发人员版以及架构师版。并且由于功能特多,很难在一个课程中讲解清楚,只能做Overview。我以后会通过博客堂为大家提供更多Team System的信息。

下载PPT及Video: http://blog.joycode.com/teamsystem.zip

 

动态控件的状态问题 (续)

要理解第一个问题,即,对于一个动态生成的DropDownList对象,为什么先添加ListItem,后调用父控件的Controls.Add,其状态并没有保存,其关键在于理解TrackViewState的调用以及动态控件加入父控件的Controls后的阶段“追赶”过程。

如果你在文档里查询Control的TrackViewState方法描述,其中说到,只有调用该方法后,view-state的变化才会存到服务器控件的StateBag对象里去,这样才会在下一次的PostBack后的LoadViewState中恢复到原来状态。

大家都知道每个控件一般都会经历如下几个阶段 (抄自《Developing Microsoft ASP.NET Server Controls and Components》 一书第九章)

1。Instantiate
2。Initialize
3。Begin Tracking View State
4。Load View State (postback only)
5。Load Postback Data (postback only)
6。Load
7。Raise Changed Events (postback only, optional)
8。Raise Postback Events (postback only, optional)
9。PreRender
10。SaveViewState
11。Render
12。Unload
13。Dispose

在页面里declared的服务器控件,譬如,例子中的ddlStatic,其TrackViewState方法是在Init阶段后面调用的,其后的变化将保存到StateBag里去,但其前的变化不会保存。如果你用Paul Wilson的ViewState Parser查看该例的ViewState,你是看不到其状态的。但假如在此之后,你改变其状态,那么这些状态也许就会保存到ViewState去(取决于该对象是否override了SaveViewState)。

那动态控件呢?其一开始是新建对象,处于原始状态,当它被加到父控件的Controls里时,父控件会根据其当前的control阶段来调用该子控件的一些方法,让子控件赶上父控件的control阶段 (这些方法可以从上个贴里leighsword和microhelper贴的Control的AddedControl方法里看到,在此就不重复了,而且也不用看那些方法)。为什么要这样呢?这应该跟整个页面的生命周期有关吧。

打个比方,不是很恰当,但凑合着吧,这好象是复制一个人后,让他快速经历婴儿,童年,少年,青年,。。。直至赶上被复制人目前的阶段为止。

但大概来讲,当我们在Page_Load里调用form1.Controls.Add()时,父控件form1处于Load阶段(上面的第六行),它就会调用下拉框的一些方法让它经过Init->Load状态,其中的一个结果是在Init后面调用了TrackViewState,DropDownList的父类ListControl, override了TrackViewState,在其中调用了Items(ListItemCollection类)对象的TrackViewState。其结果是,如果你在form1.Controls.Add()之后改变动态DropDownList控件的Items的话,那些ListItem就会被保存下来,因为ListItemCollection对象 override 了 SaveViewState() 。而在form1.Controls.Add()之前添加的ListItem则不会被保存下来。

其实解决TestDyn2.aspx中的问题有个现成的答案,即去除 if (!IsPostBack):

DropDownList ddlDynamic = new DropDownList();
ddlDynamic.ID = “ddlDynamic”;
for (int i=1; i <=3; i++)
  ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));  

form1.Controls.Add(ddlDynamic);

这样,跟ddlStatic一样,每次都生成ListItem对象

第二个问题,即为什么动态生成的DropDownList控件在PostBack后在Page_Load里其选择的项没有被设置,再供大家研究。

Team System Web Cast

MSDN Portal(http://www.microsoft.com/china/msdn) 提供了所有面向.NET开发人员的资料, 今年下半年,我们还率先提供了网络讲座形式为各位提供在线技术讲座,以便让全国的开发人员在网上实时收听收看各种最新技术的演示报告。如果你没有时间收听收看,还可以在事后进行下载收听: http://www.microsoft.com/china/msdn/events/webcasts/Webcast/webcast_oct.aspx

今天下午,我将通过WebCast与大家一起探讨Visual Studio Team System,该课程在广州与北京的TechED上分别由我及我的同事张炜在现场讲过。欢迎各位捧场。

另外,我们也希望有兴趣参与WebCast的朋友参与进来,可以把您能够讲的题目及个人的简历通过http://blog.joycode.com/joy/contact.aspx 发送给我,让MSDN China成为展现大家才华的舞台!

 

 

 

从Coder到Developer


你是否对软件开发已经了如指掌?如果是,那么就请别往下读了。

Coder(编码人员)与Developer(开发人员)的区别是什么?Coder是指那些熟练掌握了某门计算机语言的人。而Developer是指那些人,能拿起需求或概念来,组成一支团队,应用现代开发过程,做出合乎需求的设计,然后制造出来高质量的产品来。

Mike Gunderloy在他的新书《Coder To Developer — Tools and Strategies for Delivering Your Software》里告诉我们,想成为一个合格的软件开发人员,应该掌握什么东西。

在该书的15章里,Mike Gunderloy以一个.NET程序开发为例,深入浅出地谈及了软件开发中的方方面面,从计划,组织,应用源码控制,防卫性编程,使用单元测试,熟练掌握和武装IDE,深入理解/重用代码,使用工具生成编码,缺陷追踪,记录程序行为,团队合作,产生文档,精通运行代码的生成过程,保护知识产权,到交付与产品的安装/部署,在每个方面都提出了非常可借鉴的看法和经验。

譬如,在第三章《Using Source Code Control Effectively(有效地使用源码控制)》里,他谈及源码控制里的三个层次,现有市面上的种种源码控制工具,如何据于需求(价格,开发风格,代码repository,网络友好性,与IDE的集成,所需高级指令,跨平台支持等等)做出选择。

在第五章《Pumping Up the IDE(如何武装IDE)》里,他谈及怎么用户化IDE,设置有用选项,使用VS.Net PowerToys,编写Macros,选择Add-Ins,怎么使用工具浏览和分析你的源码,更换编辑器等等。

在第九章《Logging Application Activities(记录程序行为)》里,他从.Net里现有的类(System.Diagnostics.Debug 和Trace)出发,谈到微软的Enterprise Instrumentation Framework以及The Logging Application Block,最后论及开放源代码的log4net

在第十三章《Mastering the Build Process(精通Build过程)》里,他谈及自动Build 过程,以及该过程的简洁性和可靠性,分析比较了当前市面上高低档Build 工具,如何使用开发源代码的NAnt等等。

虽说自己开发软件也有不少年了,但惭愧的是,涉及的方面很有限。通过阅读这本书,了解到了自己与一个全面的软件开发人员间的距离,认识到在哪些方面还有待进一步提高。

比较欣赏书尾的几句话:

If there’s one thing that distinguishes good developers from the rest of the pack, it’s  an attitude toward learning. Good developers just don’t stop learning. There’s always some new part of the software universe to explore, some new languages to learn, or some new tool to test-drive. As you hone your own developing skills, I urge you to make use of the resources of the Internet to continue learning and exploring.

动态控件的状态问题

在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。

有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的:

下面两页差别很小,就是一句语句的前后次序有所不同,但PostBack后显示效果有所不同,请解释为什么显示效果不同,并且解释正确显示的那页(你知道是哪页,对么?)中Response.Write的输出结果

TestDyn1.aspx:

<html>
<body>
 <form id=”form1″ runat=”server”>
  <asp:Button id=”btn” runat=”server” Text=”Click Me” OnClick=”Button_Click” /> <br/>
  静态: <asp:DropDownList id=”ddlStatic” runat=”server”>
  <asp:ListItem Text=”1″ Value=”1″ />
  <asp:ListItem Text=”2″ Value=”2″ />
  <asp:ListItem Text=”3″ Value=”3″ />
       </asp:DropDownList> <br/>
  动态:
 </form>
</body>
</html>

void Page_Load(Object sender, EventArgs e)
{
   DropDownList ddlDynamic = new DropDownList();
   ddlDynamic.ID = “ddlDynamic”;

   form1.Controls.Add(ddlDynamic);

   if (!IsPostBack)
   {
 for (int i=1; i   ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
   }

  
 
   if (IsPostBack)
   {
    Response.Write(“[Page_Load]静态:” + ddlStatic.SelectedIndex + “
“);
 Response.Write(“[Page_Load]动态:” + ddlDynamic.SelectedIndex + “
“);
   }
}

void Button_Click(Object sender, EventArgs e)
{
 DropDownList ddlDynamic = (DropDownList)form1.FindControl(“ddlDynamic”);
      Response.Write(“[Button_Click]静态:” + ddlStatic.SelectedIndex + “
“);
 Response.Write(“[Button_Click]动态:” + ddlDynamic.SelectedIndex + “
“);
}

TestDyn2.aspx:

<html>
<body>
 <form id=”form1″ runat=”server”>
  <asp:Button id=”btn” runat=”server” Text=”Click Me” OnClick=”Button_Click” /> <br/>
  静态: <asp:DropDownList id=”ddlStatic” runat=”server”>
  <asp:ListItem Text=”1″ Value=”1″ />
  <asp:ListItem Text=”2″ Value=”2″ />
  <asp:ListItem Text=”3″ Value=”3″ />
       </asp:DropDownList> <br/>
  动态:
 </form>
</body>
</html>

void Page_Load(Object sender, EventArgs e)
{
   DropDownList ddlDynamic = new DropDownList();
   ddlDynamic.ID = “ddlDynamic”;

   if (!IsPostBack)
   {
 for (int i=1; i   ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
   }

   form1.Controls.Add(ddlDynamic);

 
   if (IsPostBack)
   {
    Response.Write(“[Page_Load]静态:” + ddlStatic.SelectedIndex + “
“);
 Response.Write(“[Page_Load]动态:” + ddlDynamic.SelectedIndex + “
“);
   }
}

void Button_Click(Object sender, EventArgs e)
{
 DropDownList ddlDynamic = (DropDownList)form1.FindControl(“ddlDynamic”);
      Response.Write(“[Button_Click]静态:” + ddlStatic.SelectedIndex + “
“);
 Response.Write(“[Button_Click]动态:” + ddlDynamic.SelectedIndex + “
“);
}

 

了解ViewState与控件生命周期的人,对这题自然不在话下。鉴于Lostinet对此有深入研究,本题禁止他参与,

注:本题是从我在CSDN上回答过的一个问题改编而来的

在winform里怎么调用WebBrowser控件里的脚本

这是在CSDN论坛上的一个问题,感觉也许对其他人也会有点用处,所以贴出来

JScript是建立在COM之上的,设置变量/调用函数是通过IDispatch来实现的。在.NET里,调用IDispatch里的方法是通过反射来实现的,即,通过System.Type.InvokeMember 。该方法调用 IDispatch::GetIDsOfNames 以及IDispatch::Invoke来调用COM Automation 对象里的方法和属性。参考

Binding for Office automation servers with Visual C# .NET

假如有下列HTML,

var var1 = ‘yes’;
function testx(obj)
{
  alert(obj);
  alert(var1);
}

<input type=button value=”click me” onclick=”testx(‘hello’);”>

在C#里,假设axWebBrowser1是你的WebBrowser控件对象,你已经装载了上述HTML,你可以这么做,

mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)axWebBrowser1.Document;
mshtml.IHTMLWindow2 win = (mshtml.IHTMLWindow2)doc.parentWindow;

//读变量值
object o = win.GetType().InvokeMember (“var1”, BindingFlags.GetProperty, null, win, new Object [] {});

//写变量值
win.GetType().InvokeMember (“var1”, BindingFlags.SetProperty, null, win, new Object [] {“新的值”});

//调用方法
win.GetType().InvokeMember(“testx”,BindingFlags.InvokeMethod,null,win,new object[]{1});

比较麻烦,对么?幸运的是,我们不用这么麻烦,因为IHTMLWindow2里有个现成方法,execScript,我们可以这么做:

win.execScript(“var1 = ‘abc’;”,”javascript”);
win.execScript(“testx(12)”,”javascript”);