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

Pass arrays between VB.NET and VC++

本文关键字:NET vc++ 数组 VB 之间      更新时间:2023-10-16

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

我已经写了这个函数,因为我能找到的库例程需要矩阵输入。也就是说,RiskID作为第2个维度,year作为第1个维度,amount作为实际的数组值。矩阵需要是完整的,因为零值也必须包括在内,因此对于稀疏填充的非零数据-这会导致浪费迭代,可以绕过。例程依赖于首先按Year (asc)然后按RiskID (asc)

排序的数据。

我已经在c++中编写了例程(为了速度),将其编译为dll并在VB.NET中引用。我需要传递3个数组(每个头一个),并返回一个二维数组回到VB.NET。我想我是作弊了,传递了3个单独的一维数组而不是二维数组,但这就对了。我将发布完整的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文件
LIBRARY CorrelLib
EXPORTS
CalcMatrix 

VB。

我创建了一个简单的winform,带有一个触发下面代码的按钮。我希望链接到dll,传递数组并接收结果作为2d数组。

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:UsersDocumentsVisual Studio 2013ProjectsCorrelLibTestForm"
    Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + ";" + dllDirectory)
    Result = CorrelMatrix2(Risk(1), Year(1), Loss(1), NoRows, NoRisks, NoSimYear)
End Sub
End Class

当前错误信息

类型为>'System.Runtime.InteropServices. '的未处理异常。MarshalDirectiveException'发生在>CorrelLibTestForm.exe

附加信息:无法封送"返回值":无效的>托管/非托管类型组合。

指向指针的double **指针是而不是,与vb中的二维数组相同。最好的方法是只返回一个指针:

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数组需要是公共的,以便在函数返回后分配给它的内存有效。