建立一个C++服务器组件

c++|服务器

    
    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 fordebug 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。

时间: 2025-01-19 09:24:37

建立一个C++服务器组件的相关文章

在树莓派上建立VPN(一):如何以及为何建立一个VPN服务器?

在树莓派上建立VPN(一):如何以及为何建立一个VPN服务器? 不要相信任何人,自己建立一个VPN服务器来加密Web数据从而躲过他人的窥视 虽然免费.未加密的无线AP遍地都是,但是你不应该连接这些AP来登陆你的网银账户,除非你对他人的窥视毫不在意.那么对此的解决方案是什么呢?一个虚拟专用网,也就是VPN(virtual private network). 一个VPN可以使你的私有网络拓展至公共场所,因此即使你连接着星巴克的 Wi-Fi,你的网络浏览仍然保持着安全的加密. 有很多方法来建立VPN,

建立一个SQL服务器的最低条件是什么?

问题描述 单位的宽带,没有固定IP,这样的条件安装一个SQL服务器在开发软件时,连接到自己的Sql服务器,能用吗?另外,家里一台能上网的电脑装个Sql服务器,能用吗?如果不能,建立一个SQL服务器能在internet上用的最低条件是什么? 解决方案 解决方案二:能用能用你考虑的有些过头了有没有网在本机都能用解决方案三:知道在本机可以用,但是c/s程序时,用于连接Sql服务器的语句肯定是有192.168.1.254之类的ip地址了,不知道能不能连接到,ip地址不是固定的ip呀解决方案四:需要一个固

ASP服务器组件的编程

编程|服务器 重庆出版社电脑中心 陈刚 1.什么是ASP服务器组件 ASP(Active Server Page)是当今开发交互式Web页面.Web数据库应用最强大的技术.在其中可以混用HTML.DHTML.ActiveX.VBScript或JavaScript.当这些技术都无法奏效时(例如进行高密度的数学运算.封装特定的数据库处理逻辑等),可以使用服务器组件(Server SideComponent)进一步扩展ASP的能力.Server SideComponent实际上是运行在服务器上的一个D

服务器问题-如何建立一个服务器,内外网问题

问题描述 如何建立一个服务器,内外网问题 请教,建一个ad要如何做. 另外我要建一个公司邮箱及一个vpn该如何设置. 还有如何把内外网分开 如有图片,请附上. 请教,请教.

在FreeBSD上建立一个功能完整的邮件服务器

#1 在FreeBSD上建立一个功能完整的邮件服务器 第一部分:安装邮件服务器:postfix+vm-pop3d+openwebmail 欢迎大家转贴这个文章,但要保留下面的版权信息: 作者:llzqq 联系:llzqq@126.com 以下的安装在FreeBSD 5.2.1系统上完成 1.更新 ports # cvsup -gL 2 -h cvsup.freebsdchina.org /usr/share/examples/cvsup/ports-supfile 2.安装 openssl+ap

socket-c#将客户端发来的数据流实时转发给另一个客户端 服务器数据缓冲区的建立问题

问题描述 c#将客户端发来的数据流实时转发给另一个客户端 服务器数据缓冲区的建立问题 我想要实现两部安卓客户端的实时语音通讯,安卓客户端都在内网,因此需要一外网pc做数据的中转服务器,安卓语音发送端通过audiotrack录制PCM音频数据并实时发送到服务器,服务器接收数据并不断读入一个缓冲区,开启另一个线程循环读取缓冲区的数据同时将数据发送到另一个安卓客户端,问题就在于服务器的缓冲区要怎么做?怎么保证缓冲区的数据同时被两个线程操作时的有序稳定 求大神指教,假设网络良好,不考虑网络波动带来的数据

服务器-建立一个新的数据库,如何开头?

问题描述 建立一个新的数据库,如何开头? 初学数据库,现在电脑上装了powerdesigner 和PLSQL Developer 也配置了Oracle环境,单位也有服务器,现在需要设计一个数据库,主要功能能够存储一些文档资料,有表格,图,GIS图,还有文字性的资料,现在需要一个大神来给我指指路,如何开头,该怎么入手,学习什么软件. 解决方案 文档资料,gis图一般不都存在服务器 解决方案二: 先自己学会建基础的数据库,,然后再做项目,,都是根据需求来建的 解决方案三: 先了解这几个软件的用法,找

利用MFC写一个Tcp程序,基于事件选择机制建立一个回射服务器(直接把收到的客户端消息发回给客户端)

问题描述 利用MFC写一个Tcp程序,基于事件选择机制建立一个回射服务器(直接把收到的客户端消息发回给客户端) 刚开始学习window套接字编程,很多东西还不大熟,想请高手帮忙写出这个程序作为我学习相关理论的模板 解决方案 http://pan.baidu.com/s/1hs2hKEg分享给你这个视频教程 跟着视频一步一步做可以做一个局域网聊天室 同时学习mfc编程与socket编程

服务器网站分离 给每个IIS站点建立一个用户第1/2页_win服务器

一.这样配置的好处? 不知大家有没有听过旁注?我简单的解释一下吧:有个人想黑掉A站点,但找来找去都没发现可利用的漏洞,无意中他发现与A同服务器上还有个B站点,并且在B站点上找到了可利用的漏洞,于是他将木马从B站中上传至服务器,如果服务器权限配置不当,那么现在他就可以黑掉服务器上的所有站点了!如果我们为每个站点都建立一个用户,并设置该用户只有访问本站点的权限,那么就能将访问权限控制在每个站点文件夹内,旁注问题也就解决了. 二.准备工作 Win2K 服务器版 + IIS 5.0 各分区文件系统为NT