Передача массива C # структур с массивом двойного члена в C DLL по ссылке - PullRequest
0 голосов
/ 05 октября 2010

У нас есть следующий код сортировки между c # и c dll. Однако при печати значения в функции C dll все значения, связанные со свойствами двойного массива, равны 0,0000000. Я поместил некоторые построчные комментарии для кода, имеющего проблемы.

Мы что-то упустили, чтобы настроить поведение при сортировке?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            MonteCarlo montecarlo = new MonteCarlo();
            montecarlo.RunMonteCarlo();
        }
    }
    class MonteCarlo
    {

        [DllImport("MonteCarloCUDA.dll")]
        public static extern int MonteCarloPrint([In, Out]PurchaseOrder[] purchaseorders, int length);

        public void RunMonteCarlo()
        {

            PurchaseOrder[] purchaseorders = new PurchaseOrder[3];

            purchaseorders[0] = new PurchaseOrder();
            purchaseorders[0].Value1 = "AAAAA";
            purchaseorders[0].Value2 = 0.111;
            purchaseorders[0].Value3 = new double[2]; // Assign the values to array of double
            purchaseorders[0].Value3[0] = 0.11111;
            purchaseorders[0].Value3[1] = 0.22222;

            purchaseorders[1] = new PurchaseOrder();
            purchaseorders[1].Value1 = "BBB";
            purchaseorders[1].Value2 = 0.222;
            purchaseorders[1].Value3 = new double[2];
            purchaseorders[1].Value3[0] = 0.33333;
            purchaseorders[1].Value3[1] = 0.44444;

            purchaseorders[2] = new PurchaseOrder();
            purchaseorders[2].Value1 = "CCC";
            purchaseorders[2].Value2 = 0.333;
            purchaseorders[2].Value3 = new double[2];
            purchaseorders[2].Value3[0] = 0.55555;
            purchaseorders[2].Value3[1] = 0.66666;

            int result = MonteCarloPrint(purchaseorders, purchaseorders.Length);

            Console.ReadKey();
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
        public unsafe struct PurchaseOrder
        {
            public string Value1;

            public double Value2;

            public double[] Value3; // Array of double member
        }
    }
}

// Код C

#include <stdio.h>

typedef struct PurchaseOrder 
{ 
   char* Value1; 
   double Value2; 
   double* Value3;
 };



__declspec(dllexport) int __stdcall MonteCarloPrint(PurchaseOrder *hostPurchaseOrders, int length)
{    
    printf("\nSize of PurchaseOrder: %d",sizeof(PurchaseOrder));

    for (int i = 0; i < length; i++) 
    {           
        printf("\n\nAddress: %u",hostPurchaseOrders+i);         
        printf("\nValue1: %s",(hostPurchaseOrders+i)->Value1);
        printf("\nValue2: %f",(hostPurchaseOrders+i)->Value2);
        printf("\nValue3[0]: %f",(hostPurchaseOrders+i)->Value3[0]);
        printf("\nValue3[1]: %f",(hostPurchaseOrders+i)->Value3[1]);

    }
}}

Результат печати из функции C dll

Size of PurchaseOrder: 24

Address: 13180880
Value1: AAAAA
Value2: 0.111000
Value3[0]: 0.000000 // No value are marshalled 
Value3[1]: 0.000000

Address: 13180904
Value1: BBB
Value2: 0.222000
Value3[0]: 0.000000
Value3[1]: 0.000000

Address: 13180928
Value1: CCC
Value2: 0.333000
Value3[0]: 0.000000
Value3[1]: 0.000000

1 Ответ

1 голос
/ 05 октября 2010

Удалите свойство Pack из объявления [DllImport], это неправильно.Вы не используете директиву #pragma pack в своем коде C, по умолчанию используется значение Pack.Если бы это было в силе, тогда ваш код на C сообщал бы 16.

Вы видите 24, потому что есть 4 байта заполнения для выравнивания двойного и 4 байта заполнения в конце структуры, чтобы сделатьдвойное выравнивание, когда структура используется в массиве.4 + 4 + 8 + 4 + 4 = 24. По умолчанию используется упаковка 8.

Вы можете повысить эффективность, поменяв значениями Value2 и Value3, чтобы получить структуру из 16 байтов, заполнение не требуется.,В случае, если это имеет значение.Вот что делает JIT-компилятор.


Следующая проблема сложнее, маршаллер P / Invoke будет маршалировать встроенный массив как SAFEARRAY.Вы можете решить эту проблему на неуправляемой стороне, используя этот код:

#include "stdafx.h"
#include <stdio.h>
#include <objidl.h>

struct PurchaseOrder 
{ 
    char* Value1; 
    double Value2; 
    LPSAFEARRAY Value3;
    int fence;
};


extern "C"
__declspec(dllexport) int __stdcall MonteCarloPrint(PurchaseOrder *hostPurchaseOrders, int length)
{    
    printf("\nSize of PurchaseOrder: %d",sizeof(PurchaseOrder));

    for (int i = 0; i < length; i++) 
    {           
        printf("\n\nAddress: %u",hostPurchaseOrders+i);         
        printf("\nValue1: %s",(hostPurchaseOrders+i)->Value1);
        printf("\nValue2: %f",(hostPurchaseOrders+i)->Value2);
        double* arrayPtr;
        if (S_OK == SafeArrayAccessData((hostPurchaseOrders+i)->Value3, (void**)&arrayPtr)) {
            printf("\nValue3[0]: %f", arrayPtr[0]);
            printf("\nValue3[1]: %f", arrayPtr[1]);
        }
    }
    return 0;
}

Я написал код с помощью компилятора C ++, возможно, вам придется настроить.

...