【问题标题】:Pass arrays between VB.NET and VC++在 VB.NET 和 VC++ 之间传递数组
【发布时间】:2017-02-02 10:48:45
【问题描述】:

我编写了一个函数来计算保存在平面文件结构中的变量(风险)的相关矩阵。 IE。风险识别 |年份 |金额

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

我已经用 C++(为了速度)编写了例程,以编译为 dll 并在 VB.NET 中引用。我需要传递 3 个数组(每个标题一个)并将一个二维数组返回给 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;
}

默认文件

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

当前错误信息

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

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      • 2013-12-09
      • 2016-02-19
      • 2020-10-26
      相关资源
      最近更新 更多