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