1

this is a very clean and nice solution to marsahall a struct array from unmanaged C++ code. it is allmost perfect solution when it comes to simplicity, it took me a while to get to that level of understanding the concept, so that in few lines of code, as you can see C# Main(), i have a populated array of struct ready to be 'harvested'..

typedef struct {
int Id;
BSTR StrVal;
}Package;

extern "C" __declspec(dllexport) void dodata(int requestedLength,int StringSize, Package **Packs){

    int count;
    count=0;
    *Packs = (Package*)LocalAlloc(0, requestedLength * sizeof(Package));
    Package *Cur = *Packs;
    while(count!= requestedLength)
    {
        Cur[count].StrVal = NULL;
        Cur[count].Id = count;
        Cur[count].StrVal=SysAllocString(L"abcdefghij");
        Cur[count].StrVal[StringSize-1]=count+'0';

    ++count;

    }

}

C#

[DllImport(@"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void dodata(int requestedLength, int StringSize, out IntPtr csPkPtr);


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct csPk
{
    public int V;
    [MarshalAsAttribute(UnmanagedType.BStr, SizeConst = 10)]
    public string testStr;
}

static void Main(string[] args){

    int ArrL = 16000;
    csPk[] Cpk = new csPk[ArrL];
    IntPtr CpkPtr = IntPtr.Zero;
    int szPk = Marshal.SizeOf(typeof(csPk));
    dodata(ArrL, 10, out CpkPtr);
}

now all i have to do is :

        for (int i = 0; i < Cpk.Length; i++)
        {
            Cpk[i] = (csPk)Marshal.PtrToStructure(new IntPtr(CpkPtr.ToInt32() + (szPk * i)), typeof(csPk));
        }

the solution is quite easy as you can see the question is using unsafe or any kind of transformation to the data, even going down to bytes...

how could i optimize it to perform better returning the data ?

Edit:

links i have tried to learn from other answers here in SO:

also tried google : wikipedia , a github post by stephentoub

Community
  • 1
  • 1
LoneXcoder
  • 2,121
  • 6
  • 38
  • 76

1 Answers1

1

this is a complete blazing fast solution to populate a list of objects, i did my best and i will be happy to have comments and suggestions.

c++

typedef struct _DataPacket
{
    BSTR buffer;
    UINT size;
} DataPacket;

extern "C" __declspec(dllexport)  void GetPacksUnsafe( int size, DataPacket** DpArray )
{
    int szr = size;int count=0;
    *DpArray = (DataPacket*)CoTaskMemAlloc( szr * sizeof( DataPacket ));

    if ( DpArray != NULL )
    {
        DataPacket* CurPack = *DpArray;

        for ( int i = 0; i < szr; i++, CurPack++ )
        {
            CurPack->size = i;
            CurPack->buffer = SysAllocString(L"SomeText00");
            CurPack->buffer[9]=i+'0';   

        }
    }
}

C#

    [DllImport(@"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
    public static extern void GetPacksUnsafe(int size, PackU** outPackUArr);

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct PackU
    {
        public char* StrVal;
        public int IntVal;
    }


    public static unsafe List<PackU> PopulateLstPackU(int ArrL)
    {
        PackU* PackUArrOut;
        List<PackU> RtLstPackU = new List<PackU>(ArrL);
        GetPacksUnsafe(ArrL, &PackUArrOut);
        PackU* CurrentPack = PackUArrOut;
        for (int i = 0; i < ArrL; i++, CurrentPack++)
        {
            RtLstPackU.Add(new PackU(){ StrVal = CurrentPack->StrVal, IntVal=CurrentPack->IntVal});
        }
        Marshal.FreeCoTaskMem((IntPtr)PackUArrOut);

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("{0}", new string(RtLstPackU[i].StrVal));
        }
        return RtLstPackU;
    }

using the code is as simple as it could possibly be

    static unsafe void Main(string[] args)
    {
        int ArrL = 100000;
        List<PackU> LstPackU;
        LstPackU = PopulateLstPackU(ArrL);
    }

there you have a list of custom data as fast as a bullet..

EDIT

using pointers instead of strings :

typedef struct _DataPackCharPnt
{
    char* buffer;
    UINT IntVal;
} DataPackCharPnt;


extern "C" __declspec(dllexport)  void GetPacksPnt( int size, DataPackCharPnt** DpArrPnt )
{

    int count = 0;
    int TmpStrSize = 10;
    *DpArrPnt = (DataPackCharPnt*)CoTaskMemAlloc( size * sizeof( DataPackCharPnt ));
    DataPackCharPnt* CurPackPnt = *DpArrPnt;
    char dummyStringDataObject[]= "abcdefgHi";
    for ( int i = 0; i < size; i++,CurPackPnt++ )
    {

        dummyStringDataObject[9] = i+'0';
        CurPackPnt->IntVal=i;
        CurPackPnt->buffer = (char*)malloc(sizeof(char)*TmpStrSize);
        strcpy(CurPackPnt->buffer, dummyStringDataObject);

    }
}

reduced the time taken from 11 to 7 ms populating 100k elements

is there any part of creating the buffer i could omit ?

  • the duty of dummyStringDataObject is to simulate work, say getting a file name then set the buffer with its value, so except for this extra time which is the whole purpose of this function, to return some unknown values and lengths of the strings...

could you optimize it even further ?

LoneXcoder
  • 2,121
  • 6
  • 38
  • 76
  • The marshalling for `char* StrVal` looks wrong, this is a BSTR, not a char*. Did you actually test the code? All strings should appear empty. If you were to profile this you probably would find that the allocations are the hotspot. You could optimize this further by measuring the total string length and allocating just one chunk of memory. – usr Oct 20 '15 at 17:25
  • @usr first i am not familiar with native dataTypes as there so many of them **But** to the question - 1 it does convert back and i am sure cause i check every time i test the return data - 2 i would like some more info about optimize by mesuring the total string length -3 i am (right now finishing testings) working on a code (c++ side of code) that uses char* and reducing the time even more !! from 11 to 7 ms on 100K iterations !! – LoneXcoder Oct 20 '15 at 18:28
  • @usr i have updated the new c++ code, 'no strings attached (: ' now chars is faster , the struct string representation in c# is now `IntPtr` and i have made an extention `Intptr.ToStr()` that takes care of easy access to its value later on... i would really like to further optimize any part of the c++ new approach as it is the fastest one , still i'll be happy to learn from your suggestion on the `BSTR` Anyways... – LoneXcoder Oct 20 '15 at 18:57
  • @usr and in general, i repeat the issue to be clear about it, the only step i know is a hot spot for now is any work needs to be done, represented / simulated by the string manipulation - adding the counter value to the end of the string. thanks in advance ! – LoneXcoder Oct 20 '15 at 18:59