.Net调用非托管代码数据类型不一致的问题

什么是Net互操作?.Net不能直接操作非托管代码,这时就需要互操作了。
   c#中调用非托管c++函数,此函数又包含指向某个结构的指针,譬如指向c#中的byte数组。对于这样的参数,考虑到非托管变量不能直接在托管代码中使用,那么应该如何去处理呢?

 上例子:

 private string getSelText(int start,int Scount)

      {

          try

          {

              StringBuilder a = new StringBuilder(Scount);

             

              IntPtr pdf_pag = FPDFView.FPDF_LoadPage(pdf_doc, currentPage);

              IntPtr cur_textpag = FPDFText.FPDFText_LoadPage(pdf_pag);

              int count=Scount/512;

              for (int i = 0; i <= count; i++)

              {

                  byte[] copyText = new byte[1024];//托管数组

                  IntPtr pdata = Marshal.AllocHGlobal(1024);//申请内存

                  Marshal.Copy(copyText, 0, pdata, 1024);//再向里copy数据

                  if (i != Scount / 512)

                  {

                      FPDFText_GetText(cur_textpag, start + i * 512, 512, copyText);//非托管c++函数,参数copyText是带有返回数据的非托管数组

                  }

                  else

                  {

                      FPDFText_GetText(cur_textpag, start + i * 512, Scount % 512, copyText);

                  }

                  Marshal.FreeHGlobal(pdata);//以及释放内存

                a.Append(  UnicodeEncoding.Unicode.GetString(copyText));

                  

              }

             

              DeleteObject(pdf_pag);

              DeleteObject(cur_textpag);

              return a.ToString();

          }

          catch (Exception ex)

          {

              

              MessageBox.Show(ex.Message);

              return null;

          }

         

      }

(此处需注意的是:需要非托管指针byte[]参数的内存要先申请,再向里copy数据;还有最后要记得释放申请的内存. )

     细心的读者可能会发现方法中有总的数据长度,为什么要分n个固定长度去读取呢?这是因为要操作的数据过大时,程序就会报内存不够用的错误,所以使用小数组循环读取,避免此问题的出现。可为什么数组长度是1024,而每次读取的长度是512呢?假如我们使用长度为512的数组 结果会怎样呢?

     答案是只会返回一部分字符串数据,那缺少的部分哪去了呢,问题出在哪呢?

     很容易也很关键的就想到,byte[]长度不够,字符数据被截取了,问题很可能就出在c++非托管代码返回的数据类型与c#托管代码数据类型不一致造成的偏差(定义函数的人真坑)。我们转到定义查看FPDFText_GetText的声明。

c#代码:

        [DllImport("fpdfsdk.dll", CharSet = CharSet.Unicode)]
        public static extern int FPDFText_GetText(IntPtr text_page, int start_index, int count, byte[] result);

    没错啊,byte[] 类型的,让我们再来看下关于此非托管函数的c++文档说明:

DLLEXPORT int STDCALL FPDFText_GetText ( FPDF_TEXTPAGE  text_page,
    int  start_index,
    int  count,
    unsigned short *  result 
  )

   答案出现了, c++的unsigned short类型的指针能不能用c#中byte[]类型承接?查资料 ,非托管c++unsigned short与c#中的  System.UInt16是等价的,均是 16 位的,而c#的byte是8位的,难怪总是出现数据丢失的现象。

   该怎么解决呢?由于c#中没有现成的UInt16转字符串的方法,唯有抛弃UInt16,改用原来的byte,只要每次读入固定的长度,而承接的byte[]的长度是该长度的两倍即可。

另一种解决办法是使用Marshal.PtrToStringUni(ptr)转化,代码如下:

 int BufferLength = 0;            

              BufferLength = FPDFText.FPDFText_PageToText(pdf_doc, CurrentPage, IntPtr.Zero, 0, 0);

              IntPtr ptr = Marshal.AllocCoTaskMem(BufferLength * sizeof(UInt16));//申请内存大小,Uint16,16位等价于c/c++中wchar_t,unsigned short

              FPDFText.FPDFText_PageToText(pdf_doc, CurrentPage, ptr, BufferLength, 0);

              string str = Marshal.PtrToStringUni(ptr);      

 

 

 

你可能感兴趣的:(.net)