通过引用将具有双成员数组的 C# 结构数组传递给 C DLL

发布于 2024-09-26 06:06:04 字数 3233 浏览 4 评论 0原文

我们有以下 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

We have the following code marshalling between c# and c dll. However, when print the value within the C dll function, the values associated with the double array properties are all 0.0000000. I have put some in line comments for the code having problems.

Did we miss any thing to configure the marshalling behaviour?

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 Code

#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]);

    }
}}

The print result from the C dll function

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

深海夜未眠 2024-10-03 06:06:04

从 [DllImport] 声明中删除 Pack 属性,这是错误的。您没有在 C 代码中使用 #pragma pack 指令,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++ 编译器编写了代码,您可能需要进行调整。

Remove the Pack property from the [DllImport] declaration, it is wrong. You are not using a #pragma pack directive in your C code, the default value for Pack is appropriate. If it would have been in effect then your C code would have reported 16.

You are seeing 24 because there's 4 bytes of padding to align the double and 4 bytes of padding at the end of the structure to make the double align when the structure is used in an array. 4 + 4 + 8 + 4 + 4 = 24. The packing in effect is 8, the default.

You can make it more efficient by swapping Value2 and Value3 to get a struct of 16 bytes, no padding necessary. In case it matters. That's what the JIT compiler does.


The next problem is tougher, the P/Invoke marshaller will marshal the embedded array as a SAFEARRAY. You can solve that on the unmanaged side by using this code:

#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;
}

I wrote the code using the C++ compiler, you may have to tweak.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文