【问题标题】:Implementing an OPOS printer service object for capturing printed text实现用于捕获打印文本的 OPOS 打印机服务对象
【发布时间】:2013-03-15 23:25:18
【问题描述】:

我们正在尝试为 POS 应用程序创建类似于打印机驱动程序包装的东西,这将允许我们捕获打印的收据,然后将其再次转发到原始打印机。

到目前为止,我们在“POS for .Net”之上实现了一个作为服务对象,它运行良好且一切正常,但事实证明,一些旧的 POS 应用程序只支持 OPOS。为了支持它们,我们要么必须将“POS for .Net”服务对象作为 OPOS 服务对象提供,要么必须使用 CCO 编写自己的 OPOS 服务对象。

我的问题是:

  • 在这些传统的 POS 应用程序中,是否可以使用我们的 POS for .Net 解决方案? (如果是,怎么做?)
  • 如何构建OPOS服务对象?它可以使用 .Net 框架(例如 C#)吗?
  • 我们做对了吗?有没有更好的方法来获取收据(尤其是对于这些旧版应用)?

【问题讨论】:

  • 你让程序工作了吗?我正在寻找完全相同的东西。你能分享你的代码吗?非常感谢您!
  • 由 OMG(开放管理组)标准开发组织发布的统一 POS 零售外围架构文档描述了 OPOS 架构。还有描述 POS .Net 实现和 JavaPOS 实现架构的附录。见omg.org/spec/UPOS
  • 从 2018 年开始的 OMG Unified POS 规范 1.15 Beta 版:omg.org/spec/UPOS/1.15/Beta1/PDF

标签: c# com opos pos-for-.net


【解决方案1】:

Q) 在这些旧版 POS 应用程序中,是否可以使用我们的 POS for .Net 解决方案? (如果是,如何?)

A) 不,这些应用程序不使用 .Net 库的 POS,也不使用 .Net 注册表项的 POS 搜索服务对象,这些应用程序仅使用 OPOS(OLE POS) 注册表项进行搜索用于注册的服务对象,通常调用 CCO,CCO 依次调用服务对象。

Q) 如何构建OPOS服务对象?它可以使用 .Net 框架(例如 C#)吗?

A) 是的,它可以使用 .Net 来完成,但是您需要将其公开为 COM 库,一个好方法是在 CCO 中实现接口,每个设备都有一个 DLL,请参考您需要的设备,实现其接口,并将您的类型标记为 COM 可见,添加 GUID 和 ProgId,使用 regasm “path” /register /codebase /tlb 命令,添加所需的注册表项——可以在 UPOS 规范\开发指南文档中找到——你就完成了,或者至少我是这么想的,这样你会得到一个错误,指出你的方法中缺少方法正确运行它所需的服务对象,好吧,我发现这很难,但是接口中没有引用 7 个方法 - 尽管在 UPOS 规范\开发指南文档中引用 - 这些方法是:

  1. COFreezeEvents:与属性 FreezeEvents 相同。
  2. GetPropertyNumber:用于通过属性的索引获取数字\布尔属性的值,稍后会详细介绍。
  3. SetPropertyNumber:用于通过属性的索引设置数字\布尔属性的值,稍后会详细介绍。
  4. GetPropertyString:用于通过属性的索引获取字符串属性的值,稍后会详细介绍。
  5. SetPropertyString:用于通过属性的索引设置字符串属性的值,稍后会详细介绍。
  6. OpenService:同方法open。
  7. CloseService:与close方法相同。

在实现这些方法后一切正常,这很奇怪,因为在 CCO 接口中没有引用任何方法,但是正如我所说,这些方法中的每一个都在 UPOS 规范中引用并有完整的描述。

OpenService 和 CloseService 方法存在的原因似乎是,当 CCO 库作为 com 实现时,Open 和 Close 方法名称不合适,必须更改为 OpenService 和 CloseService,同样适用于 Claim 和 Release使用新名称 ClaimDevice 和 Release Device - 但是这些在接口中正确公开,至于其他方法我找不到原因。

获取\设置属性方法

这 4 个方法用于访问对象中的所有属性,为什么?我不确定,但似乎应该从 Dispatch 接口使用这些来访问您的对象,为什么默认情况下该接口不可用? C++ 服务对象是否以相同的方式实现?我没有答案。

要以正确的方式实现这些,应查看 OPOS 安装下的 Include 目录 - CCO 安装 - 并检查 *.hi 文件,主要是 Opos.hi 和 OposPtr.hi(取决于设备,在我们的例子中是打印机),您将看到这些包括 CCO 的常量,如成功或失败枚举,以及这 4 种方法的属性索引和设备索引偏移量。

使用 OPOS 常量中的数字,您只需打开 PropIndex 参数值,并获取\设置正确的属性值。

    if (PropertyIndexHelper.IsStringPidx(PropIndex))
    {
        switch (PropIndex)
        {
            case PropertyIndexHelper.PIDX_CheckHealthText:
                return _physicalPrinter.CheckHealthText;

            case PropertyIndexHelper.PIDX_DeviceDescription:
                return _physicalPrinter.DeviceDescription;

            case PropertyIndexHelper.PIDX_DeviceName:
                return _physicalPrinter.DeviceName;
                                  .
                                  .
                                  .
                                  .
                                  .

【讨论】:

  • 我的经验是,从 ocx 甚至从 OPOS for .NET 库实现接口实际上只是 CO 的接口,而不是 SO。对于 SO,我不得不更深入地挖掘。我最终制作了自己的接口来实现。我敢打赌,如果您要对每种方法进行某种类型的登录,您会看到其中一些方法不符合要求。最后,不推荐使用 Open 和 Close(就 SO 而言),应将其替换为 OpenService 和 CloseService。
  • 要回答您关于 C++ SO 以相同方式实现的问题,答案是肯定的。
【解决方案2】:

为了进一步回答 MEYWD 的问题,我只想发布 C# 中的基本界面应该是什么样子。

[ComVisible(true), Guid("Put a GUID here")]
public interface IMSR
{
    //Common Opos
    [DispId(0x01)]
    int CheckHealth([In] int lLevel);
    [DispId(0x02)]
    int ClaimDevice([In] int lTimeOut);
    [DispId(0x03)]
    int ClearInput();
    [DispId(0x04)]
    int ClearInputProperties();
    [DispId(0x05)]
    int ClearOutput();
    [DispId(0x06)]
    int CloseService();
    [DispId(0x07)]
    int COFreezeEvents([In, MarshalAs(UnmanagedType.VariantBool)] bool Freeze);
    [DispId(0x08)]
    int CompareFirmwareVersion([In, MarshalAs(UnmanagedType.BStr)] string FirmwareFileName, [In, Out]ref int pResult);
    [DispId(0x09)]
    int DirectIO([In] int lCommand, [In, Out] ref int pData, [In, Out, MarshalAs(UnmanagedType.BStr)] ref string pString);
    [DispId(0x0A)]
    int OpenService([In, MarshalAs(UnmanagedType.BStr)] string lpclDevClass, [In, MarshalAs(UnmanagedType.BStr)] string lpclDevName, [In, MarshalAs(UnmanagedType.IDispatch)] object lpDispatch);
    [DispId(0x0B)]
    int ReleaseDevice();
    [DispId(0x0C)]
    int ResetStatistics([In, MarshalAs(UnmanagedType.BStr)] string StatisticsBuffer);
    [DispId(0x0D)]
    int RetrieveStatistics([In, Out, MarshalAs(UnmanagedType.BStr)] ref string pStatisticsBuffer);
    [DispId(0x0E)]
    int UpdateFirmware([In, MarshalAs(UnmanagedType.BStr)] string FirmwareFileName);
    [DispId(0x0F)]
    int UpdateStatistics([In, MarshalAs(UnmanagedType.BStr)] string StatisticsBuffer);
    [DispId(0x10)]
    int GetPropertyNumber([In] int lPropIndex);
    [DispId(0x11)]
    string GetPropertyString([In] int lPropIndex);
    [DispId(0x12)]
    void SetPropertyNumber([In] int lPropIndex, [In] int nNewValue);
    [DispId(0x13)]
    void SetPropertyString([In] int lPropIndex, [In, MarshalAs(UnmanagedType.BStr)] string StringData);
    //MSR Specific
    [DispId(0x14)]
    int AuthenticateDevice([In, MarshalAs(UnmanagedType.BStr)] string deviceResponse);
    [DispId(0x15)]
    int DeauthenticateDevice([In, MarshalAs(UnmanagedType.BStr)] string deviceResponse);
    [DispId(0x16)]
    int RetrieveCardProperty([In, MarshalAs(UnmanagedType.BStr)] string propertyName, [Out, MarshalAs(UnmanagedType.BStr)] out string cardProperty);
    [DispId(0x17)]
    int RetrieveDeviceAuthenticationData([In, Out, MarshalAs(UnmanagedType.BStr)] ref string challenge);
    [DispId(0x18)]
    int UpdateKey([In, MarshalAs(UnmanagedType.BStr)]string key,[In, MarshalAs(UnmanagedType.BStr)] string keyName);
    [DispId(0x19)]
    int WriteTracks([In] object data,[In] int timeout);
}

这是针对 MSR 的,但 CommonOPOS 方法在所有类型的设备上都是相同的。所以你唯一需要改变的是从 DispID 0x14 (20) 开始。我所做的是与 OPOS 文档比较他们放置的签名并将其转换为 C#。我以这种方式创建了大约 6 个 SO,并且在各种不同的场景中都可以正常工作。

另一个注意事项是在 OpenService 方法中。你会看到最后一个参数是一个对象。那是 Control 对象的实例。您需要做的是在您的项目中创建另一个接口,为您公开 COM 对象。为了坚持我的 MSR 示例,您将在这里提出。

[ComImport, Guid("CCB91121-B81E-11D2-AB74-0040054C3719"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface COPOSMSR
{
    void SOData([In] int Status);
    void SODirectIO([In] int EventNumber, [In, Out] ref int pData, [In, Out, MarshalAs(UnmanagedType.BStr)] ref string pStrIng);
    void SOError([In] int ResultCode, [In] int ResultCodeExtended, [In] int ErrorLocus, [In, Out] ref int pErrorResponse);
    void SOOutputCompleteDummy([In] int OutputID);
    void SOStatusUpdate([In] int Data);
    void SOProcessID([Out] out int pProcessID);
}

OPOS 源代码中的签名。如果您搜索源代码,您会看到这样的小注释..(来自 msr OPOS 源代码),这样您就知道要实现什么,以便触发事件。

c:\Program Files(x86)\Opos\oposSrc\zMSR\MSR.idl

[
    object,
    uuid(CCB91121-B81E-11D2-AB74-0040054C3719),
    dual,
    helpstring("IOPOSMSR 1.5 Interface"),
    pointer_default(unique)
]
interface IOPOSMSR_1_5 : IDispatch
{
// Methods for use only by the Service Object
    [id(1), hidden, helpstring("method SOData")] HRESULT SOData( [in] long Status );
    [id(2), hidden, helpstring("method SODirectIO")] HRESULT SODirectIO( [in] long EventNumber, [in, out] long* pData, [in, out] BSTR* pString );
    [id(3), hidden, helpstring("method SOError")] HRESULT SOError( [in] long ResultCode, [in] long ResultCodeExtended, [in] long ErrorLocus, [in, out] long* pErrorResponse );
    [id(4), hidden, helpstring("method SOOutputCompleteDummy")] HRESULT SOOutputCompleteDummy( [in] long OutputID );
    [id(5), hidden, helpstring("method SOStatusUpdate")] HRESULT SOStatusUpdate( [in] long Data );
    [id(9), hidden, helpstring("method SOProcessID")] HRESULT SOProcessID( [out, retval] long* pProcessID );

有了这 2 个基本的东西,您就可以制作一个 SO.. 触发一个事件也非常容易。这是我作为测试的方法

    public int OpenService(string lpclDevClass, string lpclDevName, object lpDispatch)
    {
        controlObject = (COPOSMSR)lpDispatch;
        controlObject.SOData(1)//I just fired a Data Event
    }

根据我的经验,在 C++ 中构建 SO 比在 C# 中更难。 C# 让更多的步骤变得超级简单。

一些不错的读物,足以让我开始学习。

COM 互操作第 1 部分:C# 客户端教程

http://msdn.microsoft.com/en-us/library/aa645736(v=vs.71).aspx

COM 互操作第 2 部分:C# 服务器教程

http://msdn.microsoft.com/en-us/library/aa645738(v=vs.71).aspx

COM 数据类型

https://msdn.microsoft.com/en-us/library/sak564ww%28v=vs.100%29.aspx

【讨论】:

猜你喜欢
  • 2021-04-05
  • 2013-04-08
  • 2022-09-26
  • 1970-01-01
  • 2011-11-03
  • 2012-08-30
  • 2013-01-25
  • 1970-01-01
  • 2018-02-28
相关资源
最近更新 更多