7

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

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

我的问题是:

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

2 回答 2

6

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

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

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

A) 是的,它可以使用 .Net 完成,但是您需要将其公开为 COM 库,一个好方法是在 CCO 中实现接口,每个设备都有一个 DLL,为您需要的设备引用一个,实现其接口,并将您的类型标记为 COM 可见,添加 GUID 和 ProgId,使用regasm “路径” /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:与方法关闭相同。

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

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

Get\Set 属性方法

这 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;
                                  .
                                  .
                                  .
                                  .
                                  .
于 2013-05-16T09:52:49.020 回答
3

To further MEYWD's answer I just wanted to post what a basic interface should look like in 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);
}

Granted this is for a MSR, but the CommonOPOS methods will be the same on all types of devices. SO the only thing you would have to change is from DispID 0x14 (20) on down. What I did was compare with the OPOS document the signature that they put and converted it to C#. I've created about 6 SO's this way and all are working just fine in a variety of different scenarios.

Another note is in the OpenService method. You'll see that the last argument is a object. That is the instance of the Control object. What you'll need to do is make another interface in your project that exposes the COM object for you. To stick with my MSR example here is what you would put.

[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);
}

I the signature in the source code for OPOS. if you search the source code you'll see a little comment like this.. (from the msr OPOS source code) that is so you know what to implement so you can fire events.

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

With those 2 basic things you'll be able to make a SO.. To fire an event is super easy too. Here is how I did it as a test

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

It has been my experience too that building a SO in C++ is harder than in C#. There are many more steps that C# just makes super simple.

Some good reads which was enough information for me to get started.

COM Interop Part1: C# Client Tutorial

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

COM Interop Part 2: C# Server Tutorial

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

COM Data Types

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

于 2014-02-13T16:29:19.707 回答