Hi I am trying to access a c++ array of struct from c#. The struct itself also contains an array of strings and a String. The details are below. It's not working.. does not crash but does not transfer the data across (e.g. get nulls in the array, and random number in numberOfRows integer of struct/class). See my comments at the end of the code listing. Any suggestions?
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 and button3 click events work fine so it shows that some of the c++ marshalling to c# is working. button4_click doesn't work.
label1.Text should return 1 since it's just returning matrix[0].numberOfRows but in fact it returns some huge number.
Also the getMatrix call if uncommented, also doesn't work, it does run without crash but then all the elements of the array are empty (not filled with the data that getMatrix is supposed to fill them with).