本实用新型专利申请,依据美国专利法第119(e)条第35款,要求2004年5月14日提交的号码为60/571,271的美国临时专利申请的权益,在这里引用其全文内容作为参考。
优选实施例的详细说明
本发明旨在让系统不支持的零售设备的用户象其它设备的用户那样获得“即插即用”般的感受。当安装一个不受支持的设备时,操作系统发送一个事件,如一个PnP事件,给在用户模式下操作的设备管理应用程序,该模式允许应用程序通过一个公共控制库(CCL)访问该设备。该CCL库旨在显著简化用于不受支持的设备的应用程序和服务对象的编写,提高产品的兼容性和质量,并降低成本。
根据本发明的一个实施例,遵循在统一服务终端(UPOS)V1.8规范中定义的零售设备分类法。UPOS V1.8规范可以从国家零售联合会的网站上(www.nrf-arts.org)获得。UPOS支持的一些零售设备包括:碰撞条;现金兑换机;现金出票机;信用卡认证终端;投币自动售货机;财务打印机;硬件核对机;键锁;条形码扫描器;音调指示器;动作检测器;线路显示器;磁性墨字字符识别读取器;磁条读取器;个人身份号码簿;点卡;POS键盘;POS打印机;远程订单显示器;税率表;签名识别器;以及支票图像扫描仪。本发明不限于支持的零售设备。任何没有包括在操作系统的支持的PnP设备列表中的设备都能够得到支持。
贯穿于整个规范,除非上下文中明确的作出其它指示,下列术语的定义如下。术语“OPOS”指的是销售或服务终端的对象链接和嵌入(Ole)。术语“UPOS”指的是用于销售或服务终端的统一规范。术语“POS类外设”或“OPOS设备”指的是落入到UPOS V1.8规范中定义的24种不同设备分类中之一的设备的集合。术语“设备类”是POS设备的一个类别,共享属性,方法,以及事件的相容组。例子是现金出票机和POS打印机。一些设备支持多于一种设备类。例如,某些POS打印机包括一个现金出票机。术语“控制对象(CO)”指的是一个对象,它将该组属性,方法,以及事件公开给一个用于指定设备类的应用程序。术语“服务对象(SO)”指的是一个对象,它由一个CO来调用,并为一个指定设备执行UPOS指定的功能。SO能够在CLR支持的任何语言环境下执行,包括本地代码。术语“不支持的设备”或“非支持设备”指的是在缺省条件下,不受基础操作系统支持的任何设备。
示例性操作环境
以下参照如图1所示,一个用来执行本发明的示意性系统包括一个计算设备,如计算设备100。在一个非常基本的配置中,计算设备100典型的包括至少一个处理单元102和系统内存104。取决于计算设备的精确配置和类型,系统内存可以是易失性的(如RAM),非易失性的(如ROM,闪存等等)或二者的一些组合。系统内存104典型的包括一个操作系统105,一个或多个程序模块106,并且可以包括程序数据107。PnP零售应用程序120旨在为零售设备提供PnP功能。图1中虚线108内的那些组件示出了这个基本配置。
计算设备100可以带有额外的特性和功能。例如,计算设备100也可以包括额外的数据存储设备(可移动的和/或不可移动的),例如磁盘,光盘或磁带。图1中,可移动存储装置109和不可移动存储装置110示出这种存储装置。计算机存储介质可以包括采用任何方法或技术制造的易失性和非易失性的,可移动的和不可移动的介质,来存储信息,这些信息可以是计算机可读指令,数据结构,程序模块或其它数据。系统内存104,可移动存储装置109以及不可移动存储装置110都是计算机存储介质的示例。计算机存储介质包括,但不局限于,RAM,ROM,EEPROM,闪存或其它存储技术,CD-ROM,数字化视频光(DVD)或其它的光学存储装置,磁带盒,磁带,磁盘存储装置或其它的磁存储设备,或任何可以用来存储想要的信息并能够由计算设备100进行访问的介质。任何这样的计算机存储介质可以是设备100的一部分。计算设备100还可以包括输入设备(多个)112,如零售设备,键盘,鼠标,笔,语音输入设备,触摸输入设备等等。还可以包括输出设备(多个)114,如显示器,扬声器,打印机等等。
计算设备100还可以包括通讯连接器116,允许该设备与其它的计算设备118进行通讯,如通过网络。通讯连接器116是通讯介质的一个例子。通讯介质典型的可以通过计算机可读指令,数据结构,程序模块,或调制数据信号中的其它数据,如载波或其它传输机制来实现,以及包括任何信息传输介质。术语“调制数据信号”指的是包括一个或多个自身特征集合或按照信号中的编码信息的方式进行改变的信号。举例来说,但并不局限于此,通讯介质包括象有线网络或直接连线连接这样的有线介质,也包括象声频,射频,红外这样的无线介质以及其它的无线介质。这里用到的术语计算机可读介质包括存储介质和通讯介质。
零售设备的即插即用(PnP)
图2示出的是根据本发明诸多方面的PnP零售系统的一个通用系统框图。
用于零售设备的PnP支持简化了安装和维护POS设备的任务。例如,终端用户可以简单的拔下一个POS设备,并且可以在不需要重新启动或重新配置他们的机器来与新设备发生相互作用的情况下,插入新的POS设备。
通常的,当用户安装一个不支持的设备时,如零售设备210,该安装作为一个PnP事件被操作系统220检测到。然而,操作系统220不能安装这样的零售设备,因为该设备是不支持的,并且操作系统不具备安装这样的设备所需的知识。一旦有了PnP事件,PnP管理器225就将该事件发送给设备管理器应用程序230。因为设备管理器应用程序230驻留在用户模式中,与操作系统的内核模式相比较而言,设备管理器应用程序230可以在不改变操作系统的情况下进行调整。一旦初始化,设备管理器230注册以便从操作系统接收PnP事件,象其它I/O事件一样。
由操作系统220发送到设备管理器应用程序230的PnP事件包括一个已安装的设备的设备ID。设备管理器230试图从接收到的设备ID中识别该设备。当该设备是设备管理器230支持的设备时,设备ID给安装INF文件提供一个映射,该安装INF文件描述了组件,注册表设置,以及与该设备相关联的类似的配置选项。根据一个实施例,与该设备有关的配置设置存储在设置文件(245)中。根据一个实施例,配置文件存储为XML,它包括应该怎么样配置该设备的指令。上下文中的配置是指对什么样的服务对象(SO)映射给哪个设备进行识别。包含在XML文件中的信息所包括的项目,例如:设备ID;设备类;IHV名;SO名;SO GUID;设备的特殊属性(端口速度,多接头设备等等);用于设备管理的启动UI的入口终端,等等。最后获知的设备状态也作为设备配置的一部分进行保存。最后获知的状态在调试设备时可能是很有用的。
根据本发明的一个实施例,模板INF文件提供给每个支持的设备类,作为软件开发包(SDK)的一部分。模板INF文件的目的在于识别标准注册表和支持公共控制库(CCL)所需的配置元素。当设备管理器应用程序230识别出未管理的设备是一个零售设备时,CCL 235公开给对零售设备210感兴趣的POS应用程序(240)。
当接收来自操作系统的事件并识别该设备时,设备管理器应用程序230自动的更新注册表和相关的配置入口,并且在无需用户介入的情况下,基于相关联的INF文件为该设备安装支持的二进制文件。然后,在无需知道使用特定设备的特殊规定的情况下,POS应用程序240可以使用CCL 235中的功能,对零售设备210进行访问。
CCL 235旨在为应用程序提供与支持的设备有关的属性,方法以及事件。根据一个实施例,CCL 235通过NET类库,为每个支持的设备类提供UPOS属性,方法以及事件以作为管理的代码,这些设备类都是在UPOS V1.8规范中指定的。也可以支持其它的设备。通常的,操作系统的PnP系统不支持的任何设备,可以通过设备管理器应用程序230在用户模式代码中得到支持。
图3示出的是根据本发明诸多方面的PnP系统的高层次结构框图。如图所示,该结构框图包括操作系统在核心模式中提供的功能(线335以下)和在用户模式中提供的功能(线335以上)。
I/O系统350在操作系统的核心模式下进行操作,并提供I/O管理(365),电源管理(360)和PnP管理(355)。I/O管理器提供标准I/O管理。电源管理360将不由操作系统管理的电源事件发送给设备管理器应用程序310。PnP管理器355检测PnP事件并将该事件提供给设备管理器应用程序310。PnP事件管理器320判断什么事件与设备管理器310所支持的设备有关,并将它已经注册准备接收的任何事件提供给POS应用程序305。
互操作层315用于使基于遗产COM的零售设备能够被指向CCL的应用程序使用。换句话说,互操作层315旨在为POS设备305提供一种使用相同方法就能够对PnP设备和遗产设备进行访问的统一的方式。遗产OPOS设备通常存储关于它们自己的信息,识别通信路径,来坚持配置数据。例如,一个系统注册表可以用来存储这样的信息。典型的,每个设备制造商将提供一个常规应用程序来管理设备的配置。
图4示出的是根据本发明诸多方面的PnP系统与CCL之间的交互。如图所示,系统400包括POS应用程序405,公共API(CCL)415,列举器420,PnP系统430,SO资料库440,配置管理器450,以及NET框架和Win32层。
POS应用程序405与API410进行交互,进而与CCL支持的设备进行交互。API410为POS应用程序405提供由应用程序所注册接收的PnP事件。
根类415在公共API 410中进行公开并作为所有操作的一个单独入口终端。API410给应用程序,如POS应用程序405,提供对已安装的POS设备进行计数的能力,为它用用具体例子说明服务对象的能力,并在POS设备连接或断开连接时接收即插即用事件的能力。
根据一个实施例,根类415公开了下面的方法:GetDevice()返回一个安装到机器上的所有POS设备的集合,GetDevice(字符串类型)返回一个安装到机器上的给定类型的POS设备的集合;GetDefaultDevice(字符串类型)为给定类型的缺省设备返回IDevice接口;以及OpenControl(IDevice设备)返回被请求的服务对象的情况(IPOSControl接口)。
根类415还为POS应用程序510公开了两个即插即用事件:OnDeviceAdded和OnDeviceRemoved,当POS设备连接到机器或从机器断开连接时会发出这两个事件。
OPOS定义了服务对象发出的5个事件:OnDataEvent;OnDirectIOEvent;OnErrorEvent;OnOutputCompleteEvent;以及OnStatusUpdateEvent。不同的设备类会发出,所有的事件,或事件的子集,或一个事件也不发出。这些事件都添加到IPOSControl接口。用于每个OPOS标准不能简单支持一些或全部事件的设备类的SO将不会发出它们。
该根类还检测机器上安装了什么样的服务对象,以及通过已经安装的SO(420)的内部列举器返回的列举器,把那样的信息呈现给应用程序。根类410通过块420与操作系统的即插即用系统430集成到一起,通过硬件ID来判断一个由已经安装的服务对象支持的物理设备当前是否处于已经连接或已经打开。
块420为NET服务对象扫描NET SO资料库440。根据一个实施例,块420对在HKLM\SOFTWARE\OLEforRetail.NET\ControlAssemblies注册表键中指定的目录进行扫描。该键可以包括多个字符串值:每个要扫描的目录有一个值。NET SO是配备给特定常规属性的dll。一个SO汇编中的每个服务对象类都带有一个PPSServiceObject属性,该属性指定了服务对象的设备类,名称,描述,以及版本。例如:
[POSServiceObjectAttribute(“MSR”,“CommonMSR”,“Common service objectformagnetic stripereader”,“1.0.0”)]
配置管理器550读取配置文件并用于:将PnP硬件ID映射到多个遗产SO;扩展到.NET SO元数据;将非PnP设备映射到多个SO;无效设备;以及安全设置。
可以有一个或多个POSHardwareId属性,将物理设备的硬件ID映射到SO。这些ID是硬件ID,也是由其即插即用子系统使用的。在通过指定较低和较高的id范围而得到的范围内,对硬件id进行定义。例如:[POSHardwareID(“USB\Vid_05d9&Pid_a794&Rev_0000”,“USB\Vid_05d9&Pid_a794&Rev_9999”)]。
根据一个实施例,为遗产服务对象,根类415读取了HKLM\SOFTWARE\OLEforRetail\ServiceOPOS注册表键。所有注册的OPOS服务对象的名称和标题性ID都在按设备类型成组的键之下进行定义。用于遗产SO的硬件id通过配置放入到指定的文件夹中的XML文件进行定义。
正如上面讨论的,根据一个实施例,CCL对POS应用程序开了UPOS V1.8的属性,方法和事件,作为用于每个在UPOS规范中定义的支持设备类的管理代码。
CCL 410是代表每个设备类的控制对象(CO)的汇编集合。该CCL为应用程序,例如售货终端应用程序,提供了应用程序接口。该服务对象执行API并且支持该CO的类的一个设备。接下来是标准属性,方法和事件的示意性列表。
名称 | 类型 | 可变性 | 备注 |
AutoDisable | 布尔型 | 读—写 | |
BinaryConversion | 整型 | 读—写 | 用于遗产支持 |
CapPowerReporting | 整型 | 只读 | |
CapStatisticsReporting | 布尔型 | 只读 | |
CapUpdateStatistics | 布尔型 | 只读 | |
CheckHealthText | 字符型 | 只读 | |
Claimed | 布尔型 | 只读 | |
DataCount | 整型 | 只读 | |
DataEventEnabled | 布尔型 | 读—写 | |
DeviceEnabled | 布尔型 | 读—写 | |
FreezeEvents | 布尔型 | 读—写 | |
OpenResult | 整型 | 只读 | 用于遗产支持 |
OutputID | 整型 | 只读 | |
PowerNotify | 整型 | 读—写 | |
PowerState | 整型 | 只读 | |
ResultCode | 整型 | 只读 | 用于遗产支持 |
ResultCodeExtended | 整型 | 只读 | 用于遗产支持 |
State | 整型 | 只读 | |
ControlObjectDescription | 字符型 | 只读 | |
ControlObjectVersion | 整型 | 只读 | |
ServiceObjectDescription | 字符型 | 只读 | |
ServiceObjectVersion | 整型 | 只读 | |
DeviceDescription | 字符型 | 只读 | |
DeviceName | 字符型 | 只读 | |
根据一个实施例,UPOS V1.8常数被执行并且遵照由UPOS建立的分类和命名惯例。根据一个实施例,该CCL以.NET代表的形式公开UPOS事件。另外,该库公开了与POS设备有关的PnP事件。
该CCL定义接口以及它们的设备类名称。该库也允许定义额外的设备类。
该IPhysicalDevice接口公开了几个关于该将要被接入或移除的指定设备的属性,如:它的安装状态,POS设备类,全部设备路径,描述以及硬件Id。这些信息将应用程序需要的上下文提供给应用程序,使得它能够作出关于如何处理该事件的决定。
public interface IPhysicalDevice
{
string Descriptio {get;}
string DevicePath {get;}
string Class {get;}
string
HardwareIds {get;}
bool Installed {get;}
}
PnP支持在该PhysicalDeviceInfo类中实现。根据一个实施例,该PhysicalDeviceInfo类是从System.Windows.Forms.Form.继承来的管理类。因为本地操作系统事件通过窗口消息公开给应用程序并且*.Form包装了一个本地窗口,因此PhysicalDeviceInfo继承于*.Form。该本地窗口用于从OS中捕获PnP事件并且不进行显示。
当指明该PhysicalDeviceInfo类是为来自所有设备接口类的PnP事件而注册的时候,这样它就公开什么时候任何支持PnP的设备添加到系统中或从系统中移除(包括非POS设备)的事件。该PhysicalDeviceInfo类还建立了一个系统目前所知道的所有设备的内部列表,并且判断哪一个是当前接入并启动的。这个类还包括帮助方法来返回设备的完全列表并且查询指定设备的当前安装状态,其中帮助方式是在设备计数期间由CCO.Net使用的。设备列举和设备信息是通过本地API调用来完成的,本地API是通过P-Invoke类型互用而成为多个Win32 API的。
该PhysicalDeviceInfo类将本地操作系统PnP事件作为事件公开给该根CCO.Net类。这些事件返回执该PhysicalDevice接口的IPhysicaldevice对象。该PhysicalDevice类是代表单个设备的类,并且公开几个关于设备的属性,如它的硬件Id,描述等等。这是最终唤起应用程序的同一个对象。
访问方法和属性
一个想要访问该库支持的设备的应用程序,如POS应用程序405,应该首先在调用其它方法之前打开该设备。根据一个实施例,在成功打开一个设备之前试图调用一个方法会导致OPOSClosedExceotion发生。访问专用设备需求的应用程序应该声明并且在调用大部分方法之前启动该设备。访问可共享设备的应用程序应该在试图调用大部分方法之前启动该设备。在成功打开一个设备之前,与该设备关联的大部分属性的值没有初始化。在已为设备初始化属性之后,随后声明和对设备的启动没有重新初始化这些属性。这些属性保持被初始化直到设备关闭。
由设备管理器应用程序接收的数据作为DataEvent进行排队。如果当接收数据时,该AutoDisable属性设为TRUE,那么该控制自动使它自已本身无效并将DeviceEnabled属性设为EALSE。这样就禁止了该控制从进一步输入中进行排队并且当可能时,物理地使该设备无效。
当该应用程序准备好从该设备接收输入时,它将DataEventEnabled属性设为TURE。然后,当输入被接收时(通常作为硬件中断的结果),该控制进行排队并将DataEvent发送给已经请求该事件的应用程序。如果已经将数据进行了排队,那么将对该DataEvent进行发送。这个事件可包括通过数字参数的输入状态信息。就在发出该事件之前,该控制把按照需要而增加了其它信息的输入数据放到设备指定属性当中。
就在发送该事件之前,通过将DataEventEnabled属性设为EALSE,该控制使进一步的数据事件无效。这会导致通过该控制对随后输入的数据进行排队,而该应用程序处理当前输入和相关属性。当该应用程序已经完成了当前输入并准备更多的数据时,它通过将DataEventEnabled设为TURE来重新启动事件。
如果该输入设备是专用设备,该应用程序在该设备开始读取输入之前进行声明并且启动该设备。对于可共享的输入设备,在该设备开始读取输入之前,可以打开一个或多个应用程序并且启动该设备。在该控制使用DataEvent向设备发送数据之前,应用程序调用ClaimDevice方法来请求对该设备进行单独访问。如果接收到事件一驱动输入,但是没有应用程序已经该设备进行声明,那么对该输入进行缓冲直到一个应用程序对该设备进行声明(并且该DataEventEnabld属性为TURE)。这样的行为允许在多个应用程序之间按顺序共享该设备,有效的在它们之间传递该输入焦点。
如果该控制在获取或处理事件一驱动输入时遇到了一个错误,那么该控制将它的改为Error,并且对一个或两个ErrorEvents进行排队来警告该应用程序有错误情况。这个事件(或多个事件)不进行了送直到该DataEventRnabled属性为TURE,以便按顺编好应用程序。根据一个实施例,对带有以下片段的错误事件和表达式进行发送:
InputwithData(OPOS_EL_INPUT_DATA)——当一个或多个DataEvents进行排队时,如果该错误发生,则对其进行排队。它排在所有DataEvents之前。一种典型的实现方式是将把它放在事件队列的最前面。这个事件使应用程序能够立即清除掉输入,或选择性的警告用户对错误注意并处理经过缓冲的输入。接下来的情形可能在扫描器控制方面有用:该用户能够立即被警告注意该错误,以至于直到该错误被解决,否则不会对更多的项目进行扫描。任何前面扫描过的项目在执行错误恢复之前能够被成功地进行处理。
InputNoData(OPOS_EL_INPUT)——当已经发生了一错误并且没有数据可用时,对其进行发送。一种典型的实现现方式是将把它放到事件队列的最后面。当该错误发生时,如果一些数据已经排在队列中,那么对一个带有“InputWithData”片段的ErrorEvent进行排队并首先发送出去,然后在所有DataEvents已经发送之后,对这个错误事件进行发送。如果发送了一个“InputWithData”事件并且该应用程序事件句柄响应了一个“清除”,那么这个“InputNoData”事件就不发送。
当发生了下面的一种情况时,该控制退出错误状态:(1)应用程序从InputNoDataErrorEvent返回;(2)应用程序从InputWithDataErrorEvent返回,并带有OPOS_ER_CLEAR;以及(3)应用程序调用ClearInput方法。
对于一些控制来说,应用程序调用一种方法以便开始事件驱动输入。在该控制接收到输入之后,接着典型地,不会再接收额外的输入,直到该方法被重新调用来重新开始输入为止。示例包括MICR和签名获取设备。驱动输入事件的这种变化有时候会被称为“异步输入”。
可以通达读取该DataCount属性来获取由该控制进行排队的DataEvents的数量。所有由一个控制进行排队的输入可以通过调用ClearInput方法来将其删除。ClearInput可以在用于可共享设备的打开(Open)之后和用于专用设备的声明设备(ClaimDevice)之后进行调用。
一般的事件驱动输入模型不排除对包括直接返回输入数据的方法或属性的设备类进行定义。一些设备类定义这些方法和属性,以便以更直观或更灵活的方式来操作。一个示例是该键锁设备。这种类型的输入有时候称为“同步输入”。
输出
该OPOS输出模型包括同步和异步输入。一个设备类可以支持一种或两种类型,或两种类型都不支持。
同步输出
当设备输出能够很快执行的时候,优选使用同步输出。它的优点是简单。该应用程序调用一个类指定方法来执行输出。该控制不进行返回,直到完成该输出为止。当在这种类型输出期间发生了错误时,发出一个OPOSException。
异步输出
在先进先出基础上执行异步输出。当设备输出需要慢速硬件交互时,优选使用这种输出类型。它的优点是能够被察觉到的响应性,这是因为应用程序能够在该设备正执行该输出时执行其它的操作。应用程序调用类指定方法来启动输出。该控制将该请求缓冲在程序存储器中,使得一旦物理设备能够接收并处理它,就将它发送到物理设备,将OutputID属性设为该需求的一个识别符,然后一旦可能就将它返回。当该设备成功的完成请求时,OPOS发出一个OutputCompleteEvent。该事件的一个参数包括了完成的请求的OutputID。
如果在执行异步请求时发生了一个错误,就发出一个ErroreEvent。该应用程序的事件句柄要么重试这个特殊的输出,要么将它清除。当ErrorEnevt在进程中时,该控制处于错误状态。
所有经过缓冲的输出数据,包括所有异步输出在内,能够通过调用ClearOutput而将它们删除。不必再为清除掉的输出发出OutputCompleteEvents。该方法还能够停止可能在进程中的任可输出(当可能的时候)。
错误处理
通过异常事件来执行库中的错误处理。该错误基于在UPOS V1.8规范中的定义的HRESULT代码(ResultCOde和ResultCodeExtended值)。该POSException类从System.ApplicationException衍生出来,并且是一个基础异常事件类。该类还为OPOS错误代码定义了常数。POSControlException从POSException衍生出来并由服务对象发出。POSLibraryException也从POSException衍生出来并由该库发出。
根据一个实施例,错误处理从System.ApplicationEXception衍生出来。这个衍生类执行出自UPOS规范的ResultCode和ResultCodeEctended属性。
安全
该CCL,即插即用特性以及设备列举特性使用基于角色的安全来对服务对象进行访问。设备管理包括对UI的需求以及对与服务对象有关的角色的相关配置的需求。
公开了一个帮助类,它列举了接入到系统上的经过连接和配置的设备。这个类公开了公共方法,该方法允许应用程序开发者通过查询CCL来判断什么设备是可以访问的。这个列举类还具备查询设备统计的能力(象UPOS V1.8规范定义的那样)。
为了简化提供给服务对象开发者和零售商的安全,添加的额外用户类型包括:所有者集成者管理者;以及出纳员。缺省地,所有者是管理员组的成员,集成者和管理者是重要用户,以及出纳员是一个用户。
在无需适当角色特权的情况下,试图访问一个空制,会发出一个“不允许访问”异常事件。一个标准错误消息被识别出来,说明已经发生了该错误。如果应用程序正在得理这个消息,应该有一个选项不显示该示准错误消息。如果使用的是基于角色的安全并且应用程序不处理这个错误的话,该标准消息就不应该受到阻止。
根据一个实施例,该CCL公开了一个由UPOS设备类成组起来的可访问POS设备的列举器。该库作为一个用于举例说明服务对象类的例子的工厂。它将POS应用程序复写器从指定服务对象的实现方式中分离出来,并且是一个与POS设备交互的应用程序的一个单独入口终端。
根据一个实施例,通过标准NET方式来报告错误(通过异常事件的方式)。库异常事件具有逻辑继承层次。在可能的地方使用标准OPOS错误代码并使它有效。
图5示出的是根据本发明诸多方面的一个带有PnP零售设备的集成遗产设备的结构图。
CCL 510对带有受管理的代理类的基于COM的SO进行包装。该代理通过反射530来示例SO的控制对象,并转接调用它的应用程序。该代理并不直接接与实际的SO(570)进行对话。而是它与它的CO(560)进行通讯。
该LegacyProxy类是用于所有遗产代理的全局基础类。该LegacyProxy类实现用于24个当前支持的OPOS设备类的接口(ICashDrawer,IMSR,IPOSPrinter等等)以至于它的示例能够发送给任何一个接口。基本地,LegacyProxy是所有OPOS控制的父集。LegacyProxy通过标准NET-COM互用层与CO对话,并使用IDispatch进行实际通讯,其中,该互用层考虑了所有垂直规则。因为IDispatch通过名称来调用属性和方法,只要下面的CO执行它们,该LegacyProxy类就能够公开这些属性和方法。
根据一个实施例,通过来自Syetem.Runtime.InteropService名空间的UCOMIConnectionPointContainer和UCOMIConnectionPoint接口,将来自遗产控制的事件挂起。如果事件接收器类(LegacyProxy)执行了遗产控制的指定事件接收器接口,通过UCOMIConnectionPoint就能够对事件句柄进行设置。尽管仅有5个标准OPOS事件,但对于所有控制对象(它们带有不同的接口向导)来说,这些事件接收器接口全都是不同的。取代为遗产控制对象的每一个例子来示例LegacyProxy类,从LegacyProxy类中衍生出—个动态存储器内类,它另个实现该事件接收器接口,且该接口产生想要的CO。该接口的GU/ID是从遗产CO例子(通过UCOMIConnectionPoint)的事件连接点取出来的。产生的类将对事件句柄延的调用转接到LegacyProxy类,该LegacyProxy类对它们进行翻译并将它们发送到应用程序。
1.应用程序为一个遗产OPOS控制调用RootOpenControl。
2.Root.OpenControl调用AssemblyFinder.OpenControl。
3.AssemblyFinder.OpenControl了解该请求是用于一个遗产控制,并调用AssemblyFinder.GenerateLegacyProxy。
4.AssemblyFinder.GenerateLegacyProxy说明该COM类,并然后使用UCOMIConnectionPointContainer和UCOMIConnectionPoint来获取该连接点(事件接收器)接口的向导。
5.AssemblyFinder.GenerateLegacyProxy调用AssemblyFinder.EmitProxy来产生一个存储器内代理类,其中该存储器内代理类实现一个带有该事件接收器向导的接口。
6.应用程序通过调用其上的打开(Open),来打开该f戈理。
7.在打开(Open)力法中,在其它的事情当中,LegacyProxy(所产生的代理的父类)调用UCOMIConnectionPoint.Advise以便将该例子设为事件句柄。
支持POS应用程序
OPOS.NET APl
该CCL由3个核心汇编组成:(1)POS.Interface.dll,它定义接口,列举和常数,通过SO和应用程序两者进行引用;(2)POS.dll,它包括POS.Root类,该类使应用程序(ISV)为已经安装的POS设备列举并示例服务对象;以及(3)GenericServiceObject.dll,它是一个用于服务对象的基础类。这将促使服务对象(IHV)的复写器从它之中衍生出来,并且杠杆调节它诸如事件序列,全局声明等等的基础SO功能的缺省实现方式。
因为会从POS机硬盘驱动器上的多个位置引用这3个汇编,那么就安装用于全局汇编高速缓存的汇编。这将有助于保证通过该机器只使用该二制进制文件的一个拷贝,并且该二进制文件能够在一个中心位置起作用。
服务对象接口
出于创建受管理的服务对象的目的,已经定义了几个接口。这些接口封装了POS1.8规范,并且被分成两个类:(1)设备类独立口,它们模仿一般的POS功能以及(2)设备相关接口,它们模仿指定到一个给定设备类的功能。
公共公开的POS接口(一般的和设备相关接口)被定义成一个分别汇编的POS.Interface.dll。这些接口由.NET服务对象来执行。应用程序将从CCL接收到的SO例子发送到这些接口,以便能够对特殊设备类的指定功能进行访问。基础控制接口被定义成POS.Interface.Basic名空间,并具备下面的层次。IPOSControl是用于.NET服务对象的基础接口。SO将直接地或间接地实现它。该库使用指向这个接口的指针,使得SO和应用程序将它发送到更多的指定设备的相关接口,象IMSR,IPOSPrinter等等。通过给用于事件驱动输入设备的SO添加3个属性,IPOEventInput对IPOSControl进行扩展。通过给用于支持异步输出的设备(如打印机)的SO添加OutputID属性,IPOSAsyncOutput对IPOSControl进行扩展。
用于标准OPOS设备类的设备相关接口被定义成POS.Intrrfaces.Specific名空间。它们从上述基出接口中的一个接口衍生出来,并且根据为特殊设备类指定的功能对它们进行扩展。当执行它们的SO时,IHV的SO应该从这些接口中衍生出来。示意性的接口如下:用于现金抽屉的ICashDrawer,用于磁条读取器的IMSR,用于票据打印机的IPOSPrinter等等。
这些接口近似地与各自的OPOS接口相匹配。与此同时,在一些由于COM限制导致的从UPOS衍生OPOS的情况中,用到了原始UPOS接口。例如,OPOS使用BSTR字符串来接收和发送二进制数据,这将引起某些由二进制向ASNI转换的冲突。在该CCL中,字节阵列用于二进制数据。
作为一个例子,下面的是,用于MSR(磁条读取器)的OPOS和库接口看起来是什么样子的。
OPOS:
interface IOPOSMSR:IDispatch
{
[hidden]HRESULT SOData([in]]ong Status);
[hidden]HRESULT SODirectIO([in]long EventNumber,[in,out]
long* pData,[in,out]BSTR* pString);
[hidden]HRESULT SOError([in]long ResultCode,[in]long
ResultCodeExtended,[in]long ErrorLocus,[in,out]long* pErrorResponse);
[hidden]HRESULT SOOutputCompleteDummy([in]long OutputID);
[hidden]HRESULT SOStatusUpdate([in]long Data);
[hidden]HRESULT SOProcessID([out,retval[long* pProcessID);
[propget]HRESULT OpenResult([out,retval]long* pOpenResult);
[propget]HRESULT CheckHealthText([out,retval]BSTR*
pCheckHealthText);
[propget]HRESULT Claimed([out,retval]VARIANT_BOOL*
pClaimed);
[propget]HRESULT DataEventEnabled([out,retval]
VARIANT_BOOL* pDataEventEnabled);
[propput]HRESULT DataEventEnabled([in]VARIANT_BOOL
DataEventEnabled);
[propget]HRESULT DeviceEnabled([out,retval]
VARIANT_BOOL* pDeviceEnabled);
[propput]HRESULT DeviceEnabled([in]VARIANT_BOOL
DeviceEnabled);
[propget]HRESULT FreezeEvents([out,retval]VARIANT_BOOL*
pFreezeEvents);
[propput]HRESULT FreezeEvents([in]VARIANT_BOOL
FreezeEvents);
[propget]HRESULT ResultCode([out,retval]long* pResultCode);
[propget]HRESULT ResultCodeExtended([out,retval]long*
pResultCodeExtended);
[propget]HRESULT State([out,retval]long* pState);
[propget]HRESULT ControlObjectDescription([out,retval]BSTR*
pControlObjectDescription);
[propget]HRESULT ControlObjectVersion([out,retval]long*
pControlObjectVersion);
[propget]HRESULT ServiceObjectDescription([out,retval]BSTR*_
pServiceObjectDescription);
[propget]HRESULT ServiceObjectVersion([out,retval]long*
pServiceObjectVersion);
[propget]HRESULT DeviceDescription([out,retval]BSTR*
pDeviceDescription);
[propget]HRESULT DeviceName([out,retval]BSTR*
pDeviceName);
HRESULT CheckHealth([in] long Level,[out,retval]long* pRC);
HRESULT ClaimDevice([in]long Timeout,[out,retval]long* pRC);
HRESULT ClearInput([out,retval]long* pRC);
HRESULT Close([out,retval]long* pRC);
HRESULT DirectIO([in]long Command,[in,out]long* pData,[in,
out]BSTR* pString,[out,retval]long* pRC);
HRESULT Open([in]BSTR DeviceName,[out,retval]long* pRC);
HRESULT ReleaseDevice([out,retval]long* pRC);
[propget]HRESULT AccountNumber([out,retval]BSTR*
pAccountNumber);
[propget]HRESULT CapISO([out,retval]VARIANT_BOOL*
pCapISO);
[propget]HRESULT CapJISOne([out,retval]VARIANT_BOOL*
pCapJISOne);
[propget]HRESULT CapJISTwo([out,retval]VARIANT_BOOL*
pCapJISTwo);
[propget]HRESULT DecodeData([out,retval]VARIANT_BOOL*
pDecodeData);
[propput]HRESULT DecodeData([in]VARIANT_BOOL
DecodeData);
[propget]HRESULT ExpirationDate([out,retval]BSTR*
pExpirationDate);
[propget]HRESULT FirstName([out,retval]BSTR* pFirstName);
[propget]HRESULT MiddleInitial([out,retval]BSTR*
pMiddleInitial);
[propget]HRESULT ParseDecodeData([out,retval]
VARIANT_BOOL* pParseDecodeData);
[propput]HRESULT ParseDecodeData([in]VARIANT_BOOL
ParseDecodeData);
[propget]HRESULT ParseDecodedData([out,retval]
VARIANT_BOOL* pParseDecodedData);
[propput]HRESULT ParseDecodedData([in]VARIANT_BOOL
ParseDecodedData);
[propget]HRESULT ServiceCode([out,retval]BSTR*
pServiceCode);
[propget]HRESULT Suffix([out,retval]BSTR* pSuffix);
[propget]HRESULT Surname([out,retval]BSTR* pSuename);
[propget]HRESULT Title([out,retval]BSTR* pTitle);
[propget]HESULT TracklData([out,retval]BSTR* pTracklData);
[propget]HRESULT Trackl DiscretionaryData([out,retval]BSTR*
pTracklDiscretionaryData);
[propget]HRESULT Track2Data([out,retval]BSTR* pTrack2Data);
[propget]HRESULT Track2DiscretionaryData([out,retval]BSTR*
pTrack2DiscretionryData);
[propget]HRESULT Track3Data([out,retval]BSTR* pTrack3Data);
[propget]HRESULT TracksToRead([out,retval]long*
pTracksToRead);
[propput]HRESULT TracksToRead([in]long TracksToRead);
[propget]HRESULT AutoDisable([out,retval]VARiANT_BOOL*
pAutoDisable);
[propput]HRESULT AutoDisable([in]VARIANT_BOOL
AutoDisable);
[propget]HRESULT BinaryConversion([out,retval]long*
pBinaryConversion);
[propput]HRESULT BinaryConversion([in]long BinaryConversion);
[propget]HRESULT DataCount([out,retval]long* pDataCount);
[propget]HRESULT ErrorReportingType([out,retval]long*
pErrorRepotingType);
[propput]HRESULT ErrorReportingType([in]long
ErrorReportingType);
[propget]HRESULT CapPowerReporting([out,retval]long*
pCapPowerReporting);
[propget]HRESULT PowerNotify([out,retval]long* pPowerNotify);
[propput]HRESULT PowerNotify([in]long PowerNotify);
[propget]HRESULT PowerState([out,retval]long* pPowerState);
[propget]HRESULT CapTransmitSentinels([out,retval]
VARIANT_BOOL* pCapTransmitSentinels);
[propget]HRESULT Track4Data([out,retval]BSTR* pTrack4Data);
[propget]HRESULT TransmitSentinels([out,retval]
VARIANT_BOOL* pTransmitSentinels);
[propput]HRESULT TransmitSentinels([in]VARIANT_BOOL
TransmitSentinels);
}
Library:
public interface IPOSControl
{
PowerReporting CapPowerReporting {get;}
bool CapStaticticsReporting {get;}
bool CapUpdateStatictics {get;}
bool Claimed {get;}
string ControlObjectDescription {get;}
int ControlObjectVersion {get;}
string DeviceDescription {get;}
bool DeviceEnabled {get;set;}
string DeviceName {get;}
bool FreezeEvents {get;set;}
bool PowerNotificationEnabled {get;set;}
PowerState PowerState {get;}
string ServiceObjectDescription {get;}
int ServiceObjectVersion {get;}
ControlState State {get;}
string DevicePath {get;set;}
string CheckHealth(HealthCheckLevel level);
void ClaimDevice(int timeout);
void ClearInput();
void ClearOutput();
void Close();
void Open();
void DirectIO(int command,ref int pData,ref object
pObject);
void ReleaseDevice();
void ResetStatistics(string parameters);
string RetrieveStatistics(string parameters);
void UpdateStatistics(string parameters);
event DataEventEventHandler OnDataEvent;
event DirectIOEventEventHandler OnDirectIOEvent;
event ErrorEventEventHandler OnErrorEvent;
event OutputCompleteEventEventHandler
OnOutputCompleteEvent;
event StatusUpdateEventEventHandler
OnStatusUpdateEvent;
}
public interface IPOSEventInput:IPOSControl
{
bool AutoDisable {get;set;}
int DataCount {get;}
bool DataEventEnabied {get;set;}
}
public interface IMSR:Base.IPO SEventInput
{
bool CapISO {get;}
bool CapJISOne {get;}
bool CapJISTwo {get;}
bool CapTransmitSentinels {get;}
string AccountNumber {get;}
bool DecodeData {get;set;}
MSRErrorReportingTypes ErrorReportingType {get;}
string ExpirationDate {get;}
string FirstName {get;}
string MiddleInitial {get;}
bool ParseDecodeData {get;set;}
string ServiceCode {get;}
string Suffix {get;}
string Sumame {get;}
string Title {get;}
Byte[] TracklData {get;}
Byte[] TracklDiscretionaryData {get;}
Byte[] Track2Data {get;}
Byte[] Track2DiscretionaryData {get;}
Byte[] Track3Data {get;}
Byte[] Track4Data {get;}
int TracksToRead {get;set;}
bool TransmitSentinels {get;set;}
}
这些接口将IPOSControl当作它的父亲/祖父,因此任何SO能够被发送到IPOSControl接口。该库类操作IPOSControl接口并且应用程序将SO的例子发送给设备指定接口。那样的话,允许在不改变库的情况下引入新的设备类。只要新设备类接口是从IPOSControl衍生出来的,该库就能够为新设备类处理SO例子。
图6示出的是根据本发明诸多方面的示意性的帮助类和SO资料库。
硬件售货机典型地实现一个设备相关服务对象(SO),该服务对象按照POS规范中说明的那样实现一个接口咬牙直接与它们的硬件对话。该CCO.NET库包括几种技术,这些技术或轻了产生SO的高质量实现的负担,它们包括:支持在管理代码中的编写服务对象;一个对于大多数服务对象来说都很普通的POS特性通用实现。这个包括用于设备声明/启动,引发事件,消息排队,统计等等的基础结构。IHV可杠杆平衡这个目标,以便减轻实现SO的POS指定方面的大部分负担,而使它们能够将专注于设备指定细节上;以及一系列用于性能计数器、设备统计的帮助类。
根据一个实施例,服务对象按照NET汇编进行编写。这些汇编是从IPOSControl接口中衍生出来的,或是从IPOSControl衍生出来的经过定义的设备指定接口中的一个中衍生出来的。这些汇编包括汇编级或类级属性,它们对支持设备的设备类,POS版本以及硬件Id进行说明。该CCO.NET库使用这些属性来判断SO要实现哪个设备类,以及它控制什么硬件。通过使用汇编属性,极大的简化了SO的安装,因为所需要做的就是把该汇编拷贝到一个目录中,在这个目录中该CCO.NET能够找到它。
该通用服务对象类是一个抽象基础类,它实现所有设备类的服务对象所需的缺省功能。典型的将要用于IHV的方案从通用服务对象和设备指定接口当中的一个接口中衍生出来。通过这么做,IHV能够依赖通用服务对象来处理很多POS指定细节,并且能够将它们的精力集中到SO的设备指定方面。
该通用服务象类包括一个缺省执行,它用于所有IPOSControl接口上的方法和属性。这包括一种机制,用于事件排队和发送,设备状态管理(声明,启动等等)以及状态报告。因为这一个抽象类,它不能被直接示例,并且试图单独地用于IHV,中衍生出来它们的SO。所有的方法和属性标注为虚拟的,所以IHV能够使用该缺省实现,并且不理会它们看着合适的任何方法。
该通用服务对象实现POS事件的细节,POS事件以事件队列、事件队列工人线程以及各种同步对象的形式进行发送。在一个高层次上,引发事件由通用服务对象按照下面的方式进行处理:
1)等待,直到该线程接收到信号并且该FreezeEvents属性是false。
2)查看该线程是否结束,如果结束了,清除该事件队列并结束该线程。
3)将下一个事件从该事件队列中去除。
4)调用PreFireEvent(EventArgs posEvent)。
5)如果步骤4中,PreFireEvent()返回的是true的话,将该事件发送给应用程序。
6)返回到步骤1。
提供两个帮助方法来驱动引发事件:
QueueEvent(EventArga posEvent)和PreFierEvent(EventArga posEVent)。
QueueEvent(EventArga posEvent)用来将一个事件添加到事件队列,以及发送信号通知该事件线程新事件已经到达。PreFireEvent(EventArgs posEvent)是在发送一个事件之前立即由该通用SO调用的。这使得优先于发送特殊事件,给该SO提供了一个更新内部状态的机会。
当调用该Open()方法时,创建该事件队列数据结构并对其初始化。该Close()方法释放该设备,结束了该事件线程并清除了内部对象。提供一系列帮助类,来帮助IHV以一种简单并相容的方式实现性能计数器和设备统计。
设备统计可以分成两个种类:(1)设备信息统计和(2)设备统计。设备信息统计是设备的属性,如名称、制造商、版本等等。设备统计典型地反映了设备使用信息,如已经供电了多少个小时等等。UPOS 1.8定义了一系列统计,其中有所有设备都应支持的统计,以及用于每个设备类的统计。UPOS还指定了设备能够支持制造商指定设备的统计。
该DeviceStatistics帮助类减轻了实现UPOS规范1.8版本中定义的设备统计的负担。它包括在该GenericSO实现中,因此从GenericSO中衍生出来的SO仅需要编写很小量的代码来支持统计。典型地,用户需要编写代码仅是调用IncrementStatistic(字符串名)方法,来在适当的时候增加给定统计的值。该GenericSO将考虑剩下的细节。
该DeviceStatistics类支持存储在硬件或软件中的统计。基于软件的统计以应用程序可定义的间隔自动持续发送到XML文件,并且当设备被声明时,自动地从这个文件中载入。DeviceStatistics实现3个方法(resetStatistics,retrieveStatistics和updateStatistics)以及两个属性(CapStatisticsReporting和CapUpdateStatistics)中的每一个。它还包括用于创建统计、增加统计以及将统计载入/保存到磁盘中的公共帮助方法。为了支持存储在设备本身当中的统计,由SO指定一个回调函数,返回统计的值。该DeviceStatistics类将在每次用户应用程序请求那个统计时,调用这个函数。
如果需要的话,IHV连同设备驱动程序,将用于安装它们的服务对象的INF文件一起提供出来。经过选择的一系列INF文件被预先安装,以至于当有新设备接入时,操作系统能够安装它们。
安装服务对象
根据一个实施例,通过将它们的汇编拷贝到由HKLM\SOFTWARE\OLEforRetail.NET\ControlAssemblies注册键指定的文件夹中,来安装NET服务对象。由于.NET SO在它们的汇编元数据中带有将它们映射到物理设备所需的信息,因此在简化情况下,不需要其它的信息。通过XML设置文件来提供所有额外的的设置。这些额外的设置的例子包括如下项目,例如:要映射到一个已存在的服务对象的额外的硬件id;当连接多于一个的类的设备时,用于这些情况的缺省服务对象;以及用于即插即用设备的设置,如串行口。
如上所述,全局设置保存在一个XML配置文件中。每个SO设置存在于置于一个预定文件夹中的单独的XML文件中。当列举己经安装的服务对象时,该库读取主配置文件和来自该文件夹的配置文件。
IHV带有用于它们的设备的inf文件,其中既要安装它们的驱动器,还要将SO汇编和可选择的XML配置文件拷贝到各自的文件夹中。ISV和管理员能够通过输XML配置文件来自定义该设置。
通过给基础类提供了一般功能的缺省实现,该CCL简化了对基于.NET的服务对象的编写。促使IHV从基础类中衍生出来,在需要的地方不理会所提供的实现,以及添加设备指定的特性。新的.NET服务对象是NET类,这些类实现由该库定义的设备类接口。
该CCL提供一个通用服务对象类它可以用作一个用于它的服务对象的基础类。该类实现尽可能多的设备独立功能,来简化SO的编写。
该CCL为函数提供了一系列帮助类,这些函数可能是多个售货机所需要的。旨在简化编写一个.NET SO。
根据一个实施例,该库支持.NET服务对象的拖放类型安装。SO汇编包括足够的元数据信息,以至于该CCL能够在无需额外配置的情况下使用它。可以定义一个额外的XML配置文件,来扩展汇编元数据。
图7示出的是根据本发明诸多方面的集成的示意性显示,用于提供关于接入到系统中的POS设备的信息。
每个设备都能够被启动或无效。可以使用下拉列表720,或一个应用程序可以调用该CCL来无效/启动该设备。一个被无效的设备不能由该CCL访问。在试图访问该设备之前,该应用程序应当启动该设备。
每个SO提供者为它们各自的设备提供这种类型的管理信息。
一个一般的标志和驱动器标志显示了下列用于设备的信息:名称和说明;硬件id和路径(用于即插即用设备);NET或遗产服务对象;汇编路径,全名,版本,类名(用于NET对象);以及ProgId,ClsId,二进制路径,版本,来自注册表的配置参数。
还为该设备显示一个设备状态(710)。
图8示出的是根据本发明诸多方面的已安装的POS设备的示意性屏幕快照。如图所示,销售设备的安装点显示在窗格810中。
窗格810显示的是安装的设备和配置的多个视图,包括如下的项目,例如:设备类以及当前连接到机器的设备;设备类以及过去曾经连接到机器上的设备;安装的NET服务对象汇编,类,以及它们控制的物理设备;安装的遗产服务对象以及它们控制的物理设备;以及全局库配置。这个接口旨在帮助管理员深入到与一个设备有关的底层细节,如:什么样的二进制实现什么样的服务对象,它们安装到哪,什么版本等等。
另一个面板(820)管理一系列用于选取树视图节点(s)的与上下文有关的控制。它显示了选取节点上的详细信息并提供有用的操作。例如,对打印机来说,可控制调用方法打开,声明,启动,打印正常,剪断票据等等。还可控制来自设备的事件的可视化。这个标志能够让管理员迅速测试接入的硬件,而无需运行一个真正的POS应用程序。
安全设置也可以进行选择。例如,全局安全设置可以公开,允许设备被锁定,以至于系统仅允许一定的服务对象和/或硬件能够被用程序利用。统计还可以提供快速地读取/重置对设备统计的访问。
以上的说明,示例以及数据提供了一个对本发明的构成进行制造和使用的完整描述。由于在不背离本发明实质和范围的情况下,可以作出很多本发明的实施例,因此,本发明包括在下文附上的权利要求中。