Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C# PInvoke Interop
Help with passing structure from unmanaged c++ dll to c# ?
 
I am struggling with passing a structure from an unmanaged third party c++ dll back into my c# application. The dll is for a spectrometer. All of the other commands to the spectrometer work fine except this one. I am pasting more documentation below, but here is the c++ call:
 
SDK_API MTSSE_GetDeviceSpectrometerFrameData( int DeviceID, int SpectrometerID, int WaitUntilDone, tFrameRecord* &FrameData);
 
and it is the last argument that is the problem. The documentation says this:
 

FrameRecordData - the pointer to the spectrum data structure if spectrum is ready or nil if spectrum is not ready. It is defined as:
typedef struct
{
  double* CCDData;
  double* CalibData;
  double* AbsIntenData
}tFrameRecord;
And each item again should points to a double data array which defined as double data[3648].
 

First I tried this (in the namespace):
    public struct MTFrameRecordData
    {
        public double[] CCDData;
        public double[] CalibData;
        public double[] AbsIntenData;
    }
With this in a class called myUSBCam, that holds the wrappers:
        //MTSSE_GetDeviceSpectrometerFrameData(int DeviceID, int SpectrometerID, int      WaitUntilDone, tFrameRecord* &FrameData );
        public int MTGetDeviceSpectrometerFrameData(int DeviceID, int SpectrometerID, int WaitUntilDone, MTFrameRecordData FrameData)
        {
            int numOut = MT_GetDeviceSpectrometerFrameData(DeviceID, SpectrometerID, WaitUntilDone, FrameData);
 
            if (numOut < 0)
            {
                MessageBox.Show("Error trying to SetDeviceExposureTime. No cameras found on USB 2.0 bus.", _camError, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
 
            return numOut;
        }
 
     //...

        //MTSSE_GetDeviceSpectrometerFrameData(int DeviceID, int SpectrometerID, int WaitUntilDone, tFrameRecord* &FrameData );
        [DllImport("MT_Spectrometer_SDK.dll", EntryPoint = "MTSSE_GetDeviceSpectrometerFrameData", CallingConvention = CallingConvention.StdCall)]
        private static extern int MT_GetDeviceSpectrometerFrameData(int DeviceID, int SpectrometerID, int WaitUntilDone, MTFrameRecordData FrameData);
 
And then I call this in my main application (nInit is equal to 1):
            int numOut5 = 0;
            while (numOut5 <= 0)
            {
                System.Threading.Thread.Sleep(20);
                numOut5 = myUSBCam.MTGetDeviceSpectrometerFrameData(nInit, 1, 1, frameRecordData);
             }       
 
With this I get the error message:
 

An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in USBCam.exe
 

Additional information: Cannot marshal 'parameter #4': Invalid managed/unmanaged type combination.

 
I also tried this for the declaration of the structure (again in the name space):
 

    [StructLayout(LayoutKind.Explicit)]
    public struct MTFrameRecordData
    {
        [FieldOffset(0)]
        public double[] CCDData;
        [FieldOffset(29184)]
        public double[] CalibData;
        [FieldOffset(58368)]
        public double[] AbsIntenData;
    }
 
This produces the error messsage
 
An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in USBCam.exe
Additional information: Internal limitation: method signature is too complex or too large.
 
Can anyone help me with how to pass the structure from the unmanaged c++ dll into my c# application?
 

Begin more detailed description from the documentation:
 
SDK_API MTSSE_GetDeviceSpectrometerFrameData( int DeviceID, int SpectrometerID, int WaitUntilDone, tFrameRecord* &FrameData);
After sending the above MTSSE_StartGrabSpectrum() command, user can invoke this function to check whether the spectrum is ready and to get the spectrum.
Arguments: DeviceID -- the index of the device, Please refer to the notes of MTSSE_InitDevice() function.
SpectrometerID – the index of the spectrometer, should always be set to 1.
FrameRecordData - the pointer to the spectrum data structure if spectrum is ready or nil if spectrum is not ready. It is defined as:
typedef struct
{
double* CCDData;
double* CalibData;
double* AbsIntenData
}tFrameRecord;
And each item again should points to a double data array which defined as double data[3648].
CCDData is the raw pixel data,
CalibData is the CCDDataa with ETC calibration (if etc flag was turned on), if ETC is off, it should equal the CCDData.
AbsIntenData returns the absolute intensity data if AIC coefficients exists
WaitUntilDone – the frame grabbing process may need considerable time (depends on the exposure time setting and average frame number setting). User might set:
1: The API will be blocked until the spectrum is grabbed, the returned spectrum is pointed by the FrameRecordData, If the returned pointer is nil, “Time out” error occurs.
0: The API will not be blocked, if the spectrum is ready, the spectrum will be returned in FrameRecordData), otherwise, it will return nil.
Return: -1, if the grabbing command is not finished (when WaitUntilDone is “0”) or “Time out” error occurs (when WaitUntilDone is “1”).
1, if function succeeds.
Posted 8-Aug-12 5:59am
AS012341.7K
Comments
Wes Aday at 8-Aug-12 12:08pm
   
Not an answer but thanks for posting a question that contains enough information to be solved. Quite a refreshing change from the usual drek we get.
AS01234 at 8-Aug-12 12:17pm
   
I couldn't tell from the listing: Did it properly go to the c# discussion board?
 
Wes Aday at 8-Aug-12 12:18pm
   
Looks fine to me.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

The problem is that in the C++ structure there are pointers, you are trying to convert those to arrays and that is definitely a no go.
You will have to use an intptr or something like that.
I found something that should get you in the right direction here :
http://blogs.msdn.com/b/jaredpar/archive/2008/11/05/dereference-a-double-intptr.aspx[^]
  Permalink  
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

The reference from Philip Stuyck was very helpful, but I want to also acknowledge receiving help from the company that makes the spectrometers (mightexsystems.com).
 
I still don't like the solution, because now it uses unsafe code in the wrapper, essentially c++, to de-reference the double-pointer. Can anyone suggest pure safe c# code for this?
 
Here is the solution in all of it's glory. This parallels the description of my initial attempt above, but now it works:
 
In the namespace:
 
    public struct MTFrameRecordData
    {
        public IntPtr CCDData;
        public IntPtr CalibData;
        public IntPtr AbsIntenData;
    }
 
In the spectrometer class:
 
        public double[] RawDataArray;
        public double[] CaliDataArray;
        public double[] AbsIntenArray;
 
Also in the spectrometer class, the wrapper:
 
        // Note that the wrapper does not have the MTFrameRecordData
        // structure as an argument. The return is passed directly into the
        // public spectrometer class double[] arrays: RawDataArray,
        // CaliDataArray, AbsIntenArray.
        public int MTGetDeviceSpectrometerFrameData(int DeviceID,
             int SpectrometerID, int WaitUntilDone)
        {
            IntPtr ptrFrameData = IntPtr.Zero;
            int numOut = MT_GetDeviceSpectrometerFrameData(DeviceID,
              SpectrometerID, WaitUntilDone, ref ptrFrameData);
 
            if (numOut < 0)
            {
               MessageBox.Show("Error in GetFrameData.", "Spectrometer error ",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                unsafe
                {
                    MTFrameRecordData* FrameRecordPtr;
                    double* RawDataPtr;
                    double* CaliDataPtr;
                    double* AbsIntenPtr;
                    int i;
 
                    FrameRecordPtr = (MTFrameRecordData*)ptrFrameData;
                    RawDataPtr = (double*)FrameRecordPtr->CCDData;
                    CaliDataPtr = (double*)FrameRecordPtr->CalibData;
                    AbsIntenPtr = (double*)FrameRecordPtr->AbsIntenData;
                    for (i = 0;
                      i < Properties.Settings.Default.pixelArrayLength; i++)
                    {
                        RawDataArray[i] = *RawDataPtr++;
                        CaliDataArray[i] = *CaliDataPtr++;
                        AbsIntenArray[i] = *AbsIntenPtr++;
                    }
                }
            }
 
            return numOut;
        }
 
The p-invoke is still the same I think. Excuse the bad wrapping. The format here is narrow.
 
        [DllImport("MT_Spectrometer_SDK.dll", EntryPoint = "MTSSE_GetDeviceSpectrometerFrameData", CallingConvention = CallingConvention.StdCall)]
        private static extern int MT_GetDeviceSpectrometerFrameData(int DeviceID, int SpectrometerID, int WaitUntilDone, ref IntPtr ptrFrameData);

Now the call in the main form:
 
                while (numOut5 <= 0)
                {
                    System.Threading.Thread.Sleep(100);
                    numOut5 = mightexSpectrometer1.MTGetDeviceSpectrometerFrameData(nInit, 1, 1);
                }
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 OriginalGriff 355
1 Maciej Los 180
2 Richard MacCutchan 115
3 arvind mepani 104
4 Tino Fourie 92


Advertise | Privacy | Mobile
Web01 | 2.8.140709.1 | Last Updated 11 Oct 2012
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid