从C回来时崩溃:为什么(当然)?

时间:2017-12-27 21:28:50

标签: c# c interop

在C#中填充二进制缓冲区(1.5到2 kB),我在C DLL中引用它。一切顺利(通过C表示如此),直到我必须返回C#。在最后一刻,程序崩溃了(没有异常抛出,只是一次崩溃:"停止工作")。 Cr是DLL中的C例程。 我在Visual Studio Community 2017工作。 早些时候,我通过字符串和sscanf_s在C中传递数据,这很好,但是想要加快一些事情(并在途中学习不同的东西)。 缓冲区包含各种浮点数,整数和双精度数,它们被正确地拾取到C侧(踩到这样说)。 这是第一段C#代码:

    double[,] MemberEndforces = new double[mbCount, 12];
            double[,] Deflection = new double[mbCount + 1, 6];
            //double[] Reactions = new double[6];

            unsafe
            {
                using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream()))
                {

                    BuildExportBuffer(binWriter);
                    var buffer = ((MemoryStream)binWriter.BaseStream).GetBuffer();
                    info = NativeMethods.Cr(
                        ref buffer, NrOfMod, Eigenfreqs, ref error,
                        ref rms_resid, MemberEndforces, Deflection);
                }  // end using
                if (info != 0)
                    throw new MyException(string.Format(ErrorMessages.SomeError, info));
                bool converged = error < tolerance;

                ...
            } // end unsafe

这里接下来是接收C程序的头部。只有在最后一个大括号,然后退回到C#,才会发生崩溃。它是这样做的,有或没有safe {}块。

extern "C"
{
    char ** buffer;
    //char * next_token;

    __declspec(dllexport) int __cdecl Cr(
        char **_buffer,
        int n,
        float * eigenFreq,
        double * error,
        double * rms_resid,
        double * endforces,
        double * deflection
         )
     { buffer = _buffer;
        ...
        // read stuff from  buffer and process it...
        return ExitCode;

     }

这是DLL导入:

[DllImport(@"Cr.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Cr", 
        CharSet = CharSet.Ansi, BestFitMapping =false,ThrowOnUnmappableChar =true)]


    public static extern int Cr([In] ref byte[] buffer,
        [In] int n, 
        float[] eigenfreq, 
        ref double error, 
        ref double rms_resid,            
        double[,] force,
        double[,] defl);

所以:为什么?我做错了什么?如果C部分出现问题,它会比最后一个花括号更早出现,不是吗? C代码的最后一部分会释放大量的malloc-ed块(不是缓冲区!)。

2 个答案:

答案 0 :(得分:0)

根据MSDN上的this post,在调用外部函数时,多维数组没有被编组,因此必须将数组转换为单维数组。

我看到这些数组是输出缓冲区,所以对于调用你可以这样做:

double[] MemberEndforces = new double[mbCount * 12];
double[] Deflection = new double[(mbCount + 1) * 6];

//...

info = NativeMethods.Cr(
                    ref buffer, NrOfMod, Eigenfreqs, ref error,
                    ref rms_resid, MemberEndforces, Deflection);

然后,在调用函数后,您需要将这些数组转换为多维数组:

double[,] mMemberEndforces = new double[mbCount, 12];
double[,] mDeflection = new double[mbCount + 1, 6];

for(int x = 0; x < mbCount; x++)
{
    for(int y = 0; y < 12; y++)
        mMemberEndforces[x,y] = MemberEndforces[x * 12 + y];
}

for(int x = 0; x < mbCount + 1; x++)
{
    for(int y = 0; y < 6; y++)
        mDeflection[x,y] = Deflection[x * 6 + y];
}

答案 1 :(得分:0)

我找到了一个解决方案: 我删除了不安全的{}。这不是必需的。 然后我在调用C例程时“取消引用”ref buffer:

 using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream()))
 {
     BuildExportBuffer(binWriter); // fill the buffer with doubles and floats ant ints
     var buffer = ((MemoryStream)binWriter.BaseStream).GetBuffer(); // get the buffer's address
     info = NativeMethods.Cr( buffer, NrOfMod, Eigenfreqs, ref error, ref rms_resid,
 MemberEndforces, Deflection);
  .....
    etc.

当然,DLLImport也是这样。

同样在C函数中我改变了一些小东西:

extern "C"
{
char ** buffer;

__declspec(dllexport) int __cdecl Cr(
    char *_buffer,  // <== here and
    int n,
    float * eigenFreq,
    double * error,
    double * rms_resid,
    double * endforces,
    double * deflection
     )
 {

    buffer = &_buffer; // <== here.

显示我如何从缓冲区C端读取双精度语:

double  readd(char **buffer)
{
   double *f = (double *) *buffer;
   *buffer += 8;
   return *f;
}

像魅力一样工作。我还是要弄明白为什么。