2016-07-26 6 views
0

私はC#用のC++ライブラリをビルドしています。しかし、memsetやmemcpyがあると私のプログラムは亡くなりました。C#はmemsetでmemsetでC++ dllを実行できません

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++

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が文字列結果である

C番号:以下のコードです。

私のqeustionは: です。1. memcpy、memsetがC++コードにある場合、C#プログラムはそこで死んでしまいます。何が起こった?ところで

: 2. C++からC# 3に動的な長さの文字列を送信する方法があり、C++からC#

に[]文字列を送信する方法があります皆さんありがとうございました!

+0

c#の文字は2バイトで、C++の文字列は1バイトの配列です。したがって、文字列配列をbyte []として渡す必要があります。 C++の配列はポイントなので、IntPtrとして渡す必要があります。 – jdweng

+0

なぜあなたはC++/CLIを試してみませんか –

答えて

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

char*パラメータは、CおよびC++ではあいまいです。単一のchar(C#のref char)への参照または配列(C#のchar [])への参照を意味する可能性があります。これらの言語ではそれほど重要ではありませんが、ここで間違っているのとまったく同じ方法で間違っているかもしれませんが、それは大きくなります。大きなの違いはC#です。あなたはchar [] &の代わりに&という文字を渡すことでC言語で間違ってしまい、memset()呼び出しが呼び出し元のスタックを壊してしまいます。ここでも同じことが起こります。

pinvoke marshallerは配列参照を実際に渡す必要があり、charタイプはC#では2バイト、Cでは1バイトであるため、Cコードと互換性がないとわからないため、マーシャラーはシングル charをbyteに変換します。あなたのmemset()コールは、その1バイトのためにpinvoke marshallerによって割り当てられたメモリを破壊するようになりました。結果はまったく予測できません。運が良ければAVEしか得られません。

他の何かがわからないことは、配列をコピーする必要があるということです。それが配列であることを知らないことを超えて、それはまた、その長さを知らない。そして、あなたはそれを求めなければなりません。デフォルトでは、pinvoke marshallerは、コストを避けるために配列を元に戻しません。それが戻ってコピーする必要がある配列は次のようになりますされていることのPInvokeマーシャラー占い

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

[アウト]属性が戻ってコピーする配列要素を要求し、SizeParamIndexプロパティはどこに言いますマーシャラーが何個の要素をコピーする必要があるかを知る必要があるかどうかを調べます。

それは簡単で、配列の要素に変換するためのPInvokeマーシャラーを強制しないことで、より効率的でもある。もはや

..., byte[] result 

[出力]と[MarshalAs]属性を支援する必要性を。これ以上の変換は必要ないので、ピンボケ・マーシャラーは単純に配列を固定し、その最初の要素にポインターを渡すことができます。 CコードはGCヒープストレージに直接書き込むようになりました。 max_result_size引数を持つのは良いことですが、GCヒープの破損を避けることは非常に重要です。配列のLengthプロパティを必ず渡してください。

同じ話は、datainfoのパラメータに適用されます。彼らは変換を必要としないので、致命的ではありません。代わりにdouble[]と宣言してください。

0

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); 
      } 
     } 
    } 
} 
関連する問題