简体   繁体   English

C#无法执行带有内存集的C ++ DLL

[英]c# cannot execute c++ dll with memset inside

I am building a c++ lib for c#. 我正在为c#构建一个c ++库。 However my program died when there was memset or memcpy inside. 但是,当里面有memset或memcpy时,我的程序死了。 below is the code: 下面是代码:

c# C#

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;

    namespace CsharpCallDll
    {
        public class dllfunction
        {
            [DllImport("dllgen.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
            public static extern int calData(ref double data, int data_size, 
                ref double info, int info_size, ref char result,int max_result_size,ref int realResultSize);
        }

        class Program
        {
            static void Main(string[] args)
            {
                int data_size = 2;
                double[] data = new double[data_size];

                int info_size = 3;
                double[] info = new double[info_size];

                int max_result_size = 1000;
                char[] result = new char[max_result_size];

                int real_result_size = 0;

                data[0] = 1;
                data[1] = 2;
                info[0] = 1;
                info[1] = 2;
                info[2] = 3;

                unsafe
                {
                    dllfunction.calData(ref data[0], data_size,
                             ref info[0], info_size, ref result[0], max_result_size,ref real_result_size);

                    Console.Write(result + "\n");
                }
                Pause();
            }

            public static void Pause()
            {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }
    }

c++ C ++

    MG_API int calData(double *data, int dataSize,
    double *info, int infoSize, char* result, int maxresultSize, int* realResultSize)
{       
    int errorCode = 0;

    if (dataSize == 0 || infoSize == 0)
    {
        errorCode = 1;
    }

    string resultArray = "";
    double* tempvalue = new double;
    string isnormal;
    string abnromaltype;
    int resultSiz = 0;


    for (int i = 0; i < 15; i++)
    {
        srand((int)time(NULL)+i);
        *tempvalue = (double)Random(10);
        if (*tempvalue > 8) {
            isnormal = "false";
            abnromaltype = abnromalTypeS[0];
        }else if (*tempvalue < 2) {
            isnormal = "false";
            abnromaltype = abnromalTypeS[1];
        }else{
            isnormal = "true";
            abnromaltype = abnromalTypeS[2];
        }

        resultArray = appendResultIntoString(resultArray, dataNameS[i], *tempvalue,
            dataUnitS[i], isnormal, abnromaltype);
    }

    *realResultSize = resultArray.size();
    // print the result
    cout << "this is the resultsize ";
    cout << *realResultSize << endl;
    // result = (char*) malloc(*resultSize);
     memset(result, 0, maxresultSize);
    // memcpy(result, resultArray.c_str(), *realResultSize);

    delete tempvalue;
    return errorCode;
}

resultArray is a string result. resultArray是字符串结果。

My qeustion is: 1. when there is memcpy, memset in the c++ code, c# program will die there. 我的要求是:1.当c ++代码中有memcpy和memset时,c#程序将在那里死亡。 what happened? 发生了什么?

By the way: 2. is there a way to send a dynamic length string from c++ to c# 3. is there a way to send string[] from c++ to c# 顺便说一句:2.是否可以将动态长度的字符串从c ++发送到c#?3.是否可以将string []从c ++发送到c#

Thank you guys! 感谢大伙们!

..., ref char result,int max_result_size,ref int realResultSize

The char* parameter is ambiguous in C and C++. char*参数在C和C ++中不明确。 Could mean a reference to a single char (ref char in C#) or a reference to an array (char[] in C#). 可能意味着对单个char的引用(在C#中为ref char)或对数组的引用(在C#中为char [])。 That doesn't matter much in those languages, although you can certainly get it wrong the exact same way you got it wrong here, but it makes a big, big difference in C#. 在那些语言中,这没什么大不了的,尽管您当然可以用在这里遇到的错误完全相同的方式来弄错,但是在C#中却有很大的不同。 You get it wrong in C by passing a char& instead of a char[]& and the memset() call will corrupt the stack of the caller. 您在C中通过传递char&而不是char []&来弄错了,memset()调用将破坏调用方的堆栈。 Same thing happens here. 同样的事情在这里发生。

Since the pinvoke marshaller doesn't know that it should actually pass an array reference, and the char type is not compatible with C code since it is 2 bytes in C# and 1 byte in C, the marshaller makes a copy of the single char to convert it to byte. 由于pinvoke编组器不知道它实际上应该传递数组引用,并且char类型与C代码不兼容,因为在C#中为2字节,在C中为1字节,因此编组器将单个 char复制到将其转换为字节。 Your memset() call now corrupts the memory allocated by the pinvoke marshaller for that single byte. 您的memset()调用现在破坏了pinvoke编组为该单个字节分配的内存。 Outcome is quite unpredictable, you only get an AVE if you're lucky. 结果非常不可预测,只有在幸运的情况下才能获得AVE。

Something else it does not know is that it must copy the array back. 它不知道的其他事情是它必须将阵列复制回去。 Beyond not knowing it is an array, it also doesn't know its length. 除了不知道它是一个数组之外,它也不知道它的长度。 And you have to ask for it, by default the pinvoke marshaller doesn't copy arrays back to avoid the cost of doing so. 而且您必须要求它,默认情况下,pinvoke marshaller不会将阵列复制回去,以避免这样做的成本。

Telling the pinvoke marshaller that it is an array that needs to be copied back looks like this: 告诉pinvoke marshaller,这是一个需要复制回的数组,如下所示:

..., [Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex(6)] char[] result

The [Out] attribute asks for array elements to be copied back, the SizeParamIndex property says where to look when the marshaller needs to know how many elements need to be copied. [Out]属性要求将数组元素复制回去,SizeParamIndex属性说明编组器何时需要知道需要复制多少个元素时应在哪里查看。

It is both simpler and more efficient by not forcing the pinvoke marshaller to convert the array elements: 通过不强制Pinvoke编组器转换数组元素,它既简单又高效。

..., byte[] result

No longer a need to help with the [Out] and [MarshalAs] attribute. 不再需要帮助[Out]和[MarshalAs]属性。 Since no conversion is required anymore, the pinvoke marshaller can simply pin the array and pass a pointer to its first element. 由于不再需要任何转换,因此pinvoke编组器可以简单地固定数组并将指针传递到其第一个元素。 Your C code now writes directly to the GC heap storage. 现在,您的C代码将直接写入GC堆存储。 Good thing you have the max_result_size argument, it is very important to avoid GC heap corruption. 拥有max_result_size参数是一件好事,避免GC堆损坏非常重要。 Be sure to pass the array's Length property. 确保传递数组的Length属性。

Note that the same story applies to the data and info parameters. 请注意,同一情况适用于datainfo参数。 Not nearly as fatal since they don't require conversion. 由于它们不需要转换,因此几乎没有致命的危险。 Declare them as double[] instead, no ref. 将它们声明为double[]而不是引用。

In addition to Hans Passant's advice, another possibility is to explicitly pin variables with the fixed statement. 除了Hans Passant的建议外,另一种可能性是使用fixed语句显式固定变量。

unsafe
{
    fixed (double* d = data)
    {
        fixed (double* i = info)
        {
            fixed (int* rs = &real_result_size)
            {
                dllfunction.calData((IntPtr)d, data_size, (IntPtr)i, info_size, result, max_result_size, (IntPtr)rs);
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM