views:

243

answers:

1

We're trying implement a .NET Service Object that supports a COM interface to emulate a POSPrinter but still be compatible with the older technologies.

We have our interface and class object in the following class..

using [...]

namespace yRPOSPrinterDotNet
{
    [Guid("2D570F11-4BD8-40e7-BF14-38772063AAF0")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface yRPosPrinterCOM
    {

        long Open(String DeviceName);
        long PrintNormal(long Station, String Data);

    }

    [Guid("478176F4-5105-435c-8EBC-D4CB90B7B1C7")]
    [ClassInterface(ClassInterfaceType.None)]
    //[ProgId("yRPOSPrinterDotNet.POSPrinter")] //will be set automatically as the progid <namespace><clsid>
    public class POSPrinter : yRPosPrinterCOM
    {

        #region yRPosPrinterCOM Members
        public long Open()
        {
            return 0;
        }

        public long Open(String DeviceName)
        {
            Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
            FileStream objStream = new FileStream("C:\\yRPOSLog.txt", FileMode.OpenOrCreate);
            TextWriterTraceListener objTraceListener = new TextWriterTraceListener(objStream);
            Trace.Listeners.Add(objTraceListener);
            Trace.AutoFlush = true;
            Trace.Indent();
            Trace.WriteLine("Entering Main");
            Debug.WriteLine("How does this one do??");
            Console.WriteLine("Hello World.");
            Trace.WriteLine("Exiting Main");
            Trace.Unindent();
            return 0;
        }


        public long PrintNormal(long Station, string Data)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

}

And put inside HKEY_LOCAL_MACHINE\SOFTWARE\OLEforRetail\ServiceOPOS\POSPrinter\yReceipts our ProgID of yRPosPrinterDotNet.POSPrinter

HKEY_CLASSES_ROOT\CLSID\{478176F4-5105-435C-8EBC-D4CB90B7B1C7} has our ProgID correctly after building (yRPosPrinterDotNet.POSPrinter)

We can call the DLL through a test class as follow (finding the ProgID) using this class

using [...]

namespace TestYRPosPrinterDotNet
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class UnitTest1
    {
        public UnitTest1()
        {

        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestMethod]
        public void TestMethod1()
        {
            String sProgID = "yRPosPrinterDotNet.POSPrinter";
            // We get the type using just the ProgID
            Type oType = Type.GetTypeFromProgID(sProgID);
            if (oType != null)
            {
               POSPrinter pp = (POSPrinter)Activator.CreateInstance(oType);
               long retVal = pp.Open("Nothing");
            }

        }
    }
}

But when we try calling through sample TestApp (it does show up as a serviceObject)

{"Method Open threw an exception.  The service object does not support one or more of the methods required by its release."} System.Exception {Microsoft.PointOfService.PosControlException}

Through a Sample C++ Control Object we receive a 104 defined in opos.h as (const LONG OPOS_E_NOSERVICE = 4 + OPOSERR;) with the following stack trace

----------------------------------- Doesn't work --------------------------------------------------------

 POSPrinterExample.exe!COleDispatchDriver::InvokeHelperV(long dwDispID=37, unsigned short wFlags=1, unsigned short vtRet=3, void * pvRet=0x0012f1e0, const unsigned char * pbParamInfo=0x00702014, char * argList=0x0012f110)  Line 

397 C++
 POSPrinterExample.exe!COleControlSite::InvokeHelperV(long dwDispID=37, unsigned short wFlags=1, unsigned short vtRet=3, void * pvRet=0x0012f1e0, const unsigned char * pbParamInfo=0x00702014, char * argList=0x0012f10c)  Line 1093 

C++
  POSPrinterExample.exe!CWnd::InvokeHelper(long dwDispID=37, unsigned short wFlags=1, unsigned short vtRet=3, void * pvRet=0x0012f1e0, const unsigned char * pbParamInfo=0x00702014, ...)  Line 382 C++
  POSPrinterExample.exe!COPOSPOSPrinter::Open(const char * DeviceName=0x00271f00)  Line 192 + 0x1c bytes C++
+1  A: 

This seems to be a DispInterface. That's a horrible COM hack to support Visual Basic. Basically, all functions are numbered. On the first call, all function numbers are looked up and cached. It seems you are missing the Close() function, for instance. This would cause it to fail in lookup, in the way you see.

Quoting from "OLE for Retail POS Control Guide - Rel. 1.1" :

  1. Look up the dispatch IDs for all of the Service Object methods by calling the Service Object instance’s m_lpDispatch  GetIDsOfNames function. Update the generated Service Object methods to pass these dispatch IDs to the InvokeHelper member function. If any of the required dispatch IDs are not found in the Service Object, then close the dispatch interface and return OPOS_E_NOSERVICE.
MSalters