通过p / invoke

时间:2016-11-11 03:44:30

标签: c# .net dll pinvoke

我尝试使用XGBoost's dll(libxgboost.dll)创建DMatrix(类似于2D数组)并获取它有多少列。它运行正常,直到它在下面的代码中的System.AccessViolationException行引发int cols = ...

using System;
using System.Runtime.InteropServices;

namespace basicXgboost
{
  class Program
  {
    [DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
    public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, IntPtr outputPtr);

    [DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
    public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);

    static void Main(string[] args)
    {
      IntPtr dmatrixPtr = Marshal.AllocHGlobal(1000000);
      IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);

      int result = XGDMatrixCreateFromFile("../../libs/test.txt", 0, dmatrixPtr);
      int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);

      Marshal.FreeHGlobal(dmatrixPtr);
      Marshal.FreeHGlobal(dmatrixColumnsPtr);
    }
  }
}

为什么访问使用XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr)分配的非托管内存会导致System.AccessViolationException

一种可能性可能是我错误地使用pinvoke这些功能。以下是我使用的每个dll函数的定义:

XGDMatrixCreateFromFile()

/*!
 * \brief load a data matrix
 * \param fname the name of the file
 * \param silent whether print messages during loading
 * \param out a loaded data matrix
 * \return 0 when success, -1 when failure happens
 */
XGB_DLL int XGDMatrixCreateFromFile(const char *fname,
                                    int silent,
                                    DMatrixHandle *out);

XGDMatrixNumCol()

/*!
 * \brief get number of columns
 * \param handle the handle to the DMatrix
 * \param out The output of number of columns
 * \return 0 when success, -1 when failure happens
 */
XGB_DLL int XGDMatrixNumCol(DMatrixHandle handle,
                            bst_ulong *out);

Here是我项目的回购。我正在使用Visual Studio Enterprise 2015。它内置于" Debug" Windows 10 Pro(64位)上的模式(针对x64)。可以找到libxgboost.dll的x64二进制文件here。虽然链接的repo确实包含libxgboost.dll的副本。

1 个答案:

答案 0 :(得分:1)

尝试使用似乎由DLL使用的调用约定Cdecl。

此外,XGDMatrixCreateFromFile功能的签名是错误的。期望的参数不是指向您分配的某些内存的指针,但该函数将自己分配内存,然后将指针作为输出参数返回。

尝试以下代码。请注意out函数中outputPtr参数使用XGDMatrixCreateFromFile关键字。

[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);

[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);

static void Main(string[] args)
{
    IntPtr dmatrixPtr;
    IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);

    int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
    int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);

    Marshal.FreeHGlobal(dmatrixColumnsPtr);
}

当这种方法有效时,您还可以使用ulong数据类型简化调用以获取列数:

[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);

[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, out ulong dmatrixColumnsPtr);

static void Main(string[] args)
{
    IntPtr dmatrixPtr;
    ulong dmatrixColumns;

    int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
    int cols = XGDMatrixNumCol(dmatrixPtr, out dmatrixColumns);
}