Передача массивов между VB.NET и VStack Overflow

Я написал функцию для расчета корреляционная матрица для переменных (рисков), содержащихся в плоской файловой структуре. То есть RiskID | Год | Количество

Я написал функцию, потому что библиотечные процедуры, которые я могу найти, требуют матричного ввода. То есть RiskID в качестве 2-го измерения и год в качестве 1-го измерения — с суммами в качестве фактических значений массива. Матрица должна быть полной, в том числе нулевые значения также должны быть включены и, следовательно, для малонаселенных ненулевых данных — это приводит к бесполезным итерациям, которые можно обойти. Процедура основана на сортировке данных сначала по году (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;
}

Def File

LIBRARY CorrelLib

EXPORTS
CalcMatrix

VB.NET

Я создал простую форму win с кнопкой, которая запускает приведенный ниже код. Я хочу связать с 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:\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

Текущее сообщение об ошибке

Необработанное исключение типа> System.Runtime.InteropServices.MarshalDirectiveException возникло в> CorrelLibTestForm.exe

Дополнительная информация: Cannot marshal ‘return value’: Недопустимая> комбинация управляемого / неуправляемого типа.

0

Решение

double ** указатель на указатель не то же самое с 2-мерным массивом в 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)

Ваш res массив в функции c ++ потребности быть публичным, поэтому выделенная ему память действительна после возврата из функции.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]