LinkWorks .net平台技术白皮书.doc
LinkWorks .net平台技术白皮书梦龙软件有限公司2006-4-13第一章运行环境1第二章数据安装与存储4第三章常见问题6第一章 运行环境1.1 服务器配置LinkWorks服务器要求操作系统为 Windows 2000 / 2003,IIS 5.0 或更高版本。为保证LinkWorks运行稳定,建议为其单独设置一台服务器,并及时为操作系统安装补丁程序。推荐的服务器配置为:² CPU: Pentium4 1.6GHz² 内存:2G 内存 或更高² 硬盘:100G 根据本公司的数据可扩大硬盘容量。² 网卡:100/1000M² 操作系统:Windows 2000、2003 Server1.2 客户机配置LinkWorks客户机要求操作系统为Windows 2000/XP/2003。推荐的客户机配置为:² CPU: Pentium3 600MHz² 内存:256M 内存² 硬盘:20G² 网卡:100M² 操作系统:Windows 2000、xp、2003 Professional² IE版本: IE6.0以上版本1.3 网络连接LinkWorks的标准服务中,除了文档管理系统由于经常需要传输较大文件需要宽带网(如ADSL连接)支持外,其他服务均可运行在窄带网(如56K拨号连接)中。为更好地发挥系统性能,推荐局域网网络带宽为100M,广域网网络带宽为512K。1.4 代理服务器LinkWorks支持网络代理服务器。如果LinkWorks服务器位于代理服务器之后,则必须在代理服务器配置中将相关端口映射到LinkWorks服务器上,以保证来自外网的连接能够访问到LinkWorks服务器。需要在代理服务器、或者防火墙上建立映射的端口如下:端口号协 议端 口 说 明80TCP网站端口,根据用户自行设置211TCP三层结构服务通讯端口18880TCPICU解析服务器监听端口18881TCP加密锁服务通讯端口18882TCP文件传输服务通讯端口18883TCP工作管理系统服务端端口 18884TCP定时提醒服务通讯端口18885TCP用于MrDogCom的远程服务端口18886TCPMRLKMGR 主控程序和远程控制程序进行消息通讯的端口18887TCP短信服务监听端口18888TCP通讯服务器监听端口18889TCPICU监听端口18890TCP通讯服务器文件监听端口18891TCPICU文件监听端口18892TCP多媒体通讯系统客户端口18893TCPICU屏幕监控端口18895TCP文件下载服务MrFileSrv.exe18896TCP升级服务MrUpdateSrv.exe18897TCP中转服务器监听端口18898TCP文件服务器文件监听端口18899TCP文件服务器监听端口18900TCP控制服务器监听端口18901TCP控制服务器文件监听端口18902TCPICU数据库服务器监听端口18903UDP媒体服务器控制端口UDP18904TCP网站管理服务文件传输端口MrWebManageSvr.exe18905TCP手机短信服务监听端口如果条件允许,建议采用这种方式,但如果不想再做过多投资,如下面的情况,就要做一些设置了。使用软件代理上网(如Sysgate,Internet共享等)的Intranet如果媒体服务安装到代理服务器机器上,那么直接在LinkWorks主控台设置媒体服务的地址为代理服务器的外网IP即可。如果媒体服务安装到Intranet里的某台机器A,那么需要在代理服务器P上对代理软件进行设置,设置代理服务器P的18903 UDP端口映射到机器A上。然后在LinkWorks主控台设置媒体服务的地址为代理服务器的外网IP即可。通过硬件防火墙接入Internet的Intranet目前硬件防火墙通常有Intranet区和DMZ区。Intranet区可以通过防火墙访问Internet,但Internet访问Intranet内机器往往受限制。而DMZ区是非军事化区,不管是Internet上的机器还是Intranet内的机器访问DMZ区内的机器时都会被防火墙将地址转换成DMZ内部地址,所以通常将各种服务器放在这个区。不过我们的媒体服务为了支持点对点通讯是不能放在DMZ区内的,这样媒体服务只有放在Intranet区了。这里要注意防火墙必须支持NAT映射功能,才能将服务器的18903 UDP端口映射到防火墙对外的相应端口上。最后在LinkWorks主控台设置媒体服务的地址为防火墙的外网IP即可。通过路由器接入Internet的Intranet首先要保证路由器支持NAT功能,接着要将媒体服务安装到Intranet里的某台机器A,然后需要在路由器R上设置18903 UDP端口映射到机器A上,最后在LinkWorks主控台设置媒体服务的地址为路由器的外网IP即可。第二章 数据安装与存储2.1 安装服务器操作系统的软件将服务器从网络环境中隔离出来,重新安装操作系统win2000 server、win2003 server(根据客户不同的情况实际处理是否需要重新安装操作系统和操作系统的类型。但为了保证一个干净稳定的运行环境,建议客户重新安装操作系统。);2.1.1检查是否安装IIS服务重新安装操作系统只要注意安装IIS服务即可以。没有重新安装操作系统的需要检查是否安装了IIS服务,IIS服务是中含有web服务,web服务是保证网站正常使用必不可少的服务。2.改换web网站的端口号。基于网络安全的考虑,建议客户改换web网站的端口,将端口修改为不常用的端口号,减少网站被攻击的危险。2.1.2安装防火墙、防病毒软件建议使用诺顿产品。为了保证系统的稳定和软件正常的使用,安装防火墙和防毒软件是必不可少的步骤;设置防火墙的端口。由于软件使用的端口比较的多,而且这些端口为高端端口,防火墙默认是关闭这些端口,这就造成了软件不能正常的使用。开放的端口和各个端口的作用见 1.4代理服务器。2.1.3连接网络,打相应的操作系统补丁程序为了保证系统的正常运行不受干扰,尽量打全补丁。可以在自动更新选项里设置为自动更新,以保证系统即时打上最新的补丁程序,安装SQL Server 2000 Enterprise数据库软件,安装完毕后打相应的补丁。需要注意的是SQL Server 2000在win2003操作系统中必须打SQL SP3以上的补丁,否则不能正常使用数据库。2.1.4 SQL Server安装方法:安装 SQL Server 2000,设置 SQL Server 用户认证方式时,应当选择 SQL Server 与Windows 混合认证,而不是 Windows 认证。在混合认证模式中,数据库管理员帐号为“sa”,默认密码为空。修改已存在的 SQL Server 用户认证方式的方法是:进入 SQL Server 2000 的“企业管理器”(Enterprise Manager),在该 SQL Server 服务器上单击鼠标右键,选择“属性”菜单,在弹出的属性对话框中选择“安全性”(Security)页面,将身份认证改为“SQL Server 和 Windows”即可。2.1.5安装LinkWorks协同工作平台等系列产品(根据用户购买不同的软件安装包);1.安装 LinkWorks 平台和加密锁服务运行 Setup.exe 安装 LinkWorks 平台服务程序,考虑到 LinkWorks 平台数据会越来越多,建议将平台安装到一个较大的硬盘分区上。安装完毕后,插入加密锁,系统应能够自动识别加密锁并提示安装。2.在SQL Server中配置数据库;数据库LinkWorks高端版本使用 SQL Server 2000 数据库。该数据库为网络数据库,支持多用户并发访问操作,具有良好的嵌入性和扩展性。用户需要对 SQL Server 的日常维护和管理具有一定的了解。数据量LinkWorks运行过程中,服务端除了数据库中的记录会不断增加外,还会不断增加数据文件,如工作管理系统的总结附件、文档管理系统的文档文件、信息发布系统中上载的软件和图片、即时通讯系统通过服务器转发的文件等。如果有50个用户经常在线,LinkWorks平均每天产生的数据量大约在10M左右。该数据为估计值,实际使用中产生的数据量与用户常用的数据和文件类型有很大关系。数据备份LinkWorks所有工作数据均存放在Data目录下,因此进行数据备份时只需备份该文件夹。恢复数据时,用安装盘安装LinkWorks,然后将备份的Data目录恢复回来即可。SQL Server 2000版本的用户还需要另行备份 SQL Server 数据库文件。备份工作可以利用NT和 SQL Server 2000自带的备份工具和任务计划工具来实现。第三章 常见问题3.1 安全选项设置主窗体>工具>Internet选项>安全,如图3.1。图3.1点击“自定义级别”进入后,如图3.2。图3.2关于“ActiveX控间与插件”的选项必须启动起来,否则客户端不能载入服务器端的一些控件,导致报错!3.2 弹出窗口提示如果客户端出现此窗体,如图3.3。图3.3A.如上图出现3721 雅虎助手 网络助手等提示屏蔽信息时,您只需要用鼠标右键点击安装即可。B.如果没有此类提示上传附件不成功时,需要设置IE属性,操作方法如下:在主窗体中工具选项>Internet选项>常规(删除Cookies),还有安全>自定义级别(所有关于ActiveX控件全部启用,确定即可)。第四章 开发接口说明4.1 LinkWorks主框架接口变量说明aspx/用户ID,可用此值去USR表取得除密码外的任何用户信息Session"UsrID" = UsrID;/用户姓名Session"UsrName" = UsrName;/用户部门ID,可用此值去DEP表取得任何部门信息Session"UsrDepID" = UsrDepID;/用户登录名Session"UsrCode" = UsrCode;/即时通在线状态,1代表在线Session"UsrStatus" = UsrStatus;asp/登录标记,防止直接敲url进入页面Session("Passed") ="1" /用户IDSession("UserID") =UsrID/用户登录名Session("UserLogname")=UsrCode/用户姓名Session("UserName") =UsrName/用户部门IDSession("UserDepID") =UsrDepID/即时通在线状态Session("UserState") =UsrState/即时通在线状态同上Session("UsrStatus") =UsrStatus/数据库连接字符串Session("ConnStr") =ConnStrApplication变量for aspx/ICU服务器地址MrICUServer/加密锁服务器地址LockServer/文件传输服务器地址MrTransServer/数据库类型DBType/数据库驱动类型DBProvider/数据库连接字符串ConnectionString4.2 通用授权使用说明4.2.1通用授权系统的组成通用授权系统由以下4个基本部分组成:1、权限设计器2、分级授权模块3、功能授权模块4、通用授权接口类4.2.2权限设计与授权操作1、设计并注册权限使用通用授权系统前,需要将软件模块所需的权限在权限设计器(图4.1)中进行注册。图4.1 权限设计器首先,进行模块注册,需要填写模块的名称和编码。该编码影响到以后的其他授权功能的调用。图4.2注册新模块注册完成模块之后,可以在模块下建立功能组或者直接注册具体的功能权限。功能组只是为了将功能进行意义上的分组,在授权操作的时候更加清晰。下面具体讲述功能权限的注册。进行功能注册,首先需要填写功能的基本信息(图4.3),其中主要是功能的名称和编码。功能的编码影响到之后对该功能权限的设置的获取。图4.3 授权基本信息注册图4.4 授权对象类型选择填写完基本信息之后,按“下一步”之后,将选择授权对象的类型(图4.4),目前的应用中,通常选择“部门/用户/角色”,表示使用该功能的主体是linkworks组织结构中的对象。再次按下“下一步”按钮之后,将设置该功能权限的授权范围类型(图4.5)。范围类型主要包括下图(图4.6)中的5种。图4.5授权范围类型选择图4.6 “部门/用户/角色”类型的范围表示该功能的操作对象是linkworks中的组织结构对象。该类型可以设置是否显示部门、用户、或角色,并可以设置仅仅允许选择用户对象。示例:查看工作总结的功能中,需要设定某一用户可以查看哪个部门或哪个用户的总结,应此需要设置该种类型的授权范围。“自定义列表”类型的范围表示该功能的操作对象是一系列自定义的对象或数据。例如某一表格中,需要由好几个不同的用户来填写,其中包括领导填写的项目和其他人填写的项目。这种情况下,可以为不同人填写的区域设定范围项。其中的“唯一标识”影响到权限的设置和获取,如图4.7所示。图4.7“数据库对象”类型的范围比较复杂,有图4.8所示几种情况。表示该功能的操作对象是依据数据表中的数据构建的。设置该类型范围,需要额度发按情况选择数据表和相应字段(图4.9所示)。其中,“节点类型”字段建议手动输入“0”、“1”等简单的标识数据,该数据是为了区别主表的记录和从表的记录。图4.8图4.9 “普通数值”类型和“是/否”类型的范围比较简单。当功能中需要简单的判断某一用户是否能进行操作,就使用“是/否”类型的范围。当功能中需要判断用户能操作的数值范围时,即使用“普通数值”的范围类型,例如短信发送模块中需要设置用户每月能发送的短信条数。2、分级授权分级授权模块是为了将授权的职能和范围授予其它用户,减轻授权管理员的工作量。分级授权中,首先需要选择要进行分级的功能,和需要授予的部门用户。然后在右侧选择当前选中部门用户能进行继续分级的其它用户,能进行授权的对象,以及能进行授权的范围(图4.10)。图4.103、功能授权功能授权是通用授权系统中最核心的部分之一,它的功能将设计的功能权限与部门用户等授权对象、范围关联起来,进行设置并保存。功能授权的主界面是“授权列表”视图(图4.11)。图4.11授权列表的最上方是过滤工具条,能选择各种过滤条件来显示已经存在的授权记录。符合条件的授权记录,将显示在下方的列表中。点击右上角的“添加授权”按钮,将弹出添加授权向导对话框(图4.12、4.13、4.14)。 图4.12图4.13 图4.15 图4.14按向导步骤选择完进行授权的功能、授权对象与授权范围之后,点击“完成”按钮,将向服务器提交授权的数据,并返回保存的信息。完成后可以选择继续添加或是关闭向导。添加完成后,授权列表中将显示出刚才添加的授权记录,这时可以选择相应的授权记录进行授权范围的设置了。选中点击授权列表下方的“添加授权范围”按钮,将弹出授权范围设置对话框(图4.16)。图4.16该对话框显示的内容跟权限设计时选择的范围类型有关。勾选对应范围节点,按上方的“提交”按钮,授权范围数据将提交到服务器中。4.2.3权限接口调用通用授权系统中包含AuthInterface.dll接口动态库,该库中包含名为“CommonAuthorize”的类,该类提供了设置权限、取授权范围等一系列接口。1、 获取某一对象的授权范围的接口GetAcd()该接口是最重要的接口之一,用来判断某一授权对象的授权范围中是否包含某一范围对象,或返回所有的授权范围对象列表。定义为:public string GetAcd(string AModuleCode, string AFunctionCode, string AObjID, string AObjType, string ARangeObjID, string ARangeType)AModuleCode参数:需要判断的权限对应的权限功能的模块编码;AFunctionCode参数:需要判断的权限对应的权限功能的功能编码;AObjID参数:授权对象的标志;AObjType参数:授权对象的类型。当是部门用户类型的对象时,0表示部门,1表示用户,2表示角色;ARangeObjID参数:授权范围对象的ID;ARangeType参数:授权范围对象的类型。当是部门用户类型的对象时,0表示部门,1表示用户,2表示角色;2、 获取某一授权对象所能操作的所有功能的接口GetObjAuthedFunction()该接口返回某一授权对象能操作的所有功能编码列表,定义为:public string GetObjAuthedFunction(string AModuleCode, string AObjID, string AObjType, int AAuthRegObjType)AModuleCode参数:需要判断的模块编码;AObjID参数:需要判断的对象;AObjType参数:授权对象的类型。当是部门用户类型的对象时,0表示部门,1表示用户,2表示角色;AAuthRegObjType参数:授权功能注册的类型。0 部门用户类型; 1 自定义列表; 2 数据库对象类型; 3 数值类型; 4 是否类型。4.3 MRICU接口使用说明4.3.1调用方法说明Borland Delphi1、 在系统菜单里选择Project选项,选择Import Type Library菜单,在弹出的对话框中找到Project1 Library(Version 1.0),Class Name框里应该是TMRICU2,单击Create Unit按钮,生成Morrowsoft_TLB.pas文件。2、 在需要调用MRICU接口的工程文件里包含Morrowsoft_TLB.pas文件。Microsoft Visual C+在StdAfx.h文件里加入语句#import "D:MRICU.exe" no_namespace注意: 把D:MRICU.exe换为本机MRICU.exe的路径。ASP网站系统请将下面代码拷贝到需要的网页中:<OBJECT classid=clsid:2C967898-908B-4C25-9E0F-6EF31F447AB0 height=1 id=ICUXForICU1 style="HEIGHT: 1px; WIDTH: 1px" width=1 ></OBJECT>4.3.2数据结构定义MRICU用户登录信息TICULogInfor = recordID: integer; /用户标志Name: string20; /用户姓名Code: string20; /用户代码Dep: integer; /用户部门OnlineState: integer; /当前在线状态end;MRICU用户信息TICUUser = recordID: integer; /用户标志Name: string20; /用户姓名Code: string20; /用户代码Sex: string2; /用户性别Sort_No: integer; /用户排序号Dep: smallint; /用户部门bFriend: boolean; /是否在好友列表中bResponsible: boolean; /是否是部门负责人end;MRICU部门信息TICUDepart = recordID: integer; /部门标志Sort_No: integer; /部门序号Parent: integer; /上级部门标志Name: string20; /部门名end;MRICU角色信息TICUGroup = recordID: integer; /角色标志Sort_No: integer; /角色序号Name: string20; /角色名end;MRICU服务器信息TServerInfor = recordServerIP: string50; /服务器地址LocalProxy: string50; /本地代理地址ServerProxy: string50; /远程代理地址iServerPort: smallint; /服务器端口iLocalProxyPort: smallint; /本地代理端口iServerProxyPort: smallint; /远程代理端口end;4.3.3接口函数说明提供给Borland Delphi和Microsoft Visual C+等高级编程语言的接口说明:与是否有关的参数HResult,一般0代表操作失败,1代表操作成功。Function GetLogInfor(out ALogInfor: OleVariant; out ALoged: SYSINT): HResult;函数功能 :得到本机MRICU的登录用户信息。参数说明 :Aloged, MRICU已经登录取值为1,未登录取值为0;AlogInfor, 登录信息数组。如果已经登录,则有AlogInfor0=ID,AlogInfor1=Name,AlogInfor2=Code,AlogInfor3=Dep,AlogInfor4=OnLineState; 见数据结构Function GetUserInfor(AKind: SYSINT; out AData: OleVariant): HResult; 函数功能 :得到用户,部门,角色信息。参数说明 :Akind, 需要取得信息的类型。0为用户信息, 1为部门信息, 2为角色信息。Adata, 信息内容。为人员,部门或角色数组。假设Akind为0,则有Adatax0=ID,Adatax1=Name,Adatax2=Code,Adatax3=Sex,Adatax4=Sort_No,Adatax5=Dep,Adatax6=bFriend,Adatax7= bResponsible; 见数据结构function SendMsg(AFrom: SYSINT; ATo: OleVariant; const AMsg: WideString;AHold, AOnlineOnly: SYSINT; const ASoft: WideString; out ARet: SYSINT): HResult;函数功能 :发送消息给MRICU用户。参数说明 :Afrom, 发送者ID;Ato, 收者数组;Amsg, 消息内容;Ahold, 消息保留天数,AonlineOnly, 是否只发给在线用户,Asoft, 发送消息所用的系统,Aret, 发送是否成功。function SendBroad(AFrom: SYSINT; AChild: SYSINT; AOnline: SYSINT; AHold: SYSINT; ADepart: SYSINT; const AMsg: WideString; out ARet: SYSINT): HResult;函数功能 :广播消息给MRICU用户。参数说明 :Afrom, 广播者ID;Achild, 是否发给子部门;Aonline, 是否只发给在线用户;Ahold, 消息保留天数;Adepart, 消息广播到的部门;Amsg, 消息内容;Aret, 广播是否成功。function GetServerInfor(out AServer: OleVariant; out ARet: SYSINT): HResult;函数功能 :得到服务器信息。参数说明 :Aret, MRICU是否已经登录到服务器;Aserver, 服务器信息数组,如果已经登录,则有Aserver0=ServerIP;Aserver1=LocalProxy;Aserver2=ServerProxy;Aserver3=iServerPort;Aserver4=iLocalProxyPort;Aserver5=iServerProxyPort; 见数据结构。function OpenSendMsg(AID: SYSINT; out ARet: SYSINT): HResult;函数功能 :打开MRICU的发送消息对话框。参数说明 :Aret, 打开是否成功;AID, 消息发送给的用户ID。function OpenUserInfor(AID: SYSINT; out ARet: SYSINT): HResult;函数功能 :打开MRICU的用户信息对话框。参数说明 :Aret, 打开是否成功;AID, 要查看的用户ID。function GetGRPMemberList(AID: SYSINT;out AInfor: OleVariant): Hresult;函数功能 :获取某个角色的所包含的成员的ID。参数说明 :AID, 角色的ID;AInfor,返回的用户列表。返回数据如下所示Ainfor00, Ainfor10, Ainfor20function GetIP(AINDEX: SYSINT; out AData: OleVariant): Hresult;函数功能 :通过索引,得到对应分配的IP。参数说明 :AINDEX, IP数组的下标;AData,返回的分配的IP。Function SendFile(const AFiles: OleVariant;const AUsers: OleVariant):HResult;函数功能 :得到所有用户信息。参数说明 :AFiles,文件列表。各文件名用回车换行符隔开。格式如下“D:a.txt”+#13#10+“D:b.txt”Ausers,人员列表。各人员ID用回车换行符隔开。格式如下“1”+#13#10+“2”返回值 :0调用发送接口失败。 1调用发送接口成功。见高级编程语言相同功能接口说明。提供给ASP网站系统的接口procedure SendMsg(AToID: SYSINT; const AMsg: WideString; AHold,AOnlineOnly: SYSINT;Asoft: WideString; out ARet: OleVariant);函数功能 :发送消息给MRICU用户。参数说明 :见高级编程语言接口。procedure SendBroad(ADepartId: SYSINT; const AMsg: WideString; AChild,AHold, AonlineOnly: SYSINT; out ARet: OleVariant);函数功能 :广播消息给MRICU用户。参数说明 :见高级编程语言接口。procedure GetLogInfor(out Aloged, AId, AName, ACode, ADep, AonlineState: OleVariant);函数功能 :得到本机MRICU的登录用户信息。参数说明 :Aloged, 用户是否登录;AID, 用户标志;Aname, 用户姓名;Acode, 用户代码;Adep, 用户部门,AonlineState, 用户是否在线。procedure GetUsrInfo(out Users: OleVariant);函数功能 :得到所有用户信息。参数说明 :Adata,人员数组。见高级编程语言相同功能接口说明。function GetCompany(out AName: OleVariant): HResult;函数功能 :得到软件频道里软件信息。参数说明 :AName:公司名称function GetSoftList(out AData: OleVariant): HResult;函数功能 :得到软件频道里软件信息。Adata的数据格式如下:Adatax0= 软件路径名,Adatax1= 标题,Adatax2= 起始位置,Adatax3= 参数,Adatax4= 是否是超链接,Adatax5= 快捷类型4.3.4编码示例取得所有用户信息Borland Delphi5procedure GetMRICUUser;var FMRICU: IMRICU2; i: integer; Users: OleVariant; User: TICUUser;begin FMRICU:=CoMRICU2.Create; if FMRICU<>nil then if FMRICU.GetUserInfor(0,Users)=1 then begin for i:=VarArrayLowBound(Users,1) to VarArrayHighBound(Users,1) do begin User.ID:=Usersi0; User.Name:=Usersi1; User.Code:=Usersi2;User.Sex=Usersi3;User.Sort_No=Usersi4; User.Dep:=Usersi5; User.bFriend:=Usersi6;User.bResponsible=Usersi7; /HandleUserInfor; end; exit; end;end;Microsoft Visual C+6void GetMRICUUser IMRICU2Ptr icu;int usernum;VARIANT varData1;VARIANT varData2;SAFEARRAY *pData1 = NULL;SAFEARRAY *pData2 = NULL;icu.CreateInstance("MORROWSOFT. MRICU2");if (icu->GetUserInformation(&usernum,&varData1)!=1)AfxMessageBox("函数调用失败!");return;if (!(varData1.vt&VT_VARIANT)&&(varData1.vt&VT_ARRAY)AfxMessageBox("函数参数数据格式不对!");return;if (varData1.vt&VT_BYREF)pData1=*(varData1.pparray);elsepData1=varData1.parray;long llbound,lubound;:SafeArrayGetLBound(pData1,1,&llbound);:SafeArrayGetUBound(pData1,1,&lubound);for (int i=llbound; i<=lubound; i+)VARIANT v;user u;memset(&u,0,sizeof(u);long j=i;if (:SafeArrayGetElement(pData1,&j,&varData2)=S_OK)if (!(varData2.vt&VT_VARIANT)&&(varData2.vt&VT_ARRAY)AfxMessageBox("函数参数数据格式不对!");return;if (varData2.vt&VT_BYREF)pData2=*(varData2.pparray);elsepData2=varData2.parray;long ll,lu;:SafeArrayGetLBound(pData2,1,&ll);:SafeArrayGetUBound(pData2,1,&lu);long k=ll;:SafeArrayGetElement(pData2,&k,&v);if (v.vt&VT_INT)u.ID=v.intVal;k=k+1;:SafeArrayGetEleme