I’m trying to talk to a device using the manufacturer provided com/dlls(written in c++), however I’m encountering a problem where I am unable to set the value of a field in a struct.
In C++, this struct is implemented like so according to the provided document:
typedef struct tagFCITEST
{
FCITESTTYPE TestType;
[switch_type( FCITESTTYPE ), switch_is( TestType )]
union
{
[case(FCI_TYPE_PROPORTIONAL)] FCI_TEST_PROPORTIONAL Test;
[case(FCI_TYPE_SWITCH)] FCI_TEST_SWITCH SwitchTest;
[default];
};
} FCITEST;
however, when I “Go To Definition” in Visual studio, the following is what I find:
public struct tagFCITEST
{
public __MIDL___MIDL_itf_mftFCINTF_0209_0005 __MIDL_0022;
public tagFCITESTTTYPE TestType;
}
and
public struct __MIDL___MIDL_itf_mftFCINTF_0209_0005
{
}
apparently C# does not support these kinds of unions, and it looks like visual studio messed up when creating a wrapper, so I tried the following to address the issue:
public struct tagFCITEST
{
public MFTFCINTFLib.tagFCITESTTTYPE TestType;
public __MIDL___MIDL_itf_mftFCINTF_0209_0005 __MIDL_0022;
}
[StructLayout(LayoutKind.Explicit)]
public struct __MIDL___MIDL_itf_mftFCINTF_0209_0005
{
[FieldOffset(0)]
public MFTFCINTFLib.tagFCI_TEST_PROPORTIONAL Test;
[FieldOffset(0)]
public MFTFCINTFLib.tagFCI_TEST_SWITCH SwitchTest;
}
if I use my custom struct in method calls, the compiler is not happy, and tells me that there’s an invalid arguement. And even if I were to create a custom method signature:
[DllImport("mftFCINTF.dll")]
public static extern void DownloadTag(int ISessionID,
string szwDeviceTag,
string szwDeviceSeriaINum,
ref MFTFCINTFLib.tagFCICOMMDEF pCommDef,
ref MFTFCINTFLib.tagFCIDEVIN pInputDef,
ref MFTFCINTFLib.tagFCIDEVOUT pOutputDef,
ref MFTFCINTFLib.tagFCIDEVRELATION pRelationDef,
ref tagFCITEST pTestDef, // This is the changed line
string szwSetupInstructions,
string szwCleanupInstructions,
out string pszwLocationInCalibrator,
out MFTFCINTFLib.tagFCISTATUS pStatus);
the signature is not used. Is there a way to modify visual studio’s implementation of the wrapper? Or maybe there’s a better way to go about solving this issue?
Thanks!
Edit:
Here’s the generated .IDL file
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: mftFCINTF.dll
[
uuid(C81FC550-84AC-437B-AD0E-DA283ACE4687),
version(1.0),
helpstring("mftFCINTF 1.0 Type Library"),
custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 83951780),
custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1058814716)
]
library MFTFCINTFLib
{
// TLib : // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
// Forward declare all types defined in this typelib
interface ICalibratorInfo;
interface ICalibratorDownload;
interface ICalibratorUpload;
interface IConfigurator;
[
uuid(34E98744-678E-4D9A-B57F-6A96521BB609),
helpstring("MFT 1.0 by Meriam Process Technologies")
]
coclass mftFCI {
[restricted] interface ICalibratorInfo;
[restricted] interface ICalibratorDownload;
[restricted] interface ICalibratorUpload;
[restricted] interface IConfigurator;
};
[
odl,
uuid(FF5EFD41-7B15-11D1-B326-00001CBE02AA),
version(1.0),
helpstring("ICalibratorInfo Interface")
]
interface ICalibratorInfo : IUnknown {
HRESULT _stdcall DriverProperties([out] tagFCISTATUS* pStatus);
HRESULT _stdcall Open(
[in] int nPortNumber,
[out] long* plSessionId,
[out] long* plCapabilities,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall Close(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall Properties(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall GetId(
[in] long lSessionId,
[out] LPWSTR* pszwCalManufacturer,
[out] LPWSTR* pszwCalModel,
[out] LPWSTR* pszwCalSerialNum,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall GetCalDates(
[in] long lSessionId,
[out] _SYSTEMTIME* pLastCalDate,
[out] _SYSTEMTIME* pNextCalDueDate,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall SetDateAndTime(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall SetTempStandard(
[in] long lSessionId,
[in] int nTemperatureStd,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall GetTestResultsCount(
[in] long lSessionId,
[out] int* pnCount,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall ClearMemory(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall ValidateTag(
[in] tagFCICOMMDEF* pCommDef,
[in] tagFCIDEVIN* pInputDef,
[in] tagFCIDEVOUT* pOutputDef,
[in] tagFCIDEVRELATION* pRelationDef,
[in] tagFCITEST* pTestDef,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall GetSensorModuleId(
[in] long lSessionId,
long nSensorIndex,
[out] LPWSTR* pszwCalManufacturer,
[out] LPWSTR* pszwCalModel,
[out] LPWSTR* pszwCalSerialNum,
[out] LPWSTR* pszwCalDate,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall ClearCalFatEntry(
[in] long nIndex,
[out, retval] VARIANT_BOOL* pResult);
HRESULT _stdcall SetAppDriverMode([in] long nMode);
};
typedef enum {
FCI_OK = 0,
FCI_FAILED = 1,
FCI_COMM_ERROR = 2,
FCI_REVISION_ERROR = 3,
FCI_CANCELED = 4,
FCI_NOT_SUPPORTED = 5,
FCI_ALREADY_OPEN = 6,
FCI_WRONG_SESSION = 7,
FCI_WRONG_CALIBRATOR = 8,
FCI_ERR_IN_RANGE = 9,
FCI_ERR_IN_UNITS = 10,
FCI_ERR_IN_TYPE = 11,
FCI_ERR_IN_MANUAL = 12,
FCI_ERR_OUT_RANGE = 13,
FCI_ERR_OUT_UNITS = 14,
FCI_ERR_OUT_TYPE = 15,
FCI_ERR_OUT_MANUAL = 16,
FCI_ERR_INOUT = 17,
FCI_ERR_RELATION = 18,
FCI_ERR_PROBE = 19,
FCI_ERR_CJC = 20,
FCI_ERR_ENUM = 21,
FCI_ERR_RESOURCE = 22,
FCI_ERR_TESTPOINT = 23,
FCI_ERR_TESTTYPE = 24,
FCI_ERR_POWER = 25,
FCI_ERR_TAG = 26,
FCI_MEM_FULL = 27,
FCI_MEM_EMPTY = 28,
FCI_MEM_HAS_DATA = 29,
FCI_END_OF_UPLOAD = 30,
FCI_ERR_DOWNLOAD = 31,
FCI_ERR_TAGLENGTH = 32,
FCI_ERR_SNLENGTH = 33,
FCI_ERR_MAXPOINTS = 34,
FCI_END = 35
} tagFCISTATUS;
typedef struct tag_SYSTEMTIME {
unsigned short wYear;
unsigned short wMonth;
unsigned short wDayOfWeek;
unsigned short wDay;
unsigned short wHour;
unsigned short wMinute;
unsigned short wSecond;
unsigned short wMilliseconds;
} _SYSTEMTIME;
typedef struct tagtagFCICOMMDEF {
tagFCICOMMTYPE CommType;
__MIDL___MIDL_itf_mftFCINTF_0209_0001 __MIDL_0016;
} tagFCICOMMDEF;
typedef enum {
FCI_COMMTYPE_NONE = 1,
FCI_COMMTYPE_HART = 2
} tagFCICOMMTYPE;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0001 {
tagFCI_COMMDEF_HART HartData;
} __MIDL___MIDL_itf_mftFCINTF_0209_0001;
typedef struct tagtagFCI_COMMDEF_HART {
unsigned char cBlockId;
unsigned char cURev;
unsigned char cPollAddr;
unsigned char acUid[5];
unsigned char acTag[6];
unsigned char acDescriptor[12];
unsigned char acDate[3];
unsigned char acMessage[24];
} tagFCI_COMMDEF_HART;
typedef struct tagtagFCIDEVIN {
tagFCIBLOCKTYPE DevType;
__MIDL___MIDL_itf_mftFCINTF_0209_0002 __MIDL_0017;
} tagFCIDEVIN;
typedef enum {
FCI_BLKTYPE_GENERIC = 1,
FCI_BLKTYPE_TEMP_RTD = 2,
FCI_BLKTYPE_TEMP_TC = 3,
FCI_BLKTYPE_FREQUENCY = 4,
FCI_BLKTYPE_PRESSURE = 5,
FCI_BLKTYPE_TEMP_MEASRTD = 6,
FCI_BLKTYPE_TEMP_MEASTC = 7,
FCI_BLKTYPE_HART = 8,
FCI_BLKTYPE_SWITCH = 9,
FCI_BLKTYPE_DIFFTEMP = 10
} tagFCIBLOCKTYPE;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0002 {
tagFCI_BLKDEF_PRESSURE PressureData;
tagFCI_BLKDEF_RTD TempRtdData;
tagFCI_BLKDEF_TC TempTcData;
tagFCI_BLKDEF_RTD TempMeasRtdData;
tagFCI_BLKDEF_TC TempMeasTcData;
tagFCI_BLKDEF_FREQUENCY FrequencyData;
tagFCI_BLKDEF_HART HartData;
tagFCI_BLKDEF_DIFFTEMP DiffTempData;
tagFCI_BLKDEF_GENERIC GenericData;
} __MIDL___MIDL_itf_mftFCINTF_0209_0002;
typedef struct tagtagFCI_BLKDEF_PRESSURE {
single rURV;
single rLRV;
single rSettling;
unsigned short wUnits;
unsigned short wPressureType;
} tagFCI_BLKDEF_PRESSURE;
typedef struct tagtagFCI_BLKDEF_RTD {
single rURV;
single rLRV;
single rSettling;
unsigned short wUnits;
unsigned short wProbeType;
unsigned short wNumWires;
unsigned short wRESERVED;
} tagFCI_BLKDEF_RTD;
typedef struct tagtagFCI_BLKDEF_TC {
single rURV;
single rLRV;
single rSettling;
single rManualCJC;
unsigned short wUnits;
unsigned short wProbeType;
unsigned short wCJC;
unsigned short wProbeConnect;
} tagFCI_BLKDEF_TC;
typedef struct tagtagFCI_BLKDEF_FREQUENCY {
single rURV;
single rLRV;
single rSettling;
single rAmplitude;
unsigned short wUnits;
unsigned short wWaveForm;
} tagFCI_BLKDEF_FREQUENCY;
typedef struct tagtagFCI_BLKDEF_HART {
single rUSL;
single rLSL;
single rSettling;
unsigned short wUnits;
} tagFCI_BLKDEF_HART;
typedef struct tagtagFCI_BLKDEF_DIFFTEMP {
single rURV;
single rLRV;
single rSettling;
unsigned short wUnits;
unsigned short wDeviceVariable1;
unsigned short wDeviceVariable2;
} tagFCI_BLKDEF_DIFFTEMP;
typedef struct tagtagFCI_BLKDEF_GENERIC {
single rURV;
single rLRV;
single rSettling;
unsigned short wUnits;
} tagFCI_BLKDEF_GENERIC;
typedef struct tagtagFCIDEVOUT {
tagFCIBLOCKTYPE DevType;
__MIDL___MIDL_itf_mftFCINTF_0209_0003 __MIDL_0018;
} tagFCIDEVOUT;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0003 {
tagFCI_BLKDEF_PRESSURE PressureData;
tagFCI_BLKDEF_RTD TempMeasRtdData;
tagFCI_BLKDEF_TC TempMeasTcData;
tagFCI_BLKDEF_FREQUENCY FrequencyData;
tagFCI_BLKDEF_HART HartData;
tagFCI_BLKDEF_SWITCH Switch;
tagFCI_BLKDEF_GENERIC GenericData;
} __MIDL___MIDL_itf_mftFCINTF_0209_0003;
typedef struct tagtagFCI_BLKDEF_SWITCH {
unsigned short wContactType;
unsigned short wForm;
unsigned short wTripDirection;
single rWetContactVoltage;
} tagFCI_BLKDEF_SWITCH;
typedef struct tagtagFCIDEVRELATION {
tagFCIRELATIONTYPE RelType;
__MIDL___MIDL_itf_mftFCINTF_0209_0004 __MIDL_0020;
} tagFCIDEVRELATION;
typedef enum {
FCI_RELTYPE_LINEAR = 0,
FCI_RELTYPE_SQRT = 1,
FCI_SQRT_3RD_PWR = 2,
FCI_SQRT_5TH_PWR = 3,
FCI_RELTYPE_TABLE = 4,
FCI_RELTYPE_SWITCH = 230
} tagFCIRELATIONTYPE;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0004 {
tagFCI_RELDEF_SQRT SqrtData;
tagFCI_RELDEF_TABLE TableData;
} __MIDL___MIDL_itf_mftFCINTF_0209_0004;
typedef struct tagtagFCI_RELDEF_SQRT {
single rBreakPoint;
} tagFCI_RELDEF_SQRT;
typedef struct tagtagFCI_RELDEF_TABLE {
unsigned short wNumPoints;
unsigned short wInterpolate;
single rInput[30];
single rOutput[30];
} tagFCI_RELDEF_TABLE;
typedef struct tagtagFCITEST {
tagFCITESTTTYPE TestType;
__MIDL___MIDL_itf_mftFCINTF_0209_0005 __MIDL_0022;
} tagFCITEST;
typedef enum {
FCI_TYPE_PROPORTIONAL = 1,
FCI_TYPE_SWITCH = 2
} tagFCITESTTTYPE;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0005 {
tagFCI_TEST_PROPORTIONAL Test;
tagFCI_TEST_SWITCH SwitchTest;
} __MIDL___MIDL_itf_mftFCINTF_0209_0005;
typedef struct tagtagFCI_TEST_PROPORTIONAL {
unsigned short wRESERVED;
unsigned short wPowerSource;
unsigned short wCalSourceInput;
unsigned short wCalReadInput;
unsigned short wCalMeasureOutput;
unsigned short wNumTestPoints;
single arTestPoint[21];
single rTestPointTolerance;
single rAdjustmentLimit;
single rMaxErrorLimit;
} tagFCI_TEST_PROPORTIONAL;
typedef struct tagtagFCI_TEST_SWITCH {
single rTripSetPoint;
single rTripTolerance;
single rResetDeadband;
single rDeadbandTolerance;
single rRampTime;
long bTestReset;
} tagFCI_TEST_SWITCH;
[
odl,
uuid(FF5EFD42-7B15-11D1-B326-00001CBE02AA),
version(1.0),
helpstring("ICalibratorDownload Interface")
]
interface ICalibratorDownload : IUnknown {
HRESULT _stdcall StartDownloading(
[in] long lSessionId,
[in] LPWSTR szSessionName,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall DownloadTag(
[in] long lSessionId,
[in] LPWSTR szwDeviceTag,
[in] LPWSTR szwDeviceSerialNum,
[in] tagFCICOMMDEF* pCommDef,
[in] tagFCIDEVIN* pInputDef,
[in] tagFCIDEVOUT* pOutputDef,
[in] tagFCIDEVRELATION* pRelationDef,
[in] tagFCITEST* pTestDef,
[in] LPWSTR szwSetupInstructions,
[in] LPWSTR szwCleanupInstructions,
[out] LPWSTR* pszwLocationInCalibrator,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall FinishDownloading(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall AbortDownload(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
};
[
odl,
uuid(FF5EFD43-7B15-11D1-B326-00001CBE02AA),
version(1.0),
helpstring("ICalibratorUpload Interface")
]
interface ICalibratorUpload : IUnknown {
HRESULT _stdcall StartUploading(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall UploadNextTag(
[in] long lSessionId,
[out] LPWSTR* pszwLocationInCalibrator,
[out] LPWSTR* pszwDeviceTag,
[out] LPWSTR* pszwDeviceSerialNum,
[out] LPWSTR* pszwTechnician,
[out] LPWSTR* pszwServiceNote,
[out] int* pnTemperatureStd,
[out] tagFCICOMMDEF* pCommDef,
[out] tagFCIRESULT* pAsFound,
[out] tagFCIRESULT* pAsLeft,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall FinishUploading(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall AbortUpload(
[in] long lSessionId,
[out] tagFCISTATUS* pStatus);
HRESULT _stdcall PreviewNextTag(
[in] long lSessionId,
[out] LPWSTR* pszwLocationInCalibrator,
[out] LPWSTR* pszwDeviceTag,
[out] LPWSTR* pszwDeviceSerialNum,
[out] long* plRecordType,
[out] _SYSTEMTIME* pDate,
[out] tagFCISTATUS* pStatus);
};
typedef struct tagtagFCIRESULT {
tagFCITESTTTYPE ResultType;
__MIDL___MIDL_itf_mftFCINTF_0209_0006 __MIDL_0024;
} tagFCIRESULT;
typedef union tag__MIDL___MIDL_itf_mftFCINTF_0209_0006 {
tagFCI_RESULT_PROPORTIONAL Results;
tagFCI_RESULT_SWITCH SwitchResults;
} __MIDL___MIDL_itf_mftFCINTF_0209_0006;
typedef struct tagtagFCI_RESULT_PROPORTIONAL {
double dInputLowerRangeValue;
double dInputUpperRangeValue;
int nInputRangeUnits;
double dOutputLowerRangeValue;
double dOutputUpperRangeValue;
int nOutputRangeUnits;
int nRelationship;
int nNumberOfTestPoints;
double adInput[21];
double adOutput[21];
LPWSTR szwAuxEquipManufacturer;
LPWSTR szwAuxEquipModel;
LPWSTR szwAuxEquipSerialNum;
double dAmbientTemperature;
int nAmbientTemperatureUnits;
_SYSTEMTIME TestDate;
} tagFCI_RESULT_PROPORTIONAL;
typedef struct tagtagFCI_RESULT_SWITCH {
unsigned short wUnits;
single rTripPoint;
single rResetPoint;
unsigned short bResetTested;
_SYSTEMTIME TestDate;
LPWSTR szwAuxEquipManufacturer;
LPWSTR szwAuxEquipModel;
LPWSTR szwAuxEquipSerialNum;
} tagFCI_RESULT_SWITCH;
[
odl,
uuid(084ABD42-30CE-49AD-B1FA-9C869AAD2A27),
version(1.0),
helpstring("IConfigurator Interface")
]
interface IConfigurator : IUnknown {
[helpstring("method UploadConfiguration")]
HRESULT _stdcall UploadConfiguration(
long* pDataAvailabe,
BSTR bstrFilePath,
[in, out] BSTR* pbstrTag,
[in, out] long* plDof,
[in, out] long* plAsFoundAsLeft,
[in, out] long* pnLocation,
[in, out] long* pnMultivar,
[in, out] BSTR* pbstrDeviceSerialNum,
[out, retval] VARIANT_BOOL* pResult);
[helpstring("method DownloadConfiguration")]
HRESULT _stdcall DownloadConfiguration(
[in] BSTR bstrFilePath,
[in, out] long* pnLocation,
[out, retval] VARIANT_BOOL* pResult);
[helpstring("method ClearConfiguratorMemory")]
HRESULT _stdcall ClearConfiguratorMemory();
[helpstring("method GetConfigCount")]
HRESULT _stdcall GetConfigCount([out, retval] long* lCount);
[helpstring("method PreviewConfiguration")]
HRESULT _stdcall PreviewConfiguration(
long* pDataAvailabe,
[in, out] BSTR* pbstrTag,
[in, out] long* plDof,
[in, out] long* plAsFoundAsLeft,
[in, out] long* plOrigin,
[in, out] long* pnLocation,
[in, out] BSTR* pbstrDeviceSerialNum,
[out] _SYSTEMTIME* pDate,
[out, retval] VARIANT_BOOL* pResult);
[helpstring("method SetPortNumber")]
HRESULT _stdcall SetPortNumber(long nPort);
[helpstring("method ClearConfigFatEntry")]
HRESULT _stdcall ClearConfigFatEntry(
long nIndex,
[out, retval] VARIANT_BOOL* pResult);
};
};
You no doubt got these declarations from importing the type library of the COM server. Either by adding a reference to the COM dll or by running Tlbimp.exe by hand.
Yes, that’s a problem, type libraries only support a sub-set of what can be expressed in IDL. And these discriminated unions are certainly not part of it. They are only important for getting the structure to marshal correctly across an apartment boundary, only the proxy/stub DLLs need to see them right and that’s auto-generated code.
Two basic ways to tackle the problem, neither is particularly pleasant:
You can edit the interop library that was generated from the type library and fix the structure declaration. That starts by decompiling the DLL with ildasm.exe, editing the IL and putting it back together with ilasm.exe. You can use a sample C# program that you decompile to see what the IL should look like. This works but it is important that the COM component is stable so you don’t have to do this over and over again. There’s an How-To article in MSDN that describes the procedure.
Write a wrapper for the component in the C++/CLI language. You can #include the .h file that was generated by midl.exe from the IDL. Clearly that will only work if you have access to the IDL or have the .h file, you’ll need to contact the vendor or author if you don’t have it. One advantage of doing it this way is that you’ll also circumvent other type problems. A disadvantage is that you’ll need to know the basics of the language and of COM client programming in C++.