从C#调用非托管C ++库(dll):如何在托管代码和非托管代码之间正确传递数据

时间:2012-08-29 09:46:15

标签: c# c++ .net pinvoke

我一直在使用一个界面来读取传感器的测量数据,并使用用C ++编写的库来分析这些数据。

该功能仅限于以下内容:

  1. 在C ++库上设置测量参数
  2. 从传感器获取数据(共计1200次测量)
  3. 将数据写入C ++库
  4. 在C ++库中处理所有1200次测量
  5. 从C ++库中读取结果
  6. 从C#-code调用此C ++库之前已经处理过这个问题:Calling unmanaged C++ library (dll) from C# creates an access violation error (0xc0000005).

    现在看来,C ++库

    • 无法正确获取数据
    • 无法保存数据
    • 无法正确地将结果返回给我的C#代码。

    不好的是,我无法调试这个C ++库。

    我的代码出了什么问题?

    1)设置测量参数

    namespace PdWaveApi
    {
    [StructLayout(LayoutKind.Sequential)]
    public struct PDDataInfo
    
    {                   
       public int   nPings;              
       public int   nDataRate;          
       public int   nSamples;            
       public float fFrequency;          
       public float fBeamAngle;          
       public int   nInstrument;         
       public int   nCoordSystem;        
       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
       public short[] hBeamToXYZ;       
       public short hWaveT1;
    
        // Constructor
       public static PDDataInfo Create()
       {
           PDDataInfo DataStruct = new PDDataInfo();
           DataStruct.hBeamToXYZ = new short[9];
           return DataStruct;
       }
       }
    }
    
    public class PdWaveBaseLWrapper
    {
       [DllImport("PdWaveBase.dll", EntryPoint = "PDSetInstrumentConfig")]
       public static extern int PDSetInstrumentConfig(ref PDDataInfo pDataInfo);
    }
    
    public void SetInstrumentConfiguration()
    {
        PdWaveApi.PDDataInfo InstrumentConfiguration = new PdWaveApi.PDDataInfo();
        .................
        Initializing the InstrumentConfiguration structure
        ...............
        PdWaveBaseLWrapper.PDSetInstrumentConfig(ref InstrumentConfiguration);
    }
    

    3)从传感器读取数据并将数据写入C ++库

    namespace PdWaveApi
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct PDWaveSample
        {   
            [MarshalAs(UnmanagedType.I1)]
            public bool Valid;
            public float fPressure;
            public float fDistance;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PD_MAX_WAVEBEAMS)]
            public float[] fVel;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PD_MAX_WAVEBEAMS)]
            public ushort[] nAmp;
    
            // Constructor
    
            public static PDWaveSample Create()
            {
                PDWaveSample DataStruct = new PDWaveSample();
                DataStruct.fVel = new float[Constants.PD_MAX_WAVEBEAMS];
                DataStruct.nAmp = new ushort[Constants.PD_MAX_WAVEBEAMS];
                return DataStruct;
            }
        }
    }
    
    public class PdWaveBaseLWrapper
    {
        [DllImport("PdWaveBase.dll", EntryPoint = "PDSetWaveSample")]
        public static extern int PDSetWaveSample(ref PDWaveSample pWaveSample);
    }
    
    namespace SensorInterface
    {
        public partial class frmSensorInterface : Form
        {
            public PdWaveApi.PDWaveSample WaveSampleData = PdWaveApi.PDWaveSample.Create();
    
            private void OnNewData(object sender, OnNewDataEvent e)
            {
                ReadWaveSample(ref WaveSampleData);
                SetWaveSample(ref WaveSampleData);
            }
    
            public void ReadWaveSample(ref PdWaveApi.PDWaveSample WaveSampleData)
            {
                DateTime MeasurementTimeStamp;
                float[] dVel = new float[4];
                float dTemperature = new float();
                float dPitch = new float();
                float dRoll = new float();
                float dHeading = new float();
                float dPressure = new float();
                short[] sAmp = new short[4];
    
                //  Read some of the latest data from the control
                GetVelocity(ref dVel[0], ref dVel[1], ref dVel[2], ref dVel[3]);
                GetAmplitude(ref sAmp[0], ref sAmp[1], ref sAmp[2], ref sAmp[2]);
    
                ..............
                // Set other data to the structure
    
            }
    
            public void SetWaveSample(ref PdWaveApi.PDWaveSample WaveSampleData)
            {
                PdWaveBaseLWrapper.PDSetWaveSample(ref WaveSampleData);
            }
        }
    }
    

    4)在C ++库中处理所有1200次测量

    [StructLayout(LayoutKind.Sequential)]
    public struct PDWaveBurst
    {
        [MarshalAs(UnmanagedType.ByValArray , SizeConst = Constants.PD_MAX_WAVEMEAS_AST)]    
        public float[] fST; 
        public float fWinFloor;
        public float fWinCeil;
        [MarshalAs(UnmanagedType.I1)]
        public bool bUseWindow;
        [MarshalAs(UnmanagedType.I1)]
        public bool bSTOk;
        [MarshalAs(UnmanagedType.I1)]
        public bool bGetRawAST;
        [MarshalAs(UnmanagedType.I1)]
        public bool bValidBurst;
    
        public static PDWaveBurst Create()
        {
            PDWaveBurst DataStruct = new PDWaveBurst();
            DataStruct.fST = new float[Constants.PD_MAX_WAVEMEAS_AST];
            return DataStruct;
        }
    }
    
    [DllImport("PdWaveBase.dll", EntryPoint = "PDPreProcess")]
    public static extern int PDPreProcess(int nSample, ref PDWaveBurst pWaveBurst);
    
    [DllImport("PdWaveBase.dll", EntryPoint = "PDProcessReturnInt")]
    public static extern int PDProcessReturnInt();
    
    public void PreprocessBurstData(int nSamples)
    {
        PdWaveApi.PDWaveBurst WaveBurstData = PdWaveApi.PDWaveBurst.Create();
    
        WaveBurstData.fST = new float[4096];
        WaveBurstData.fWinFloor = (float)1.25;
        WaveBurstData.fWinCeil = 2;
        WaveBurstData.bUseWindow = false;
        WaveBurstData.bSTOk = false;
        WaveBurstData.bGetRawAST = false;
        WaveBurstData.bValidBurst = false;
    
        PdWaveBaseLWrapper.PDPreProcess(nSamples, ref WaveBurstData);
    }
    
    public void ProcessData()
    {
        int ProcessError = PdWaveBaseLWrapper.PDProcessReturnInt();
    }
    

    5)从C ++库中读取结果

    [StructLayout(LayoutKind.Sequential)]
    public struct PDWavePar {
       public float fTm02;  
       public float fTp;     
       public float fDirTp;  
       public float fSprTp;  
       public float fMainDir;
       public float fUI;     
       public float fHm0;   
       public float fH3;
       public float fT3;    
       public float fH10; 
       public float fT10;
       public float fHmax;
       public float fTmax;
       public float fTz;
       public float fMeanPres;
       public int   nNumNoDet;
       public int   nNumBadDet;
       public int   nErrCode;  
       public int   nSpectrum; 
       public float fMeanAST;
    }
    
    
    [DllImport("PdWaveBase.dll", EntryPoint = "PDGetWavePar")]
    public static extern int PDGetWavePar(ref PDWavePar pwWavePar);
    
    public void GetOutput()
    {
        PdWaveApi.PDWavePar WaveParameters = new PdWaveApi.PDWavePar();
        PdWaveBaseLWrapper.PDGetWavePar(ref WaveParameters);
    }
    

    所以,作为结论:

    我的代码应该更改什么 - 将数据正确传递给非托管dll - 让dll在其内部结构中保存和处理数据 - 从非托管代码到我的C#程序正确读取结果?

    (对不起我的问题的长度。)

1 个答案:

答案 0 :(得分:1)

我的问题的最终解决方案是将对齐更改为1个字节,如下所示:

C#结构定义: 从:

    [StructLayout(LayoutKind.Sequential)]

    [StructLayout(LayoutKind.Sequential, Pack=1)]

至少在这里讨论过这个问题: Calling C++ metro dll from c# metro app and returning filled structures