Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
135 views
in Technique[技术] by (71.8m points)

c# - faster way to return data as an array interoping c++

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 ?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...