我正在尝试从C#中调用cminpack_dll.dll中的lmdif1方法,并且遇到了一些奇怪的错误。

传递给lmdif1的第二个和第三个参数是ints,在我的测试中,顺序的值是12和9。现在,当ther在C代码中时,现在的12值现在是9,而9的值则在308000之间变化和912000。不知道为什么。

首先,我想知道我使用的签名是否有效

C#签名:

[DllImport("cminpack_dll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int lmdif1(IntPtr fcn, int m, int n, double[] x, double[] fVec, double tol, int[] iwa, double[] wa, int lwa);

C签名:
int __cminpack_func__(lmdif1)(__cminpack_decl_fcn_mn__ void *p, int m, int n, real *x,
real *fvec, real tol, int *iwa,
real *wa, int lwa)

和我所说的方式:
//All of the variables passed in match the types in C# signature above.
var info =lmdif1(
            functionPointer,
            pointsetLength,
            initialGuessLength,
            initialGuess,
            fVec,
            tol,
            iwa,
            wa,
            lwa);

现在,这是我第一次处理PInvoke,而我的C也不是很好,因为以前从未真正做过,所以任何帮助都将很棒。我的怀疑是我可能需要编码东西,但是我尝试将I4和U4编码为int,但它仍然做同样的事情。

预先加油为您提供帮助。

编辑:
这是描述C函数的注释,如果有帮助的话:
/*     ********** */

/*     subroutine lmdif1 */

/*     the purpose of lmdif1 is to minimize the sum of the squares of */
/*     m nonlinear functions in n variables by a modification of the */
/*     levenberg-marquardt algorithm. this is done by using the more */
/*     general least-squares solver lmdif. the user must provide a */
/*     subroutine which calculates the functions. the jacobian is */
/*     then calculated by a forward-difference approximation. */

/*     the subroutine statement is */

/*       subroutine lmdif1(fcn,m,n,x,fvec,tol,info,iwa,wa,lwa) */

/*     where */

/*       fcn is the name of the user-supplied subroutine which */
/*         calculates the functions. fcn must be declared */
/*         in an external statement in the user calling */
/*         program, and should be written as follows. */

/*         subroutine fcn(m,n,x,fvec,iflag) */
/*         integer m,n,iflag */
/*         double precision x(n),fvec(m) */
/*         ---------- */
/*         calculate the functions at x and */
/*         return this vector in fvec. */
/*         ---------- */
/*         return */
/*         end */

/*         the value of iflag should not be changed by fcn unless */
/*         the user wants to terminate execution of lmdif1. */
/*         in this case set iflag to a negative integer. */

/*       m is a positive integer input variable set to the number */
/*         of functions. */

/*       n is a positive integer input variable set to the number */
/*         of variables. n must not exceed m. */

/*       x is an array of length n. on input x must contain */
/*         an initial estimate of the solution vector. on output x */
/*         contains the final estimate of the solution vector. */

/*       fvec is an output array of length m which contains */
/*         the functions evaluated at the output x. */

/*       tol is a nonnegative input variable. termination occurs */
/*         when the algorithm estimates either that the relative */
/*         error in the sum of squares is at most tol or that */
/*         the relative error between x and the solution is at */
/*         most tol. */

/*       info is an integer output variable. if the user has */
/*         terminated execution, info is set to the (negative) */
/*         value of iflag. see description of fcn. otherwise, */
/*         info is set as follows. */

/*         info = 0  improper input parameters. */

/*         info = 1  algorithm estimates that the relative error */
/*                   in the sum of squares is at most tol. */

/*         info = 2  algorithm estimates that the relative error */
/*                   between x and the solution is at most tol. */

/*         info = 3  conditions for info = 1 and info = 2 both hold. */

/*         info = 4  fvec is orthogonal to the columns of the */
/*                   jacobian to machine precision. */

/*         info = 5  number of calls to fcn has reached or */
/*                   exceeded 200*(n+1). */

/*         info = 6  tol is too small. no further reduction in */
/*                   the sum of squares is possible. */

/*         info = 7  tol is too small. no further improvement in */
/*                   the approximate solution x is possible. */

/*       iwa is an integer work array of length n. */

/*       wa is a work array of length lwa. */

/*       lwa is a positive integer input variable not less than */
/*         m*n+5*n+m. */

/*     subprograms called */

/*       user-supplied ...... fcn */

/*       minpack-supplied ... lmdif */

/*     argonne national laboratory. minpack project. march 1980. */
/*     burton s. garbow, kenneth e. hillstrom, jorge j. more */

/*     ********** */

/*     check the input parameters for errors. */

最佳答案

我认为这里的根本问题是,您没有真正的方法来告诉 native 代码实际是什么。您需要了解所有宏才能做到这一点。

所以,这就是我所做的。我下载了该库,并通过C预处理器传递了lmdif1.c文件,因此扩展了宏。我使用了mingw编译器中的那个。它产生了以下输出:

int __attribute__((__dllexport__)) lmdif1(cminpack_func_mn fcn_mn,
    void *p, int m, int n, double *x, double *fvec, double tol,
    int *iwa, double *wa, int lwa);

然后查看cminpack_func_mn的定义,我们有:
typedef int (*cminpack_func_mn)(void *p, int m, int n, const double *x,
    double *fvec, int iflag);

因此,lmdif1会收到一个额外的void指针。由于函数指针类型cminpack_func_mn也收到具有相同非描述符名称的void指针,因此我敢打赌,传递给lmdif1的指针将传递回回调函数fcn_mn。此机制通常用于允许库的使用者编写有权访问额外状态的回调函数。

如果不需要,并且肯定不会使用C#委托(delegate),则可以传递IntPtr.Zero并忽略回调中的值。

要解决此问题,您需要进行三处更改:
  • 将void指针添加到lmdif1的C#声明中。
  • 将void指针添加到回调委托(delegate)的C#声明中,并添加实现回调的函数。
  • IntPtr.Zero传递给lmdif1的额外的void指针参数。

  • 我不确定为什么您将回调参数声明为IntPtr。您可以在此处使用委托(delegate)类型,也可以使用UnmanagedFunctionPointer属性强制执行Cdecl

    更新:为了完整起见,我深入研究了lmdif1的实现,并研究了如何调用回调。是的,如上所述,将空指针p传递回了回调。

    10-07 23:53