C# 实现用于捕获打印文本的OPOS打印机服务对象

C# 实现用于捕获打印文本的OPOS打印机服务对象,c#,com,opos,pos-for-.net,C#,Com,Opos,Pos For .net,我们正在尝试为POS应用程序创建一个类似打印机驱动程序包装的东西,它允许我们捕获打印的收据,然后再次将其转发到原始打印机 到目前为止,我们在“POS for.Net”之上实现了一个作为服务对象的应用程序,它工作得非常好,而且一切都很好,但事实证明,一些旧式POS应用程序只支持OPO。为了支持它们,我们要么将“POS for.Net”服务对象作为OPOS服务对象提供,要么使用CCOs编写自己的OPOS服务对象 我的问题是: 在这些传统POS应用程序中,是否可以使用我们的POS for.Net解决

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

到目前为止,我们在“POS for.Net”之上实现了一个作为服务对象的应用程序,它工作得非常好,而且一切都很好,但事实证明,一些旧式POS应用程序只支持OPO。为了支持它们,我们要么将“POS for.Net”服务对象作为OPOS服务对象提供,要么使用CCOs编写自己的OPOS服务对象

我的问题是:

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

A) 不,这些应用程序不使用POS for.Net库,也不使用POS for.Net注册表项搜索服务对象,这些应用程序仅使用OPOS(OLE POS)注册表项搜索已注册的服务对象,并且通常调用CCO,后者反过来调用服务对象

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

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

  • 协同冻结事件:与属性冻结事件相同
  • GetPropertyNumber:用于通过属性的索引获取数值\布尔属性的值,稍后将详细介绍
  • SetPropertyNumber:用于通过属性的索引设置数值\布尔属性的值,稍后将详细介绍
  • GetPropertyString:用于通过属性的索引获取字符串属性的值,稍后将详细介绍
  • SetPropertyString:用于通过属性的索引设置字符串属性的值,稍后将详细介绍
  • OpenService:与open方法相同
  • 关闭服务:与关闭方法相同
  • 在实现了这些方法之后,一切都很好,这很奇怪,因为CCO接口中没有引用任何方法,但是正如我所说的,UPOS规范中引用了这些方法,并且有完整的描述

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

    Get\Set属性方法

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

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

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

        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;
                                      .
                                      .
                                      .
                                      .
                                      .
    

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

    虽然这是针对MSR的,但CommonOPOS方法在所有类型的设备上都是相同的。因此,您唯一需要更改的是从DispID 0x14(20)开始。我所做的是与OPOS文件进行比较,他们将其签名并将其转换为C。我已经创建了大约6个这样的场景,在各种不同的场景中都可以正常工作

    另一个注意事项是OpenService方法。您将看到最后一个参数是一个对象。这是控件对象的实例。您需要做的是在项目中创建另一个接口,为您公开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:\ProgramFiles(x86)\Opos\oposrc\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 );
    
    有了这两件基本的事情,你就可以做出一个如此。。发起一项活动也非常容易。下面是我如何做的测试

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