在VB.NET和VC ++之间传递数组

时间:2016-09-24 19:12:33

标签: c++ arrays vb.net matrix pearson-correlation

我编写了一个函数来计算平面文件结构中保存的变量(风险)的相关矩阵。即RiskID |年|量

我编写了这个函数,因为我能找到的库例程需要一个矩阵输入。也就是说,RiskID为第二维,年为第一维 - 金额为实际数组值。矩阵需要完整,因为必须也包括零值,因此对于稀疏填充的非零数据 - 这会导致浪费的迭代,可以绕过。该例程依赖于首先按年(asc)排序的数据,然后依据RiskID(asc)

我已经用C ++(速度)编写了例程,编译为dll并在VB.NET中引用。我需要传递3个数组(每个头文件各一个)并将2维数组返回给VB.NET。我想我通过传递3个单独的1d阵列而不是2d阵列作弊但是你去了。我会发布完整的C ++例程,因为其他人可能会发现它有用,如果我想做类似的事情。如果以前没有这样做,我会感到惊讶 - 但我无法找到它。

我缺乏正确实施此操作的互操作知识,并且无处可搜索。据我所知,我可能需要使用SAFEARRAY? 或者是否可以快速解决此问题?或者SAFEARRAY是小菜一碟。无论哪种方式,一个例子都会非常有用。

另外,作为旁注 - 我确定内存管理在某处失败了吗?

这是Visual C ++(VS2013)

标头文件

#ifndef CorrelLib_EXPORTS
#define CorrelLib_API __declspec(dllexport) 
#else
#define CorrelLib_API __declspec(dllimport) 
#endif

// Returns correlation matrix for values in flat file
extern "C" CorrelLib_API double** __stdcall CalcMatrix(int* Risk, int* Year, double* Loss, const int& RowNo, const int& RiskNo, const int& NoSimYear);

CPP档案

#include "stdafx.h"
#include "CorrelLib.h"
#include <memory>
#include <ctime>
using namespace std;

extern "C" CorrelLib_API double** __stdcall CalcMatrix(int* Risk, int* Year, double* Loss, const int& RowNo, const int& RiskNo, const int& NoSimYear)
{
int a, b;
int i, j, k;
int YearCount, MissingYears;
int RowTrack;
//Relies on Year and Risk being sorted in ascending order in those respective orders Year asc, Risk asc
double *RiskTrack = new double[RiskNo](); //array of pointers?
int *RiskTrackBool = new int[RiskNo](); //() sets inital values to zero
double *RiskAvg = new double[RiskNo]();
double *RiskSD = new double[RiskNo]();
//Create 2d array to hold results 'array of pointers to 1D arrays of doubles'
double** Res = new double*[RiskNo];
for (i = 0; i < RiskNo; ++i)
{
    Res[i] = new double[RiskNo](); //()sets initial values to zero
}


//calculate average
for (i = 0; i < RowNo; i++)
{
    a = Risk[i];
    RiskAvg[a] = RiskAvg[a] + Loss[i];
}

for (i = 0; i < RiskNo; i++)
{
    RiskAvg[i] = RiskAvg[i] / NoSimYear;
}

//Enter Main Loop
YearCount = 0;
i = 0; //start at first row
do {
    YearCount = YearCount + 1;
    a = Risk[i];
    RiskTrack[a] = Loss[i] - RiskAvg[a];
    RiskTrackBool[a] = 1;

    j = i + 1;
    do
    {
        if (Year[j] != Year[i])
        {
            break;
        }
        b = (int)Risk[j];
        RiskTrack[b] = Loss[j] - RiskAvg[b];
        RiskTrackBool[b] = 1;
        j = j + 1;
    } while (j < RowNo);

    RowTrack = j;

    //check through RiskTrack and if no entry set to 0 - avg
    for (j = 0; j < RiskNo; j++)
    {
        if (RiskTrackBool[j] == 0)
        {
            RiskTrack[j] = -1.0 * RiskAvg[j];
            RiskTrackBool[j] = 1;
        }
    }

    //Now loop through and perform calcs
    for (j = 0; j < RiskNo; j++)
    {
        //SD
        RiskSD[j] = RiskSD[j] + RiskTrack[j] * RiskTrack[j];
        //Covar
        for (k = j + 1; k < RiskNo; k++)
        {
            Res[j][k] = Res[j][k] + RiskTrack[j] * RiskTrack[k];
        }
    }

    //Reset RiskTrack
    for (k = 0; k<RiskNo; k++)
    {
        RiskTrack[k] = 0.0;
        RiskTrackBool[k] = 0;
    }

    i = RowTrack;
} while (i < RowNo);

//Account For Missing Years
MissingYears = NoSimYear - YearCount;

for (i = 0; i < RiskNo; i++)
{
    //SD
    RiskSD[i] = RiskSD[i] + MissingYears * RiskAvg[i] * RiskAvg[i];
    //Covar
    for (j = i + 1; j < RiskNo; j++)
    {
        Res[i][j] = Res[i][j] + MissingYears * RiskAvg[i] * RiskAvg[j];
    }
}

//Covariance Matrix
for (i = 0; i < RiskNo; i++)
{
    //SD
    RiskSD[i] = sqrt(RiskSD[i] / (NoSimYear - 1));
    if (RiskSD[i] == 0.0)
    {
        RiskSD[i] = 1.0;
    }
    //Covar
    for (j = i + 1; j < RiskNo; j++)
    {
        Res[i][j] = Res[i][j] / (NoSimYear - 1);
    }
}

//Correlation Matrix
for (i = 0; i < RiskNo; i++)
{
    Res[i][i] = 1.0;
    for (j = i + 1; j < RiskNo; j++)
    {
        Res[i][j] = Res[i][j] / (RiskSD[i] * RiskSD[j]);
    }
}

//Clean up
delete[] RiskTrack;
delete[] RiskTrackBool;
delete[] RiskAvg;
delete[] RiskSD;

//Return Array
return Res;
}

Def File

LIBRARY CorrelLib

EXPORTS
CalcMatrix 

VB.NET

我创建了一个简单的winform,其中有一个触发下面代码的按钮。我希望链接到dll,传递数组并以二维数组的形式接收结果。

Imports System
Imports System.Runtime.InteropServices

Public Class Form1
<DllImport("CorrelLib.dll", EntryPoint:="CalcMatrix", CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function CorrelMatrix2(ByRef Risk_FE As Integer, ByRef Year_FE As Integer, ByRef Loss_FE As Double, _
                                    ByRef RowNo As Long, ByRef RiskNo As Long, ByRef NoSimYear As Long) As Double(,)
End Function

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim i As Integer, j As Integer
    Dim Risk() As Long, Year() As Long, Loss() As Double
    Dim NoRisks As Long, NoSimYear As Long, NoRows As Long
    Dim counter As Long
    Dim Result(,) As Double

    NoRisks = 50
    NoSimYear = 10000
    NoRows = NoRisks * NoSimYear

    ReDim Risk(0 To NoRows - 1), Year(0 To NoRows - 1), Loss(0 To NoRows - 1)

    counter = 0
    For i = 1 To NoSimYear
        For j = 1 To NoRisks
            Risk(counter) = j
            Year(counter) = i
            Loss(counter) = CDbl(Math.Floor((1000000 - 1 + 1) * Rnd())) + 1
            counter = counter + 1
        Next j
    Next i

    Dim dllDirectory As String = "C:\Users\Documents\Visual Studio 2013\Projects\CorrelLibTestForm"
    Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + ";" + dllDirectory)

    Result = CorrelMatrix2(Risk(1), Year(1), Loss(1), NoRows, NoRisks, NoSimYear)
End Sub
End Class

当前错误消息

  

未处理的类型&gt;&#39; System.Runtime.InteropServices.MarshalDirectiveException&#39;发生在&gt; CorrelLibTestForm.exe

中      

其他信息:无法封送&#39;返回值&#39;:无效&gt;托管/非托管类型组合。

1 个答案:

答案 0 :(得分:0)

指向指针的double **指针与vb中的2维数组相同。你最好的选择是只返回一个指针:

double *pdbl;
pdbl = &res[0][0];

return pdbl; //pdbl points to the first element

在vb中,您使用IntPtr来获取指针:

Dim Result As IntPtr = Marshal.AllocHGlobal(4)
Dim dbl As Double

Result = CorrelMatrix2(Risk(1), Year(1), Loss(1), NoRows, NoRisks, NoSimYear)

//derefference the double pointer, i(integer) is actually the index in the array of doubles 
dbl = CType(Marshal.PtrToStructure(IntPtr.Add(Result, i * 8), GetType(Double)), Double)

c ++函数中的res数组需要是公共的,因此在函数返回后分配给它的内存有效。