C#调用C++结构体,以接口的形式

问题遇到的现象和发生背景

img

img

img

问题相关代码,请勿粘贴截图
#pragma once

using namespace System;
using namespace System::Runtime;
using namespace System::Runtime::InteropServices;
using namespace Autodesk::AutoCAD::Runtime;
using namespace Autodesk::AutoCAD::DatabaseServices;

#include "dynprops.h"

namespace Autodesk
{
    namespace AutoCAD
    {
        namespace Windows
        {
            namespace OPM
            {
                [InteropServices::Guid("d0f45feb-71d5-44ea-B1A0-6E2F27B2085D")]
                [InteropServices::InterfaceTypeAttribute(InteropServices::ComInterfaceType::InterfaceIsIUnknown)]
                [InteropServices::ComVisible(true)]
                public interface class IOPMPropertyExpander2
                {
                    // 获得元素值
                    // DISPID dispID
                    // IUnknown * pUnk
                    // DWORD dwCookie
                    // VARIANT * pVarOut
                    void GetElementValue(
                        [InteropServices::In] System::Int32 dispID,
                        [InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
                        [InteropServices::In] System::UInt32 dwCookie,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr< Object^> pVarOut
                    );

                    // 设置元素值
                    // DISPID dispID
                    // IUnknown * pUnk
                    // DWORD dwCookie
                    // VARIANT VarIn
                    void SetElementValue(
                        [InteropServices::In] System::Int32 dispID,
                        [InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
                        [InteropServices::In] System::UInt32 dwCookie,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr< Object^> VarIn
                    );

                    // 获取元素分组
                    // DISPID dispID
                    // IUnknown * pUnk
                    // short * groupingNumber
                    void GetElementGrouping(
                        [InteropServices::In] System::Int32 dispID,
                        [InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::I2)] interior_ptr<short> groupingNumber
                    );

                    // 获取组个数
                    // DISPID dispID
                    // IUnknown * pUnk
                    // long * nGroupCnt
                    void GetGroupCount(
                        [InteropServices::In] System::Int32 dispID,
                        [InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::I8)] interior_ptr<long> nGroupCnt
                    );

                    // 获取元素字符串
                    // DISPID dispID
                    // IUnknown * pUnk
                    // OPMLPOLESTR __RPC_FAR * pCaStringsOut
                    // OPMDWORD __RPC_FAR * pCaCookiesOut
                    void GetElementStrings(
                        [InteropServices::In] System::Int32 dispID,
                        [InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object pUnk,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr<OPMLPOLESTR> pCaStringsOut,
                        [InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr<OPMDWORD> pCaCookiesOut
                    );

                };
                //typedef public struct  tagOPMLPOLESTR
                //{
                //    ULONG cElems;
                //    /* [size_is] */ LPOLESTR __RPC_FAR* pElems;
                //} OPMLPOLESTR;

                //typedef struct tagOPMLPOLESTR __RPC_FAR* LPOPMLPOLESTR;

                //typedef public struct  tagOPMDWORD
                //{
                //    ULONG cElems;
                //    /* [size_is] */ DWORD __RPC_FAR* pElems;
                //} OPMDWORD;

                //typedef struct tagOPMDWORD __RPC_FAR* LPOPMDWORD;
            }
        }
    }
}


    #region 自定义属性
    [
    Guid("EC0AB7F1-5D92-4FF1-96AE-7AC537C1347E"),
    ProgId("OPMNETSample.ExpanderProperty.1"),

    // No class interface is generated for this class and
    // no interface is marked as the default.
    // Users are expected to expose functionality through
    // interfaces that will be explicitly exposed by the object
    // This means the object can only expose interfaces we define

    ClassInterface(ClassInterfaceType.None),
    // Set the default COM interface that will be used for
    // Automation. Languages like: C#, C++ and VB allow to 
    //query for interface's we're interested in but Automation 
    // only aware languages like javascript do not allow to 
    // query interface(s) and create only the default one

    ComDefaultInterface(typeof(IDynamicProperty2)),
    ComVisible(true)
        ]
    public class ExpanderPropertyDemo : IDynamicProperty2, ICategorizeProperties, IOPMPropertyExpander2
    {
        private IDynamicPropertyNotify2 property_notify_ = null;
        private int m_numberOfVertices;
        private Point3d[] vertices;
        private int currrntIndex = 0; // Save the current vertex's index

        // 默认构造函数
        public ExpanderPropertyDemo()
        {
            m_numberOfVertices = 5;
            vertices = new Point3d[m_numberOfVertices];
            vertices[0] = new Point3d(1000, 1000, 0);
            vertices[1] = new Point3d(2000, 2000, 0);
            vertices[2] = new Point3d(3000, 3000, 0);
            vertices[3] = new Point3d(4000, 4000, 0);
            vertices[4] = new Point3d(5000, 5000, 0);

        }

        void IDynamicProperty2.Connect(object pSink)
        {
            property_notify_ = (IDynamicPropertyNotify2)pSink;
        }

        void IDynamicProperty2.Disconnect()
        {
            property_notify_ = null;
        }

        void ICategorizeProperties.GetCategoryName(int propcat, uint lcid, out string pbstrName)
        {
            pbstrName = "扩展属性";
        }

        void IDynamicProperty2.GetCurrentValueData(object pUnk, ref object varData)
        {
            varData = null;
        }

        void IDynamicProperty2.GetCurrentValueName(out string name)
        {
            name = null;
        }

        void IDynamicProperty2.GetCurrentValueType(out ushort pVarType)
        {
            pVarType = 12; // VT_VARIANT = 12
        }

        void IDynamicProperty2.GetDescription(out string description)
        {
            description = "扩展动态属性演示";
        }

        void IDynamicProperty2.GetDisplayName(out string name)
        {
            name = "当前顶点";
        }

        void IOPMPropertyExpander2.GetElementGrouping(int dispID, object pUnk, ref short groupingNumber)
        {
            groupingNumber = 3;
        }

        void IOPMPropertyExpander2.GetElementValue(int dispID, object pUnk, uint dwCookie, ref object pVarOut)
        {
            int index = (int)dwCookie / 3;
            int subIndex = ((int)dwCookie - index * 3) % 3;
            pVarOut = vertices[index][subIndex];
            currrntIndex = index;
        }

        void IOPMPropertyExpander2.GetGroupCount(int dispID, object pUnk, ref int nGroupCnt)
        {
            nGroupCnt = m_numberOfVertices;
        }

        void IDynamicProperty2.GetGUID(out Guid propGUID)
        {
            propGUID = new Guid("9CAF41C2-CA86-4ffb-B05A-AC43C424D076");
        }

        void IDynamicProperty2.IsPropertyEnabled(object pUnk, out int bEnabled)
        {
            bEnabled = 1;
        }

        void IDynamicProperty2.IsPropertyReadOnly(out int bReadonly)
        {
            bReadonly = 0;
        }

        void ICategorizeProperties.MapPropertyToCategory(int dispid, out int ppropcat)
        {
            ppropcat = 4;
        }

        void IDynamicProperty2.SetCurrentValueData(object pUnk, object varData)
        {
            /*
                         * when pick a point, this method is called.
                         */
            double[] xyz = (double[])varData;
            vertices[currrntIndex] = new Point3d(xyz[0], xyz[1], xyz[2]);
        }

        void IOPMPropertyExpander2.SetElementValue(int dispID, object pUnk, uint dwCookie, ref object VarIn)
        {
            int index = (int)dwCookie / 3;
            int subIndex = ((int)dwCookie - index * 3) % 3;
            Point3d point = vertices[index];
            if (subIndex == 0)
            {
                vertices[index] = new Point3d((double)VarIn, point.Y, point.Z);
            }
            else if (subIndex == 1)
            {
                vertices[index] = new Point3d(point.X, (double)VarIn, point.Z);
            }
            else if (subIndex == 2)
            {
                vertices[index] = new Point3d(point.X, point.Y, (double)VarIn);
            }
        }

/*        public unsafe void GetElementStrings(
            [In] int dispID,
            [In, MarshalAs(UnmanagedType.IUnknown)] object pUnk,
            [In, Out, MarshalAs(UnmanagedType.Struct)] ref OPMLPOLESTR pCaStringsOut,
            [In, Out, MarshalAs(UnmanagedType.Struct)] ref OPMDWORD pCaCookiesOut)
        {
            List<string> strings = new List<string> { "顶点 X 坐标", "顶点 Y 坐标", "顶点 Z 坐标" };
            List<uint> values = new List<uint> { 0, 1, 2 };
            pCaStringsOut = OPMUtils.CreateOPMLPOLESTR(strings);
            pCaCookiesOut = OPMUtils.CreateOPMDWORD(values);
        }*/

        public void Initialize()
        {
            ExpanderPropertyDemo epd = new ExpanderPropertyDemo();
        }

        public void Terminate()
        {
            throw new NotImplementedException();
        }
    }
    #endregion
运行结果及报错内容

一种结果是显示不支持语法,
另一种结果是实现接口后,还是出现错误

我的解答思路和尝试过的方法

C#调用C++的结构体,以接口类的形式调用
尝试过网络一些C#调用C++的结构体方式,显示还是报错,导入C++结构体,是后面再指向C++的结构体,而接口实现,则是开始就指向C++的结构体

我想要达到的结果

希望能有一个好的解决方案,能让代码通过测试

C++ dll接口定义,举个例子

struct User
{
    char userId[64];
    char username[32];
    char password[64];
};

DLL_EXPORT int STDCALL API_UpdateOneUser
(
    IN User *user
);

C#数据结构及接口定义

[StructLayout(LayoutKind.Sequential)]
public struct User
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] userId;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public byte[] username;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] password;
    }

const string DLL_PATH = "mydll.dll";

[DllImport(DLL_PATH, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int API_UpdateOneUser(ref User user);

//以下是接口调用
User user = new User();
user.userId = ...
user.username = ...
int result = API_UpdateOneUser(ref user);

  1. 输出参数,结构体作为指针传出
    非托管部分代码:
typedef struct

{

    char name[20];

    int age;

    double scores[32];

}Student;

 

//Class中包含结构体数组类型

typedef struct

{

    int number;

    Student stedents[50];

}Class;

 

JNAAPI int GetClass(Class *pClass,int len)

{

    for(int i = 0; i < len; i++)

    {

        pClass[i].number = i;

        for(int j = 0; j< 50; j++)

        {

            //把name中的前20个字节用0代替

            memset(pClass[i].stedents[j].name, 0, 20);

            //给每个同学命名

            sprintf(pClass[i].stedents[j].name, "name_%d_%d", i, j);

            pClass[i].stedents[j].age = j % 2 == 0 ? 15:20;

        }//for

    }//for

 

    return 0;

}

上面DLL 的导出函数要求传递的参数为它自定义的Class结构体数组, 那么我们在C#调用它时也要自定义对应的结构体了,

我们可以定义为如下:

[StructLayout(LayoutKind.Sequential)]

struct Student

{

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]

    public string name;

    public int age;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]

    public double[] scores;

}

[StructLayout(LayoutKind.Sequential)]

struct Class

{

    public int number;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]

    public Student[] students;

 

}

需要注意的是,这2个结构体中的数组大小一定要跟C++中的限定一样大小哦,接下来如何使用这个API来正确的获取数据呢,大多数人可能想到像这样的处理方式:

Class myclass = new Class();

IntPtr ptr=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Class)));

GetClass(ptr);

Marshal.FreeHGlobal(ptr);

没错,这样的处理是没问题的,但是我们的API的参数是Class数组,这种处理方式只是传递一个Class结构体参数,所以这种方式在这里就不太合适了,!

那大家就想到先Class[] myclass = new Class[MaxClass]; 然后在用Marshal.AllocHGlobal 来获取myclass 数据的指针,
其实这样也是错的, 因为 Class结构中包含了,不能直接封送的Student结构,所以无论如何上面的想法是错误的!
那要怎么办呢,其实很简单,就是先分配一段非托管内存,并调用API后,再将非托管内容数据读取到托管结构体数据中!
示例演示代码如下:

// 接口定义  

[DllImport("CSharpInvokeCpp_CppDemo.dll", CallingConvention = CallingConvention.Cdecl)]

public static extern int GetClass(IntPtr pv, int len);

//复杂结构体传递测试代码

int size = Marshal.SizeOf(typeof(Class)) * 50;

IntPtr pBuff = Marshal.AllocHGlobal(size);

CppDLL.GetClass(pBuff, 50);

 

Class[] pClass = new Class[50];

for (int i = 0; i < 50; i++)

{

    IntPtr pr = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(Class)) * i);

    pClass[i] = (Class)Marshal.PtrToStructure(pr, typeof(Class));

}

Marshal.FreeHGlobal(pBuff);