实现OPOS打印机服务对象以捕获打印文本

时间:2013-03-26 08:29:29

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

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

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

我的问题是:

  • 在这些传统POS应用程序中,甚至可以将我们的POS用于.Net解决方案吗? (如果是,怎么样?)
  • 如何构建OPOS服务对象?它可以使用.Net框架(例如C#)吗?
  • 我们做得对吗?是否有更好的方法来捕获收据(特别是对于这些遗留应用程序)?

2 个答案:

答案 0 :(得分:6)

问:在这些传统的POS应用程序中,甚至可以将我们的POS用于.Net解决方案吗? (如果是,怎么样?)

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

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

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

  1. COFreezeEvents :与属性FreezeEvents相同。
  2. GetPropertyNumber :用于通过属性的索引获取numeric \ Boolean属性的值,稍后将详细介绍。
  3. SetPropertyNumber :用于通过属性的索引设置numeric \ Boolean属性的值,稍后会详细介绍。
  4. GetPropertyString :用于通过属性的索引获取字符串属性的值,稍后将详细介绍。
  5. SetPropertyString :用于通过属性的索引设置字符串属性的值,稍后将详细介绍。
  6. OpenService :与方法open相同。
  7. CloseService :与close方法相同。
  8. 在实现这些方法之后,一切正常,这很奇怪,因为在CCO接口中没有引用,但正如我所说的那些在UPOS规范中引用并且有完整的描述。

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

    获取\设置属性方法

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

    要以正确的方式实现这些,应该查看OPOS安装下的包含目录 - 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;
                                      .
                                      .
                                      .
                                      .
                                      .
    

答案 1 :(得分:3)

为了进一步了解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)on down。我所做的是与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 );

有了这两个基本的东西,你就可以制作一个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 Interop Part1:C#客户端教程

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

COM Interop第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