【问题标题】:Calling unmanaged C++ library (dll) from C# : How to properly pass data between managed and unmanaged code从 C# 调用非托管 C++ 库 (dll):如何在托管代码和非托管代码之间正确传递数据
【发布时间】:2012-11-20 14:35:09
【问题描述】:

我一直在使用从传感器读取测量数据并使用 C++ 编写的库来分析该数据的接口。

函数大致如下:

  1. 在 C++ 库中设置测量参数
  2. 从传感器获取数据(总共 1200 次测量)
  3. 将数据写入 C++ 库
  4. 在 C++ 库中处理所有 1200 次测量
  5. 从 C++ 库中读取结果

从 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# 程序中?

(对不起,我的问题太长了。)

【问题讨论】:

  • 输出有什么问题?是否有异常或错误代码?您的数据处理是否在用 c++ 编写的小样本中工作?只是为了验证错误是由 c# 和本机库之间的编组引起的,我将使用 c++ 中的一个小数据集测试该库,并在 c# 中尝试相同的操作。
  • 好吧。我的程序运行无异常。但是,在处理完 dll 后,会返回指示输入数据无效的错误代码。我可以从 dll 读取的值类似于 -9.671407E+24,-1.661535E+35,2.407412E-35,-1.136868E-13,-1.084202E-19,2.199023E+12,5.6295E+14 ... . 所以,绝对不是有效数据。我没有使用 c++ 测试过 dll。
  • 嗯...也许这不是重点,但我还是要问:你为什么使用struct(而不是object或其他类型)? AFAIK,使用struct 代替object 类型没有任何好处。此外,模仿 constructor 的静态方法的使用与 C# 代码应有的方式有点不同。
  • Woohoo,关于用于创建构造函数的静态方法,您可能是对的。

标签: c# c++ .net pinvoke


【解决方案1】:

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

C# 结构定义: 来自:

    [StructLayout(LayoutKind.Sequential)]

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 1970-01-01
    • 2013-08-24
    • 2011-02-27
    相关资源
    最近更新 更多