帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > ASP编程
建立一个C++服务器组件
作者:佚名 发布时间:2005-04-02 来源:不详
    C++是标准化的计算机语言,不属于任何人,而属于一个标准委员会。STL是支持数据结构和算法的C++扩展。ATL是微软拥有和维护的模板库,使得COM编程更容易。综合这些技术形成了创建COM组件的一种有效方法,这些COM组件用于ASP页面。
    下面用所有这些技术创建一个COM对象,你将看到VC++ 6.0的向导如何提供大量代码,因此,可以把注意力集中在解决问题上,而不是担心具体的编程细节。
    17.3.1 问题
    表现数据的最普通方法是表,列代表字段的类型,每一行是一条记录,拥有字段的值。在文本文件中,表通常由用逗号分开的值(comma-separated values,CSV)组成。
    我们将要创建的COM组件以CSV数据作为输入,高效地存储它,并提供访问函数去检索它。这些数据在COM组件中以STL数据结构表示。在以后部分中,我们会看到怎样用STL算法去处理这些数据。另外,在下一章,将介绍怎样在数据库中存储存这些数据。
    为了便于说明,假设数据在一个稀疏表中。第一行的字段是列标题,接下来的是一条条数据记录,记录的每个字段对齐于列标题。逗号隔离字段,换行符(/n)隔离行,空的字段用两个逗号表示,即“,,”。
    表17-1是一个展开的表的例子。导出时,逗号会隔离每一个字段。

17.3.2 设计
    这个组件的设计目的是使数据的存储空间和访问时间最小。由于数据有可能是稀疏的,即许多字段是空的,这就有可能使数据的存储空间最小化。可以通过数值(基于零的索引)访问数据的行,可以通过字段名访问数据的列。例如,要得到表17-1中Keith Moon的Instrument,可以调用GetField (1,"Instrument")。
    完成以上工作的工具是STL的vector和map数据结构,这些数据结构是容器,就是说,它们是包含其他对象的一个集合的对象。为了访问集合中的对象,使用STL遍历器。
17.3.3 实现
    现在你对这个组件的功能已经有了概念,我们将按下面的步骤实现它:
    ? 创建包含组件的DLL。
    ? 创建组件。
    ? 增加属性。
    ? 增加方法。
    1. 创建DLL和一个组件
    选择ATLCOMAppWizard,创建一个新的VC++项目,然后命名为ASPCOMponents。使用默认的服务器类型(DLL),不选中复选框,如图17-1所示。

    当完成后,这个向导会自动产生一个组件的外壳。这时没有组件存在,只有DLL根据COM规范所要求的函数存在,可以在ASPCOMponents.cpp文件中看到这些函数,但不用关心文件的细节,也不用改变它。当向DLL中添加组件时,向导会修改该文件。
    现在将组件放入DLL。在工作区窗口选择ClassView选项卡,右击ASPComponents Classes,选择New ATL Object。然后再选Simple Object,并且在Short Name框中键入Tablestorage,如图17 - 2所示。

    除非你打算更深入地研究ATL,否则在Attributes选项卡保持默认状态,不必改变任何选项。对所有这些选项的意义的详细描述已超出本书的范围。Attributes选项卡如图17-3所示。
    点击OK后,就有了一个用来工作的对象。

    到目前为止,VC++向导已经完成了所有的工作,如果查看TableStorage.h,你会看到向导产生的ATL代码。在大多数情况下,不必改变这些代码。实际上,可以只依靠这些代码,不用了解ATL,继续进行编程。然而,需要对默认的设置做一点改变,使得生成的代码能够编译。
    为了减小组件的大小,当AppWizard创建一个组件时,自动关闭C++的异常处理。因为如果启用这一功能的话,则需要C运行期库。STL使用异常处理,所以需要启用它。在ProjectSettings对话框的C/C++选项卡中,下拉Category框选择C++ Language,确保Settings for框设置为All Configurations,选择Enable exception handing复选框,如图1 7 - 4所示。

     如果用Release模式编译可能会得到一个链接错误。当编译一个发行版本的ATL项目时,如果异常处理没有关闭,会出现这样的问题。使用异常处理时,需要C运行期启动代码;但是在默认方式下,Release模式的ATL项目定义了_ ATL _MIN_CRT符号,它拒绝启动代码。为了解决这个问题,在C/C++预处理器定义中删除_ ATL_MIN_CRT。详细资料请看微软基础知识库中的文章Q165259。
    我们增加的第一段代码用于建立STL数据结构,把下列代码加到TableStorage.h的开头:

    这里,声明了两个映射和一个矢量。这个矢量实际是两个映射之一的矢量。使用C++的typedef命令定义映射和矢量主要是为了使得代码更易读,对数据类型描述得更好。
    COLUMN_INDEX_MAP将一个字符串(列的名称)映射到一个索引中。INDEX_FIELD_MAP表示数据表中的每一行的数据。由于他可能是一个稀疏行,用一个映射实现是高效的,空字段不占用任何空间。INDEX_FIELD_MAP映射COLUMN_INDEX_MAP提供的索引到字段值。最后,ROW_VECTOR包含代表行的每一个映射。
    现在已说明了内部数据结构,可将它们用于使用属性的外部世界。
    2. 增加属性
    我们将增加两个属性:行数属性和列数属性。
    在项目工作区选择ClassView选项卡,右击ITableStorage接口。在菜单中选择Add Property,按图1 7 - 5填写对话框。选择Get Function复选框,而不选择Put Function复选框,使得它为只读属性。

    产生返回这一属性的get_numRows方法。这就是说可有产生属性值的逻辑。在这种情况下,可通过调用行矢量的size()方法设置属性:

    现在有了获得所存储数据的行数和列数的方法,还需让一些数据输入到组件中,这是下一步要做的工作。
    3. 增加方法
    到目前为止,还无法把任何数据输入到内部数据结构中,也无法读取它们。下面增加四个方法完成下列任务:
    ? 在数据结构中插入数据。
    ? 从数据结构中获取一个字段。
    ? 获取列名称。
    ? 对列进行排序。
    (1) 分析数据
    第一个方法将获得一个以逗号隔离的字符串,进行分析,再将数据输入数据结构中。
    在项目工作区中选择ClassView选项卡,右击ITableStorage接口。在菜单中选择AddMethod,按图17 - 6填写对话框。

    然后用下列代码填写ParseCSV方法的主体部分:

    CSV数据作为一个字符串参数传递,并清除两个STL成员变量,删除以前调用这个方法时输入的数据。

     要特别注意第一行, 因为它包含列的名称,每一列的名称作为一个键存储在m_columnIndexMap中,用映射的当前大小作为索引。当一行处理完后,可以利用列名称映射的索引也就是列的数值索引这一事实。如果被分析的字段有数据,就将其存储在INDEX_FIELD_MAP中。


    一旦所有行都处理完后, COM组件中的CSV数据的存储空间已经最小化,并且由于使用映射可以加快访问速度。因为只存储了有实际值的字段,所以存储空间最小。由于映射在内部组织数据,可快速检索,访问速度很快。
    (2) 数据访问
    现在数据已能高效存储,下一步是能够访问它。
    向ITableStorage接口添加一个称为GetField的新方法,按图17 - 7对对话框进行填写。

    给定行数和列名称,这个方法将返回字段值(如果它存在)。改变这个方法的主体,如下所示:


    用于获得与映射的键相对应的值的映射方法是find。对于COLUMN_INDEX_MAP映射,从find中返回pair<wstring, unsigned short>类型的遍历器。如果没有找到键,遍历器具有m_columnIndexMap.end()的值。如果找到键,返回的遍历器的second成员包含对应值:在这种情况下,它是列名称的索引。这个索引作为INDEX_FIELD_MAP映射的键,给定行即可得到字段值。如果找到这个值,通过[out, retval]参数返回,也就是fieldValue。
    下面创建一个得到列名称的方法。在ITableStorage中添加一个称为GetColumnName的新方法,按图17 - 8所示填写对话框。

    给定一列的索引,这个方法将返回列的名称,用下列代码改变这个方法的主体:


    在这个方法中,必须通过映射进行线性查找,因为程序实际是使用键的值而不是键进行检索,遍历m_columnIndexMap映射直到找到索引或者搜索到映射的末端(即索引未找到)。注意遍历器的second成员用于比较,这是因为查找的是键的值而不是键。如果找到列索引,列名称(实际上是键)通过[out, retval]参数返回,也就是columnName。
    (3) 数据排序
    下面添加一个对数据有实际影响的方法。使用STL sort算法对行进行排序。

    然后给I Ta b l e S t o r a g e增加一个新的方法S o r t,按图1 7 - 9对对话框进行填写:



    因为CTableStorage数据结构是一种STL数据结构的组合(一种映射矢量),需要提供一种定制的比较函数进行排序。例如,CTableStorage::Sort允许行根据任何列排序,这种排序可以仅使用一个比较函数,或使用一个函数对象。用于排序的列被传递给函数对象doCOMpare的构造器。
    在TableStorage.cpp文件某处增加如下函数对象,例如放在includes和TableStorage类的实现部分之间。

     这是doCOMpare函数对象的声明和实现部分,在排序中用于比较。确实,这个函数看起来可能复杂一点,详细的语法解释可以查C++手册,不过它的目的却非常简单。比较各行以便按m_direction参数指定的次序排序。以列号作为构造器中的第一个参数,并存储在一个成员变量(m_column)中。这样,当做比较时,函数对象就知道比较的是哪一列。
    注意doCOMpare是由STL binary_function模板衍生的。STL提供binary_function使得创建比较函数非常方便。
17.3.4 测试
    这个组件可用于许多地方:比如VB程序中、ASP文件中甚至于C++程序中。下面分析一下在ASP中的使用方式。


    首先创建一个字符串CSVString,它代表数据。用Server.CreateObject创建一个TableStorage对象,用ParseCSV方法对字符串进行分析。这时数据在内存中。然后根据名字按升序排序。

    注意, COMponentTest.ASP页面的全部代码可以从Worx网站上下载,本章及下一章的Visual C++项目ASPCOMponents是本书源代码的一部分。
    另外,为了得到一个字段的值,必须指定字段名。一个更灵活的界面应允许使用索引来得到字段的值。运行这个页面,浏览器中应该得到如图1 7 - 1 0所示的显示。
17.3.5 错误处理
    错误处理的两个主要方面是:
    ? 确保能捕捉所有的错误。
    ? 提供错误情况的精确描述。
    在程序开发过程中尽量早地使用合适的错误处理工具,能明显减少开发和测试时间,这是因为能更快地发现和纠正错误。可以使用C++的异常处理来捕捉错误,使用Error对象向客户端反馈信息。
    1. 异常处理
    错误处理能力受程序语言限制。一个典型的错误处理过程包含如下几个方面:
    ? 调用一个函数。
    ? 检查返回值。
    ? 如果成功,程序采用一个代码路径,如果失败采用另一个。
    这个过程的问题是导致代码嵌套,很难跟踪。另外,没有办法迫使程序员检测返回值。他们可能懒得检查返回值,或是忘了。
    我们更希望编程环境能替我们捕捉错误并引导代码按预先确定的错误处理路线运行。在Visual Basic中可以用On Error Goto <错误处理函数>做到这点。当错误产生时,程序将跳到指定的错误处理函数处。也可以在Visual Basic中调用Err.Raise来标记一个错误。
    C++和Java拥有相同的错误处理概念,叫作异常处理。异常处理允许把代码的一部分放入try块来保护它。如果错误产生于try块,那么程序的执行跳到catch块,在那儿有错误处理代码。当错误产生于catch程序块时,代码将自动指向try程序块。使用throw关键字发出错误信息。
    下面是异常处理的简单例子:

     在第1 8章中将使用这种异常处理风格。
     一旦捕获了错误,下一步是将错误信息返回给用户。如果所使用的COM对象的宿主环境是ASP,最好的方法是通过Error对象报告错误。
     2. Error对象
    如果未用On Error Resume Next,当对没有值的字段调用GetField方法时,将遇到如下错误:

     这个提示用处不大,在错误产生时需要组件能提供更多的信息,可以通过Error对象来做到这一点。
     如果创建一个新的ATL对象,并使用Error对象,可以选择ATL Object Wizard Properties对话框中的Support ISupportErrorInfo复选框,指示向导产生支持Error对象的代码。

    通过这种修改,我们的类将支持ISupportErrorInfo COM 接口。然而,需要在源文件TableStorage.cpp插入下面的代码,才可以实现增加的方法。

    我们的类从C COM CoClass派生,有一个Error方法。也就是说C TableStorage类能够使用C COM Co Class定义的所有公共方法和属性。现在C TableStorage类支持错误接口,因而可以使用这个方法。C TableStorage类也提供Error对象的所有参数,包括错误代码、描述和帮助信息。

    现在不仅得到了增加的错误信息,而且得到了组件的ProgID。当组件支持ISupportErrorInfo时,ProgID信息将自动地插入。
    另外,我极力推荐程序员在组件开发初期使用错误支持。这将有助于程序员调试组件,并能够帮助组件用户的开发工作。当然,如果不能从错误信息中判断出是什么出了错,下一步就是调试程序了。
17.3.6 调试
    在Visual C++中可以直接调试一个正常的可执行程序。可以设置断点,然后在调试模式下运行程序。如果有DLL,则需要做更多的工作: C++调试器必须附加上支持调试的进程。
    一个DLL不在自己的进程运行,它运行在其他进程中。因此,必须给调试器捆绑上能容纳DLL的应用程序进程空间。如果在Visual Basic中测试,就需要捆绑上Visual Basic;如果是在ASP上测试,则需要捆绑上Web服务器进程。另外,在Visual Basic中,可以运行VB6.EXE,打开一个使用DLL的项目,像正常情况一样设置断点。
    还有一个直接设置组件进行调试的方法,只要在组件中需要调试的地方加上下面一行即可:
         DebugBreak();
    当容纳DLL程序的进程运行到这一行时,将停下来弹出一个对话框并告诉你已经到了断点,并询问是否调试应用程序。按Cancel调用调试器(注意,按OK不进入调试器)。如果组件调试版本而不是发行版本,当按Cancel时,Visual C++将开始运行并停在插入DebugBreak() 的地方。在这可以设置另外的断点、观察变量和做其他的调试工作。
    这种方式的主要问题是其侵入性。为了调试代码必须修改代码,还得十分小心避免遗留DebugBreak()。如果组件产品中有DebugBreak(),运行时将弹出对话框并锁住每一个用户使其脱离Web服务器,等待用户按下OK或Cancel。这是很糟的。
    如果组件在ASP中使用,修改然后重新编译就会出现如下错误:
    LINK:fatal error LINK1168:connot Debug/aspComponent.dll for writing
    Error Executing link.exe
    服务器组件在Web服务器的进程空间中运行,如果得到上述错误,意味着服务器仍有此组件的引用。更确切地说,服务器正引用组件中的DLL,所以需要解除对DLL的引用。但是,做到这些需要关闭并重新启动WWW服务。然而,如果对象是在MTS中运行,这个过程就简单多了。
    COM+调试
    首先,确定设定的激活属性(activation property)是专用的服务器进程而不是库进程。这才能确保调试器绑定到所要调试的组件的进程。在Project Settings选项卡上,将Executable for debug session项设置为dllhost.exe,将Program arguments项设置为ProcessID: <进程的ID >,如图1 7 - 11所示。

    进程的I D可以通过在Component Services Explorer中右键单击应用程序图标,并选择Properties来获得,如图1 7 - 1 2所示。

    关闭组件所在的应用程序的服务器进程,确保组件当前不会驻留在内存中。否则,可能会使用没有绑定调试器的组件实例。确被建立组件的调试版本,在调试版本中设置所需断点并从Build菜单中执行程序。
    运行带有程序参数的dllhost.exe,以指示它载入包含被调试组件的应用程序。因为COM+进程绑定了调试器,无论什么时候访问组件,调试器都能在断点处中断调用。
    如果对象失败,有关C++中的组件的其他情况都自动地放在事件日志中。
    本章介绍了C++的起源以及设置与开发ASP组件相关的环境。读者已经学习了一部分C++,如STL和ATL,这些都是创建ASP组件的有用工具。这些工具都能满足服务器组件的设计需要,其目的就是减少存储空间和访问时间。为了介绍它们的使用方法,本章创建了一个能用于任何应用程序的通用组件。
    组件的可利用能力的一个重要方面是其错误处理。C++异常处理提供了在组件中捕获错误的有效方法。要想在应用程序调用之外报告错误,组件需要支持ISupportErrorInfo。
  
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·利用索引服务(Indexing Service)  (2005-04-02)
 ·用TDC建立自己的数据库格式  (2005-04-02)
 ·建立灵巧结构的PHP程序一例  (2005-04-02)
 ·建立ASP.NET开发平台  (2005-04-02)
 ·JDBC 入门(二) - 建立联接  (2005-04-02)
 ·快速建立Servlet和JSP的运行、调  (2005-04-02)
 ·Freebsd+Resin成功建立支持jsp平  (2005-04-02)
 ·使用用JSP建立灵活安全的Web站点  (2005-04-02)
 ·用ASP建立站内搜索  (2005-03-12)
 ·怎样才能方便的建立自己的每日更  (2005-03-12)

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·DiscuzX2.5会员整合通行证发布
·帝国CMS 7.0版本功能建议收集
·帝国网站管理系统2012年授权购买说
·PHPWind8.7会员整合通行证发布
·[官方插件]帝国CMS-访问统计插件
·[官方插件]帝国CMS-sitemap插件
·[官方插件]帝国CMS内容页评论AJAX分
   类别最新
·在ASP中使用数据库
·使用ASP脚本技术
·通过启动脚本来感受ASP的力量
·学习使用ASP对象和组件
·解析asp的脚本语言
·初看ASP-针对初学者
·ASP开发10条经验总结
·ASP之对象总结
·ASP与数据库应用(给初学者)
·关于学习ASP和编程的28个观点
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统