marshall包含字符串数组的struct数组

时间:2012-07-15 06:57:18

标签: c# c++

您好我正在尝试从c#访问c ++的c ++数组。 struct本身还包含一个字符串数组和一个String。详情如下。它不起作用..不会崩溃但不会传输数据(例如,在数组中获取空​​值,以及在structO / class的numberOfRows整数中的随机数)。请参阅代码清单末尾的我的评论。有什么建议吗?

c ++ cppClassLib.cpp

// This is the main DLL file.



  #include "stdafx.h"
    #include <Objbase.h>

    #include "cppClassLib.h"

    #include <string.h>
    //#include <malloc.h>

    namespace cppClassLib {

    /*
    http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
    http://stackoverflow.com/questions/9093292/use-a-c-library-from-c-sharp-code
    http://stackoverflow.com/questions/5671658/what-does-invalid-managed-unmanaged-type-combination-mean
    http://stackoverflow.com/questions/2338146/returning-pointers-from-unmanaged-to-managed-code
    CoTaskMemAlloc http://msdn.microsoft.com/en-us/library/windows/desktop/ms692727%28v=vs.85%29.aspx
    */

    char *createStr(char *input)
    {
        int len = strlen(input)+1;

        // can't use malloc because it needs to
        //  be accessible from another process.
        // can't use CoTaskMemAlloc because get an
        // error when trying to link, not found.
        //char *newStr = (char *)CoTaskMemAlloc(len);
        //char *newStr = (char *)malloc(len);

        char *newStr = (char *)GlobalAlloc(GPTR,len);
        //char* newStr = new char[len];
        strcpy_s(newStr, len, input);
        return newStr;
    }

    int Class1::getMatrixNumberOfRowsInColumnZero(int maxColumns, Class1::columnT *matrix)
    {
        if (maxColumns < 1) {
            return 0;
        }
        return matrix[0].numberOfRows;
    }

    int Class1::getMatrix(int maxColumns, Class1::columnT *matrix)
    {
        if (maxColumns < 2) {
            return 0;
        }
        int numberOfColumns = 2;

        //Class1::columnT *column0 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
        Class1::columnT *column0 = &(matrix[0]);
        column0->columnName = createStr("Col0");
        int numRows = 2;
        column0->numberOfRows = numRows;
        char **rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
        rows[0] = createStr("C0R0");
        rows[1] = createStr("C0R1");
        column0->rows = rows;

        Class1::columnT *column1 = &(matrix[1]);
        //Class1::columnT *column1 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
        column1->columnName = createStr("Col1");
        numRows = 2;
        column1->numberOfRows = numRows;
        rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
        rows[0] = createStr("C1R0");
        rows[1] = createStr("C1R1");
        column1->rows = rows;

        //matrix[0]=column0;
        //matrix[1]=column1;
        //(matrix[0])->columnName = createStr("Test0");

        return numberOfColumns; // 2
    }

    int Class1::getInt(void)
    {
        return 1234;
    }

    char* Class1::getHi(void)
    {
        //char *result = createStr("Hello");
        //return result;
        //return createStr("hello");
        return createStr("hello");
    }

    char** Class1::getHeaderList(void)
    {
        char** list;
        list = (char **)GlobalAlloc(GPTR,sizeof(char *)*2);
        list[0]=createStr("test1");
        list[1]="test2";
        return list;
    }

    int Class1::getHeaderListTwo(int maxsize, char ***result)
    {
        char** list;
        int len = 2;
        if (maxsize < len) {
            return NULL;
        }
        list = (char **)GlobalAlloc(GPTR,sizeof(char *)*maxsize);
        list[0]=createStr("test01");
        list[1]="test02";
        for (int i=2; i<maxsize; ++i) {
            list[i]="";
        }
        *result = list;
        return len;
    }

    char* Class1::getHi2(void)
    {
        return "Hi";
    }

    char* Class1::getHi3(void)
    {
        return "Hi!";
    }

    void Class1::getData(int *totalColumns,
                    char** headers[2],
                    char** items[2][3])
    {
        *totalColumns = 2;
        *headers[0]=createStr("Testing");
        *headers[1]=createStr("Pets");
        *items[0][0]=createStr("test1");
        *items[0][1]=createStr("test2");
        *items[0][2]=createStr("test3");
        *items[1][0]=createStr("Cats");
        *items[1][1]=createStr("Dogs");
        *items[1][2]=createStr("Fish");
    }

}

c ++ cppClassLib.h

    // cppClassLib.h

    #pragma once

    using namespace System;

    #define DllExport   __declspec( dllexport )

    // http://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx

    namespace cppClassLib {

    public class Class1 {
        public:
            struct columnT {  
                int numberOfRows;
                char **rows;
                char *columnName;
            };
            static DllExport int getMatrix(int maxColumns, columnT *matrix);
            static DllExport int getMatrixNumberOfRowsInColumnZero(int maxColumns, columnT *matrix);
            static DllExport void getData(int *totalColumns,
                    char** headers[2],
                    char** items[2][3]);
            static DllExport char *getHi(void);
            static DllExport char *getHi2(void);
            static DllExport char *getHi3(void);
            static DllExport int getInt(void);
            static DllExport char** getHeaderList(void);
            static DllExport int getHeaderListTwo(int maxsize, char ***result);
    };
}

c#Form1.cs

  using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    using System.Runtime.InteropServices;

    /*

    http://msdn.microsoft.com/en-us/library/aa288468%28v=vs.71%29.aspx
    http://ondotnet.com/pub/a/dotnet/2002/03/18/customcontrols.html?page=2
    http://www.codeproject.com/Articles/2995/The-Complete-Guide-to-C-Strings-Part-I-Win32-Chara
    http://msdn.microsoft.com/en-us/library/z6cfh6e6.aspx

    */

    namespace listViewFromC
    {
    public partial class Form1 : Form
    {
        /*
        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getInt@Class1@cppClassLib@@QAEHXZ")]
            public static extern int getInt();
        */

        // get EntryPoint using
        // "DLL Export Viewer" software

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getHi2@Class1@cppClassLib@@SAPADXZ")]
            public static extern String getHi2();

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getHi@Class1@cppClassLib@@SAPADXZ")]
        [return: MarshalAs(UnmanagedType.LPStr)]  
        public static extern string getHi();

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getHeaderList@Class1@cppClassLib@@SAPAPADXZ")]
        [return: MarshalAs(UnmanagedType.LPArray, 
            ArraySubType=UnmanagedType.LPStr, SizeConst=2)]
        public static extern String[] getHeaderList();

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getHeaderListTwo@Class1@cppClassLib@@SAHHPAPAPAD@Z")]
        public static extern int getHeaderListTwo(int maxsize,
            [MarshalAs(UnmanagedType.LPArray,
            ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]
            ref String[] result
            );

        [StructLayout(LayoutKind.Sequential)]  
        public class columnType
        {
            public int numberOfRows;
            [MarshalAs(UnmanagedType.LPArray,
            ArraySubType = UnmanagedType.LPStr)]
            public String[] rows;
            [MarshalAs(UnmanagedType.LPStr)]
            public String columnName;
        }  

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getMatrix@Class1@cppClassLib@@SAHHPAUcolumnT@12@@Z")]
        public static extern int getMatrix(int maxColumns,
            [MarshalAs(UnmanagedType.LPArray,
            ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
            columnType[] matrix);

        [DllImport("cppClassLib.dll",
        CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?getMatrixNumberOfRowsInColumnZero@Class1@cppClassLib@@SAHHPAUcolumnT@12@@Z")]
        public static extern int getMatrixNumberOfRowsInColumnZero(int maxColumns,
            [MarshalAs(UnmanagedType.LPArray,
            ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
            columnType[] matrix);

        /*
        [DllImport("kernel32.dll")]
        static extern IntPtr GlobalAlloc(uint uFlags, uint dwBytes);

        const uint GMEM_FIXED = 0x0000;
        const uint GMEM_ZEROINIT = 0x0040;
        const uint GPTR = GMEM_FIXED | GMEM_ZEROINIT;
        */

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //label1.Text = getInt().ToString();
            label1.Text = getHi2();
            listView1.Items.Clear();
            listView1.Items.Add(label1.Text);
            //listView1.
        }

        private void button2_Click(object sender, EventArgs e)
        {
            label1.Text = getHi();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            const int maxsize = 2;
            String[] headerList = new String[maxsize];
            int len = getHeaderListTwo(maxsize, ref headerList);
            MessageBox.Show("Got length " + headerList.Length+" ("+len+")");
            label1.Text="";
            for (int i = 0; i < headerList.Length; ++i)
            {
                if (headerList[i].Length>0)
                {
                    label1.Text += headerList[i].ToString() + " // ";
                }
                else
                {
                    label1.Text += " // ";
                }
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            int maxColumns=5;
            int numberOfColumns = 0;
            columnType[] matrix = new columnType[maxColumns];
            for (int i = 0; i < maxColumns; ++i)
            {
                matrix[i] = new columnType();
            }
            matrix[0].numberOfRows = 1; // pick something just to see if we can get it back
            //uint sz = (uint)maxColumns*4;
            //IntPtr matrixIP = GlobalAlloc(GPTR, sz);
            //columnType[] matrix = matrixIP;

            //IntPtr pointerArr = Marshal.AllocHGlobal(maxColumns*4);

            //numberOfColumns = getMatrix(maxColumns, matrix);
            label1.Text = getMatrixNumberOfRowsInColumnZero(maxColumns,matrix).ToString();

            //label1.Text = matrix[0].columnName;
        }
    }
}

button1,button2和button3单击事件工作正常,因此它显示某些c ++编组到c#工作正常。 button4_click不起作用。

label1.Text应返回1,因为它只返回matrix [0] .numberOfRows 但实际上它会返回一些巨大的数字。

getMatrix调用如果取消注释,也不起作用,它确实运行而不会崩溃但是数组的所有元素都是空的(没有填充getMatrix应该填充它们的数据)。

1 个答案:

答案 0 :(得分:1)

这是我的解决方案。这个解决方案唯一的怪癖是结构中需要固定长度数组,我更喜欢可变长度数组,但它不接受LPArray的Marshall。也许这是不可能的。

我遇到的主要问题是我已将其声明为类而不是结构。另一个问题是结构中的数组是一个LPArray非托管marshall类型,尝试拥有一个可变长度数组,但由于它需要ByValArray(或SafeArray)才能工作,因此无效。


cppClassLib.h

// cppClassLib.h

#pragma once

using namespace System;

#define DllExport   __declspec( dllexport )

// http://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx

#define maxRowsCpp 100

namespace cppClassLib {

public class Class1 {
    public:
        struct columnT {  
            int numberOfRows;
            char *rows[maxRowsCpp];
            char *columnName;
        };
        struct columnT2 {  
            int numberOfRows;
        };
        static DllExport int __thiscall getMatrix(int maxColumns, int maxRows, columnT *matrix[]);
        static DllExport int __thiscall getMatrixNumberOfRowsInColumnZero(int maxColumns, columnT matrix[]);
        static DllExport int __thiscall getMatrixNumberOfRowsInColumnZero2(int maxColumns, columnT2 matrix[]);
        static DllExport void getData(int *totalColumns,
                char** headers[2],
                char** items[2][3]);
        static DllExport char *getHi(void);
        static DllExport char *getHi2(void);
        static DllExport char *getHi3(void);
        static DllExport int getInt(void);
        static DllExport char** getHeaderList(void);
        static DllExport int getHeaderListTwo(int maxsize, char ***result);
};

}


cppClassLib.cpp

// This is the main DLL file.

#include "stdafx.h"
#include <Objbase.h>

#include "cppClassLib.h"

#include <string.h>
//#include <malloc.h>

namespace cppClassLib {

/*
http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
http://stackoverflow.com/questions/9093292/use-a-c-library-from-c-sharp-code
http://stackoverflow.com/questions/5671658/what-does-invalid-managed-unmanaged-type-combination-mean
http://stackoverflow.com/questions/2338146/returning-pointers-from-unmanaged-to-managed-code
CoTaskMemAlloc http://msdn.microsoft.com/en-us/library/windows/desktop/ms692727%28v=vs.85%29.aspx
*/

char *createStr(char *input)
{
    int len = strlen(input)+1;

    // can't use malloc because it needs to
    //  be accessible from another process.
    // can't use CoTaskMemAlloc because get an
    // error when trying to link, not found.
    //char *newStr = (char *)CoTaskMemAlloc(len);
    //char *newStr = (char *)malloc(len);

    char *newStr = (char *)GlobalAlloc(GPTR,len);
    //char* newStr = new char[len];
    strcpy_s(newStr, len, input);
    return newStr;
}

int Class1::getMatrixNumberOfRowsInColumnZero(int maxColumns, Class1::columnT matrix[])
{
    if (maxColumns < 1) {
        return 0;
    }
    return (matrix[0]).numberOfRows;
}

int Class1::getMatrixNumberOfRowsInColumnZero2(int maxColumns, Class1::columnT2 matrix[])
{
    if (maxColumns < 1) {
        return 0;
    }
    return (matrix[0]).numberOfRows;
}

int Class1::getMatrix(int maxColumns, int maxRows, Class1::columnT *matrix[])
{
    // require at least able to have 2 rows and 2 columns
    if ((maxColumns < 2) || (maxRows < 2)) {
        return 0;
    }
    int numberOfColumns = 2;
    int numberOfRows = 2;
    //return matrix[0].columnName[0];

    //Class1::columnT *column0 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
    Class1::columnT *column0 = &(*matrix[0]);
    column0->columnName = createStr("Col0");
    column0->numberOfRows = numberOfRows;
    //char **rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
    column0->rows[0] = createStr("C0R0");
    column0->rows[1] = createStr("C0R1");

    Class1::columnT *column1 = &(*matrix[1]);
    //Class1::columnT *column1 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
    column1->columnName = createStr("Col1");
    column1->numberOfRows = numberOfRows;
    //rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
    column0->rows[0] = createStr("C1R0");
    column0->rows[1] = createStr("C1R1");

    return numberOfColumns;
}

int Class1::getInt(void)
{
    return 1234;
}

char* Class1::getHi(void)
{
    //char *result = createStr("Hello");
    //return result;
    //return createStr("hello");
    return createStr("hello");
}

char** Class1::getHeaderList(void)
{
    char** list;
    list = (char **)GlobalAlloc(GPTR,sizeof(char *)*2);
    list[0]=createStr("test1");
    list[1]="test2";
    return list;
}

int Class1::getHeaderListTwo(int maxsize, char ***result)
{
    char** list;
    int len = 2;
    if (maxsize < len) {
        return NULL;
    }
    list = (char **)GlobalAlloc(GPTR,sizeof(char *)*maxsize);
    list[0]=createStr("test01");
    list[1]="test02";
    for (int i=2; i<maxsize; ++i) {
        list[i]="";
    }
    *result = list;
    return len;
}

char* Class1::getHi2(void)
{
    return "Hi";
}

char* Class1::getHi3(void)
{
    return "Hi!";
}

void Class1::getData(int *totalColumns,
                char** headers[2],
                char** items[2][3])
{
    *totalColumns = 2;
    *headers[0]=createStr("Testing");
    *headers[1]=createStr("Pets");
    *items[0][0]=createStr("test1");
    *items[0][1]=createStr("test2");
    *items[0][2]=createStr("test3");
    *items[1][0]=createStr("Cats");
    *items[1][1]=createStr("Dogs");
    *items[1][2]=createStr("Fish");
}

}


c#Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Runtime.InteropServices;

/*

http://msdn.microsoft.com/en-us/library/aa288468%28v=vs.71%29.aspx
http://ondotnet.com/pub/a/dotnet/2002/03/18/customcontrols.html?page=2
http://www.codeproject.com/Articles/2995/The-Complete-Guide-to-C-Strings-Part-I-Win32-Chara
http://msdn.microsoft.com/en-us/library/z6cfh6e6.aspx

 */

namespace listViewFromC
{
public partial class Form1 : Form
{
    const int maxRows = 100;
    /*
    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?getInt@Class1@cppClassLib@@QAEHXZ")]
        public static extern int getInt();
    */

    // get EntryPoint using
    // "DLL Export Viewer" software

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?getHi2@Class1@cppClassLib@@SAPADXZ")]
        public static extern String getHi2();

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?getHi@Class1@cppClassLib@@SAPADXZ")]
    [return: MarshalAs(UnmanagedType.LPStr)]  
    public static extern string getHi();

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?getHeaderList@Class1@cppClassLib@@SAPAPADXZ")]
    [return: MarshalAs(UnmanagedType.LPArray, 
        ArraySubType=UnmanagedType.LPStr, SizeConst=2)]
    public static extern String[] getHeaderList();

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "?getHeaderListTwo@Class1@cppClassLib@@SAHHPAPAPAD@Z")]
    public static extern int getHeaderListTwo(int maxsize,
        [MarshalAs(UnmanagedType.LPArray,
        ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]
        ref String[] result
        );

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct columnType
    {
        public int numberOfRows;
        // note: can't marshal rows field as LPArray must be ByValArray or SafeArray
        // for maximum of maxRows rows
        [MarshalAs(UnmanagedType.ByValArray,
        ArraySubType = UnmanagedType.LPStr,
        SizeConst=maxRows)]
        public String[] rows;
        [MarshalAs(UnmanagedType.LPStr)]
        public String columnName;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct columnType2
    {
        public int numberOfRows;
    }

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.ThisCall,
    EntryPoint = "?getMatrix@Class1@cppClassLib@@SEHHHQAPAUcolumnT@12@@Z")]
    public static extern int getMatrix(int maxColumns,
        int maxRows,
        [MarshalAs(UnmanagedType.LPArray,
        ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
        ref columnType[] matrix);

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.ThisCall,
    EntryPoint = "?getMatrixNumberOfRowsInColumnZero@Class1@cppClassLib@@SEHHQAUcolumnT@12@@Z")]
    public static extern int getMatrixNumberOfRowsInColumnZero(int maxColumns,
        [MarshalAs(UnmanagedType.LPArray,
        ArraySubType = UnmanagedType.Struct, 
        SizeParamIndex = 0)]
        columnType[] matrix);

    [DllImport("cppClassLib.dll",
    CallingConvention = CallingConvention.ThisCall,
    EntryPoint = "?getMatrixNumberOfRowsInColumnZero2@Class1@cppClassLib@@SEHHQAUcolumnT2@12@@Z")]
    public static extern int getMatrixNumberOfRowsInColumnZero2(int maxColumns,
        [MarshalAs(UnmanagedType.LPArray,
        SizeParamIndex = 0,
        ArraySubType = UnmanagedType.Struct)]
        columnType2[] matrix);

    /*
    [DllImport("kernel32.dll")]
    static extern IntPtr GlobalAlloc(uint uFlags, uint dwBytes);

    const uint GMEM_FIXED = 0x0000;
    const uint GMEM_ZEROINIT = 0x0040;
    const uint GPTR = GMEM_FIXED | GMEM_ZEROINIT;
    */

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //label1.Text = getInt().ToString();
        label1.Text = getHi2();
        listView1.Items.Clear();
        listView1.Items.Add(label1.Text);
        //listView1.
    }

    private void button2_Click(object sender, EventArgs e)
    {
        label1.Text = getHi();
    }

    private void button3_Click(object sender, EventArgs e)
    {
        const int maxsize = 2;
        String[] headerList = new String[maxsize];
        int len = getHeaderListTwo(maxsize, ref headerList);
        MessageBox.Show("Got length " + headerList.Length+" ("+len+")");
        label1.Text="";
        for (int i = 0; i < headerList.Length; ++i)
        {
            if (headerList[i].Length>0)
            {
                label1.Text += headerList[i].ToString() + " // ";
            }
            else
            {
                label1.Text += " // ";
            }
        }
    }

    private void button4_Click(object sender, EventArgs e)
    {
        int maxColumns=5;
        int numberOfColumns = 0;
        columnType[] matrix = new columnType[maxColumns];
        for (int i = 0; i < maxColumns; ++i)
        {
            matrix[i] = new columnType();
        }
        matrix[0].numberOfRows = 1; // pick something just to see if we can get it back
        matrix[0].columnName = "ABC";
        // numberOfRows must be less than or equal to maxRows

        columnType2[] matrix2 = new columnType2[maxColumns];
        for (int i = 0; i < maxColumns; ++i)
        {
            matrix2[i] = new columnType2();
        }
        matrix2[0].numberOfRows = 1; // pick something just to see if we can get it back

        //uint sz = (uint)maxColumns*4;
        //IntPtr matrixIP = GlobalAlloc(GPTR, sz);
        //columnType[] matrix = matrixIP;

        //IntPtr pointerArr = Marshal.AllocHGlobal(maxColumns*4);

        //int result = getMatrixNumberOfRowsInColumnZero(maxColumns,matrix);
        //label1.Text = result.ToString();

        numberOfColumns = getMatrix(maxColumns, maxRows, ref matrix);
        label1.Text = matrix[0].columnName;

    }
}
}