C# 从C中调用C:当其他两个都进行得很好时,如何处理这一个2D双打数组?

C# 从C中调用C:当其他两个都进行得很好时,如何处理这一个2D双打数组?,c#,c,marshalling,C#,C,Marshalling,我从C中调用一个C例程,使用一些double、int、一个大字符串和一些数组作为参数。有些数组返回的值正确,但只有一个数组返回不正确。该数组在返回C时损坏,而不是在执行C例程时损坏 C方: 现在,除了MemberEndforces之外,所有参数都返回OK。 另外,当我交换调用方和被调用方的反应顺序和MemberEndforces时,反应从Cr OK返回,但不是MemberEndforces。 我无法读取VS监视窗格中MemberEndforces的内存。 另外,如果我没有填充MemberEndf

我从C中调用一个C例程,使用一些double、int、一个大字符串和一些数组作为参数。有些数组返回的值正确,但只有一个数组返回不正确。该数组在返回C时损坏,而不是在执行C例程时损坏

C方:

现在,除了MemberEndforces之外,所有参数都返回OK。 另外,当我交换调用方和被调用方的反应顺序和MemberEndforces时,反应从Cr OK返回,但不是MemberEndforces。 我无法读取VS监视窗格中MemberEndforces的内存。 另外,如果我没有填充MemberEndforces C-side,那么对它不做任何处理,也没有什么区别:仍然是相同的错误消息

C端:


当然,问题是:我做错了什么?为什么?也许这里似乎有一些概念问题。关于最后一个参数可能损坏的原因。将其定义为二维数组c,然后将其用作c中的一维数组

在C语言中,数组是指向某个特定类型的内存块的指针,例如double。通常,您将其分配为*typeofdouble*arraysize*。二维数组是指向某一类型指针的内存块的指针。要创建2D数组,必须首先分配指针数组,然后将每个指针设置为一个数组。您可能会看到这样的情况:

C在这里有不同的选项,请参阅:另外,C是一个托管内存系统,因此从C中传递指针来表示C是一个问题,因为GC垃圾收集器随时可能启动并更改一个变量的内存地址,该变量从运行C代码的C下面拉出地毯。C有一种使用“不安全”代码修复内存的方法,它允许您“锁定”和地址,防止GC在一段时间内移动内存,就像在调用非托管代码期间一样


最后,您只需使用地址来访问内存块。确保这些块在调用过程中不会移动,并确保在两种编码语言中以相同的方式解释块的格式。

导致问题的是本征频率/f数组和nrModes参数。问题是100%的C端。呼叫/接收正常。我只是在C函数Cr中处理了错误的本征频率,覆盖了一些东西。谢谢你和我一起思考。

你为什么要将double*编组为double[,]?double*是指向一维数组的指针?我认为应该是double[]这是非常危险的pinvoke,C代码直接写入GC堆,无法确保数组足够大。我们无法判断DoF和nE是否具有与mbCount匹配的正确值。尤其是偏转阵列的访问看起来可疑。+1破解看起来像是一个一个接一个的bug等待着发生。线外着色会导致内存损坏,程序开始产生垃圾和随机崩溃。也将mbCount作为参数传递。正在讨论的数组使用新的。它们不是指向指针数组的锯齿状指针数组,而是真正的2D。Q是参差不齐的,但这与这里无关:它是复制的。我猜它们是通过ref传递给C的?因此,在C端拾取指针将使数组以某种方式可以在C端访问。所以我想所有存储在C端的值都必须是C端可见的。这是真的,数组是通过引用交换的。语言之间的互操作可能是一个技巧。例如,C中的StringBuilder或String与C中的char*相同,因此我假设您可以不使用它,因为它没有被使用。您可以将C创建的数组传递到C中;如果没有超过数组边界,并且在C调用期间停止GC移动内存。您可以使用Marshal.AllocHGlobal在C中分配非托管内存,但请记住使用“fixed”语句释放它或禁止GC。您应该检查nE和mbCount的值。如果我对MemberEndforces C-side不做任何操作,只是将其作为指针传入,我仍然会收到相同的问题/错误消息。
[DllImport(@"Critias.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Cr", 
            CharSet = CharSet.Ansi, BestFitMapping =false,ThrowOnUnmappableChar =true)]
public static extern int Cr([In] StringBuilder isc, 
            int nrModes, 
            double[] eigenfrequencies, 
            ref double error, 
            double[,] defl,
            double[] reactions,
            double[,] force);

public double[,] Deflection;
public double[,] MemberEndforces;       
public double[] Reactions;
public double error;

    ....

var eigenFreq = new double[nrModes + 1]; // +1 for C-sides pseudo FORTRAN arrays
MemberEndforces = new double[mbCount,12];                
Deflection = new double[mbCount + 1,6];               
Reactions = new double[6];       
info = NativeMethods.Cr(
            isc, nrModes, eigenFreq, ref error,  Deflection, Reactions, MemberEndforces);
extern "C"
{
    __declspec(dllexport) int __cdecl Cr(
        char *_isc,
        int nM_calc,
        double* f,
        double* error,
        double* deflection,     
        double* reactions ,
        double* endforces ) // this is the annoying one
     {
      double** Q; // malloc-ed somewhere.
      double* D;
      double *R;

     .....
     for (int ii = 0; ii < DoF; ii++)
            deflection[ii ] = D[ii+1];
    for (int ii = 0; ii < nE; ii++)
        for (int jj = 0; jj < 12; jj++)
        {
            endforces[ii * 12 + jj] = Q[ii+1][jj + 1];// values OK,but...
        }
    for (int ii = 0; ii < 6; ii++)
        reactions[ii] = R[ii+1];

            ......

        }